diff --git a/packages/cart/test/bench.spec.ts b/packages/cart/test/bench.spec.ts index e99839ab..4af31424 100644 --- a/packages/cart/test/bench.spec.ts +++ b/packages/cart/test/bench.spec.ts @@ -182,7 +182,7 @@ describe('benchmarks', () => { height: undefined, human: {offer: 'Package up to 20kg'}, maxWeight: 20000, - price: new Decimal(11.8), + price: new Decimal(12.95), type: 'package', width: undefined }], diff --git a/packages/facade/src/gql/protos/graphql.ts b/packages/facade/src/gql/protos/graphql.ts index 54803458..08cb96ad 100644 --- a/packages/facade/src/gql/protos/graphql.ts +++ b/packages/facade/src/gql/protos/graphql.ts @@ -17,7 +17,14 @@ import { type ServiceDescriptorProto } from 'ts-proto-descriptors'; export const Mutate = ['Create', 'Update', 'Upsert']; -export const preprocessGQLInput = async (data: any, model: GraphQLInputObjectType | GraphQLEnumType | GraphQLInputField | GraphQLInputType): Promise => { +export const preProcessGQLInput = async ( + data: any, + model: GraphQLInputObjectType | GraphQLEnumType | GraphQLInputField | GraphQLInputType +): Promise => { + if (data === undefined) { + return undefined; + } + if (model instanceof GraphQLEnumType) { return data; } @@ -40,11 +47,17 @@ export const preprocessGQLInput = async (data: any, model: GraphQLInputObjectTyp }; } else { const fields = model.getFields(); - for (let key of Object.keys(fields)) { - if (data && key in data) { - data[key] = await preprocessGQLInput(data[key], fields[key].type); - } - } + const converted = await Promise.all( + Object.keys(fields).filter( + key => key in data + ).map( + key => preProcessGQLInput(data[key], fields[key].type) + ) + ); + return { + ...data, + ...converted, + }; } } @@ -55,13 +68,15 @@ export const preprocessGQLInput = async (data: any, model: GraphQLInputObjectTyp } if (model instanceof GraphQLNonNull) { - return await preprocessGQLInput(data, model.ofType); + return await preProcessGQLInput(data, model.ofType); } if (model instanceof GraphQLList) { - for (let i = 0; i < data.length; i++) { - data[i] = await preprocessGQLInput(data[i], model.ofType); - } + return await Promise.all( + data.map( + (d: any) => preProcessGQLInput(d, model.ofType) + ) + ); } if (model instanceof GraphQLScalarType) { @@ -82,12 +97,16 @@ export const preprocessGQLInput = async (data: any, model: GraphQLInputObjectTyp }; export const postProcessGQLValue = (data: any, model: GraphQLOutputType): any => { + if (data === undefined) { + return undefined; + } + if (model instanceof GraphQLEnumType) { return data; } if (model instanceof GraphQLObjectType) { - if (model.name === 'GoogleProtobufAny' && data?.value) { + if (model.name === 'GoogleProtobufAny' && data.value) { // TODO Use encoded once resource base supports it const decoded = JSON.parse((data.value as Buffer).toString()); @@ -98,10 +117,14 @@ export const postProcessGQLValue = (data: any, model: GraphQLOutputType): any => }; } else { const fields = model.getFields(); - for (let key of Object.keys(fields)) { - if (data && key in data) { - data[key] = postProcessGQLValue(data[key], fields[key].type); - } + const converted = Object.keys(fields).filter( + key => key in data + ).map( + key => postProcessGQLValue(data[key], fields[key].type) + ); + return { + ...data, + ...converted, } } } @@ -111,9 +134,9 @@ export const postProcessGQLValue = (data: any, model: GraphQLOutputType): any => } if (model instanceof GraphQLList) { - for (let i = 0; i < data.length; i++) { - data[i] = postProcessGQLValue(data[i], model.ofType); - } + return data.map( + (d: any) => postProcessGQLValue(d, model.ofType) + ); } return data; diff --git a/packages/facade/src/gql/protos/resolvers.ts b/packages/facade/src/gql/protos/resolvers.ts index 49d91cd5..7f60646b 100644 --- a/packages/facade/src/gql/protos/resolvers.ts +++ b/packages/facade/src/gql/protos/resolvers.ts @@ -9,7 +9,7 @@ import flat from 'array.prototype.flat'; import { getTyping } from './registry.js'; import { getWhitelistBlacklistConfig, Mutate, postProcessGQLValue, - preprocessGQLInput, + preProcessGQLInput, } from './graphql.js'; import { camelCase, @@ -75,9 +75,18 @@ const fetchUnauthenticatedUserToken = async (ctx: any, domain: string) => { return response?.token; }; -export const getGQLResolverFunctions = - , CTX extends ServiceClient, SRV = any, R = ResolverFn, any>, B extends keyof T = any, NS extends keyof CTX = any> - (service: ServiceDescriptorProto, key: NS, serviceKey: B, cfg: ServiceConfig): { [key in keyof SRV]: R } => { +export const getGQLResolverFunctions = < + T extends Record, + CTX extends ServiceClient, + SRV = any, + R = ResolverFn, any>, + B extends keyof T = any, NS extends keyof CTX = any + > ( + service: ServiceDescriptorProto, + key: NS, + serviceKey: B, + cfg: ServiceConfig + ): { [key in keyof SRV]: R } => { if (!service.method) { return {} as { [key in keyof SRV]: R }; } @@ -132,14 +141,11 @@ export const getGQLResolverFunctions = const client = context[key].client; const service = client[serviceKey]; try { - const converted = await preprocessGQLInput(args.input, typing.input); + const converted = await preProcessGQLInput(args.input, typing.input); const scope = args?.input?.scope; let req = typing.processor.fromPartial(converted); - // convert enum strings to integers - // req = convertEnumToInt(typing, req); - req.subject = getTyping(authSubjectType)!.processor.fromPartial({}); if (subjectField !== null) { const authToken = (context as any).request!.req.headers['authorization']; @@ -151,7 +157,11 @@ export const getGQLResolverFunctions = } } - if (!req.subject.token && 'origin' in (context as any).request!.req.headers) { + if ( + cfg?.disableUnauthenticatedUserTenant?.toString() != 'true' + && !req.subject.token + && 'origin' in (context as any).request!.req.headers + ) { req.subject.token = await fetchUnauthenticatedUserToken(context, (context as any).request!.req.headers['origin']); } @@ -170,13 +180,11 @@ export const getGQLResolverFunctions = } } - const methodFunc = service[camelCase(realMethod)] || service[realMethod]; + const methodFunc = service[camelCase(realMethod)] ?? service[realMethod]; if (method.clientStreaming) { - const readableStreamKey = Object.keys(req).filter((key) => { - if (req[key] instanceof stream.Stream.Readable) { - return key; - } - }); + const readableStreamKey = Object.keys(req).filter( + (key) => req[key] instanceof stream.Stream.Readable + ); if (readableStreamKey.length > 0) { req = streamToAsyncIterable(req, readableStreamKey[0]); } @@ -521,7 +529,7 @@ export const generateSubServiceResolvers = < for (const key of Object.keys(meta.options.messages)) { const message = meta.options.messages[key]; if (message.fields) { - const typing = getTyping('.' + meta.fileDescriptor.package + '.' + key); + const typing = getTyping(`.${meta.fileDescriptor.package}.${key}`); if (typing) { const result: any = {}; for (const fieldName of Object.keys(message.fields)) { @@ -531,7 +539,7 @@ export const generateSubServiceResolvers = < const resolver = field['resolver'] as Resolver; // TODO This creates an N+1 problem! - result[resolver.fieldName as string] = async (parent: any, _: any, ctx: any) => { + result[resolver.fieldName] = async (parent: any, _: any, ctx: any) => { if (!parent || !(fieldJsonName in parent) || parent[fieldJsonName] === undefined) { return undefined; } @@ -539,18 +547,18 @@ export const generateSubServiceResolvers = < resolver.targetService = config?.namespace ?? resolver.targetService const client = ctx[resolver.targetService].client; const service = client[resolver.targetSubService]; - const idList: string[] = Array.isArray(parent[fieldJsonName]) ? parent[fieldJsonName] : [parent[fieldJsonName]]; + const ids: string[] = Array.isArray(parent[fieldJsonName]) ? parent[fieldJsonName] : [parent[fieldJsonName]]; // TODO Support custom input messages const req = ReadRequest.fromPartial({ filters: [{ - filters: idList.map(id => ({ + filters: { field: 'id', - operation: Filter_Operation.eq, - value: id, - type: Filter_ValueType.STRING - })), - operator: FilterOp_Operator.or + operation: Filter_Operation.in, + value: JSON.stringify(ids), + type: Filter_ValueType.ARRAY + }, + limit: ids.length }] } as any); @@ -561,14 +569,18 @@ export const generateSubServiceResolvers = < req.subject!.token = authToken.split(' ')[1]; } - if (!req.subject!.token && 'origin' in (ctx as any).request!.req.headers) { + if ( + config?.disableUnauthenticatedUserTenant?.toString() !== 'true' + && !req.subject!.token + && 'origin' in (ctx as any).request!.req.headers + ) { req.subject!.token = await fetchUnauthenticatedUserToken(ctx, (ctx as any).request!.req.headers['origin']); } const methodFunc = service[camelCase(resolver.targetMethod)] || service[resolver.targetMethod]; const result = await methodFunc(req); - if (result && result.items && result.items.length) { + if (result?.items?.length) { if (Array.isArray(parent[fieldJsonName])) { return result.items.map((item: any) => item.payload); } else { diff --git a/packages/facade/src/modules/identity/oauth/oauth.ts b/packages/facade/src/modules/identity/oauth/oauth.ts index 66e9a83a..5a4dca3a 100644 --- a/packages/facade/src/modules/identity/oauth/oauth.ts +++ b/packages/facade/src/modules/identity/oauth/oauth.ts @@ -135,7 +135,7 @@ export const createOAuth = (): KoaRouter<{}, IdentityContext> => { token }); - if (!user || !user.payload) { + if (!user?.payload) { ctx.body = 'user not logged in'; return next(); } @@ -180,11 +180,11 @@ export const createOAuth = (): KoaRouter<{}, IdentityContext> => { const ids = ctx.identitySrvClient as IdentitySrvGrpcClient; const user = await ids.o_auth.exchangeCode({ service: ctx.params.service, - code: ctx.request.query['code'] as string, - state: ctx.request.query['state'] as string + code: ctx.request.query.code?.toString(), + state: ctx.request.query.state?.toString() }); - if (!user.user || !user.user.payload || !user.token || (user.user.status && user.user.status.code !== 200)) { + if (!user.user || !user.user.payload || !user.token || user.user.status?.code !== 200) { ctx.type = 'html'; ctx.body = await register(user.email || ''); return next(); diff --git a/packages/facade/tests/custom.spec.ts b/packages/facade/tests/custom.spec.ts index 1055fcf6..420c94b8 100644 --- a/packages/facade/tests/custom.spec.ts +++ b/packages/facade/tests/custom.spec.ts @@ -82,7 +82,7 @@ describe('extend', () => { beforeAll(async () => { facade = createTestFacade(); await facade.start(); - request = agent(facade.server); + request = agent(facade.server) as any; // await new Promise(resolve => setTimeout(resolve, 20000)) });