From 4c60f7b4acd1523db9a1cd1aa0594d9e39d96891 Mon Sep 17 00:00:00 2001 From: koekiebox Date: Wed, 22 Jan 2025 15:22:17 +0100 Subject: [PATCH] feat(3114): review feedback. --- .../generated/graphql.ts | 3 +- .../src/graphql/generated/graphql.schema.json | 14 +++++++- .../backend/src/graphql/generated/graphql.ts | 3 +- .../src/graphql/resolvers/wallet_address.ts | 24 ++++++++++---- packages/backend/src/graphql/schema.graphql | 4 ++- .../backend/src/middleware/tenant/index.ts | 5 --- .../open_payments/wallet_address/service.ts | 32 ++++++++++++------- packages/backend/src/shared/baseModel.ts | 11 ++----- packages/frontend/app/generated/graphql.ts | 3 +- .../src/generated/graphql.ts | 3 +- test/integration/lib/generated/graphql.ts | 3 +- 11 files changed, 67 insertions(+), 38 deletions(-) diff --git a/localenv/mock-account-servicing-entity/generated/graphql.ts b/localenv/mock-account-servicing-entity/generated/graphql.ts index a958f5c1b9..1ee5eb4488 100644 --- a/localenv/mock-account-servicing-entity/generated/graphql.ts +++ b/localenv/mock-account-servicing-entity/generated/graphql.ts @@ -374,7 +374,7 @@ export type CreateWalletAddressInput = { /** Public name associated with the wallet address. This is visible to anyone with the wallet address URL. */ publicName?: InputMaybe; /** Unique identifier of the tenant associated with the wallet address. This cannot be changed. Optional, if not provided, the tenantId will be obtained from the signature. */ - tenantId?: InputMaybe; + tenantId?: InputMaybe; /** Wallet address URL. This cannot be changed. */ url: Scalars['String']['input']; }; @@ -1251,6 +1251,7 @@ export type QueryReceiverArgs = { export type QueryWalletAddressArgs = { id: Scalars['String']['input']; + tenantId?: InputMaybe; }; diff --git a/packages/backend/src/graphql/generated/graphql.schema.json b/packages/backend/src/graphql/generated/graphql.schema.json index 028b55788d..6930bd5cd4 100644 --- a/packages/backend/src/graphql/generated/graphql.schema.json +++ b/packages/backend/src/graphql/generated/graphql.schema.json @@ -2176,7 +2176,7 @@ "description": "Unique identifier of the tenant associated with the wallet address. This cannot be changed. Optional, if not provided, the tenantId will be obtained from the signature.", "type": { "kind": "SCALAR", - "name": "String", + "name": "ID", "ofType": null }, "defaultValue": null, @@ -6841,6 +6841,18 @@ "defaultValue": null, "isDeprecated": false, "deprecationReason": null + }, + { + "name": "tenantId", + "description": "Unique identifier of the tenant associated with the wallet address. Optional, if not provided, the tenantId will be obtained from the signature.", + "type": { + "kind": "SCALAR", + "name": "String", + "ofType": null + }, + "defaultValue": null, + "isDeprecated": false, + "deprecationReason": null } ], "type": { diff --git a/packages/backend/src/graphql/generated/graphql.ts b/packages/backend/src/graphql/generated/graphql.ts index a958f5c1b9..1ee5eb4488 100644 --- a/packages/backend/src/graphql/generated/graphql.ts +++ b/packages/backend/src/graphql/generated/graphql.ts @@ -374,7 +374,7 @@ export type CreateWalletAddressInput = { /** Public name associated with the wallet address. This is visible to anyone with the wallet address URL. */ publicName?: InputMaybe; /** Unique identifier of the tenant associated with the wallet address. This cannot be changed. Optional, if not provided, the tenantId will be obtained from the signature. */ - tenantId?: InputMaybe; + tenantId?: InputMaybe; /** Wallet address URL. This cannot be changed. */ url: Scalars['String']['input']; }; @@ -1251,6 +1251,7 @@ export type QueryReceiverArgs = { export type QueryWalletAddressArgs = { id: Scalars['String']['input']; + tenantId?: InputMaybe; }; diff --git a/packages/backend/src/graphql/resolvers/wallet_address.ts b/packages/backend/src/graphql/resolvers/wallet_address.ts index dd0e4ed2fd..0ba1b48840 100644 --- a/packages/backend/src/graphql/resolvers/wallet_address.ts +++ b/packages/backend/src/graphql/resolvers/wallet_address.ts @@ -60,12 +60,25 @@ export const getWalletAddresses: QueryResolvers['walletAd export const getWalletAddress: QueryResolvers['walletAddress'] = async (parent, args, ctx): Promise => { + const tenantId = tenantIdToProceed( + ctx.isOperator, + ctx.tenant.id, + args.tenantId + ) + if (!tenantId) { + throw new GraphQLError( + errorToMessage[WalletAddressError.UnknownWalletAddress], + { + extensions: { + code: errorToCode[WalletAddressError.UnknownWalletAddress] + } + } + ) + } + const walletAddressService = await ctx.container.use('walletAddressService') - const walletAddress = await walletAddressService.get(args.id) - if ( - !walletAddress || - !tenantIdToProceed(ctx.isOperator, ctx.tenant.id, walletAddress.tenantId) - ) { + const walletAddress = await walletAddressService.get(args.id, tenantId) + if (!walletAddress) { throw new GraphQLError( errorToMessage[WalletAddressError.UnknownWalletAddress], { @@ -111,7 +124,6 @@ export const createWalletAddress: MutationResolvers['createW const options: CreateOptions = { assetId: args.input.assetId, - // We always have a tenant for [ForTenantIdContext]. tenantId: ctx.forTenantId, additionalProperties: addProps, publicName: args.input.publicName, diff --git a/packages/backend/src/graphql/schema.graphql b/packages/backend/src/graphql/schema.graphql index 1874dc4433..3138c5df9e 100644 --- a/packages/backend/src/graphql/schema.graphql +++ b/packages/backend/src/graphql/schema.graphql @@ -53,6 +53,8 @@ type Query { walletAddress( "Unique identifier of the wallet address." id: String! + "Unique identifier of the tenant associated with the wallet address. Optional, if not provided, the tenantId will be obtained from the signature." + tenantId: String ): WalletAddress "Get a wallet address by its url if it exists" @@ -1232,7 +1234,7 @@ type CreateReceiverResponse { input CreateWalletAddressInput { "Unique identifier of the tenant associated with the wallet address. This cannot be changed. Optional, if not provided, the tenantId will be obtained from the signature." - tenantId: String + tenantId: ID "Unique identifier of the asset associated with the wallet address. This cannot be changed." assetId: String! "Wallet address URL. This cannot be changed." diff --git a/packages/backend/src/middleware/tenant/index.ts b/packages/backend/src/middleware/tenant/index.ts index 611cd1a3f5..e0ef5b27e1 100644 --- a/packages/backend/src/middleware/tenant/index.ts +++ b/packages/backend/src/middleware/tenant/index.ts @@ -19,11 +19,6 @@ export async function validateTenantMiddleware( onFailValidation, next } = args - if (!tenantIdInput) { - ;(context as ForTenantIdContext).forTenantId = context.tenant.id - return next() - } - const forTenantId = tenantIdToProceed( context.isOperator, context.tenant.id, diff --git a/packages/backend/src/open_payments/wallet_address/service.ts b/packages/backend/src/open_payments/wallet_address/service.ts index d2cb2c0bad..c04a2720fa 100644 --- a/packages/backend/src/open_payments/wallet_address/service.ts +++ b/packages/backend/src/open_payments/wallet_address/service.ts @@ -65,7 +65,7 @@ export interface WalletAddressService { id: string, includeVisibleOnlyAddProps: boolean ): Promise - get(id: string): Promise + get(id: string, tenantId?: string): Promise getByUrl(url: string): Promise getOrPollByUrl(url: string): Promise getPage( @@ -116,7 +116,7 @@ export async function createWalletAddressService({ walletAddressId, includeVisibleOnlyAddProps ), - get: (id) => getWalletAddress(deps, id), + get: (id, tenantId) => getWalletAddress(deps, id, tenantId), getByUrl: (url) => getWalletAddressByUrl(deps, url), getOrPollByUrl: (url) => getOrPollByUrl(deps, url), getPage: (pagination?, sortOrder?, tenantId?) => @@ -268,12 +268,20 @@ async function updateWalletAddress( async function getWalletAddress( deps: ServiceDependencies, - id: string + id: string, + tenantId?: string ): Promise { - const walletAdd = await deps.walletAddressCache.get(id) - if (walletAdd) return walletAdd + const inMem = await deps.walletAddressCache.get(id) + if (inMem) { + return tenantId && inMem.tenantId !== tenantId ? undefined : inMem + } + + const query = WalletAddress.query(deps.knex) + if (tenantId) { + query.andWhere({ tenantId }) + } - const walletAddress = await WalletAddress.query(deps.knex).findById(id) + const walletAddress = await query.findById(id) if (walletAddress) { const asset = await deps.assetService.get(walletAddress.assetId) if (asset) walletAddress.asset = asset @@ -349,11 +357,13 @@ async function getWalletAddressPage( sortOrder?: SortOrder, tenantId?: string ): Promise { - const addresses = await WalletAddress.query(deps.knex).getPage( - pagination, - sortOrder, - tenantId - ) + const query = WalletAddress.query(deps.knex) + + if (tenantId && tenantId.length > 0) { + query.where({ tenantId }) + } + + const addresses = await query.getPage(pagination, sortOrder) for (const address of addresses) { const asset = await deps.assetService.get(address.assetId) if (asset) address.asset = asset diff --git a/packages/backend/src/shared/baseModel.ts b/packages/backend/src/shared/baseModel.ts index 5515121794..8a2c33a796 100644 --- a/packages/backend/src/shared/baseModel.ts +++ b/packages/backend/src/shared/baseModel.ts @@ -50,13 +50,11 @@ class PaginationQueryBuilder extends QueryBuilder< * https://relay.dev/graphql/connections.htm * @param pagination Pagination - cursors and limits. * @param sortOrder SortOrder - Asc/Desc sort order. - * @param tenantId string - When filtering for a specific tenant. * @returns Model[] An array of Models that form a page. */ getPage( pagination?: Pagination, - sortOrder: SortOrder = SortOrder.Desc, - tenantId?: string + sortOrder: SortOrder = SortOrder.Desc ): this { const tableName = this.modelClass().tableName if ( @@ -69,18 +67,13 @@ class PaginationQueryBuilder extends QueryBuilder< if (first < 0 || first > 100) throw new Error('Pagination index error') const last = pagination?.last || 20 if (last < 0 || last > 100) throw new Error('Pagination index error') - - const tenantFilterClause = tenantId - ? ` AND "${tableName}"."tenantId" = '${tenantId}'` - : '' - /** * Forward pagination */ if (typeof pagination?.after === 'string') { const comparisonOperator = sortOrder === SortOrder.Asc ? '>' : '<' return this.whereRaw( - `("${tableName}"."createdAt", "${tableName}"."id") ${comparisonOperator} (select "${tableName}"."createdAt" :: TIMESTAMP, "${tableName}"."id" from ?? where "${tableName}"."id" = ?${tenantFilterClause})`, + `("${tableName}"."createdAt", "${tableName}"."id") ${comparisonOperator} (select "${tableName}"."createdAt" :: TIMESTAMP, "${tableName}"."id" from ?? where "${tableName}"."id" = ?)`, [this.modelClass().tableName, pagination.after] ) .orderBy([ diff --git a/packages/frontend/app/generated/graphql.ts b/packages/frontend/app/generated/graphql.ts index eb63120530..78028ee916 100644 --- a/packages/frontend/app/generated/graphql.ts +++ b/packages/frontend/app/generated/graphql.ts @@ -374,7 +374,7 @@ export type CreateWalletAddressInput = { /** Public name associated with the wallet address. This is visible to anyone with the wallet address URL. */ publicName?: InputMaybe; /** Unique identifier of the tenant associated with the wallet address. This cannot be changed. Optional, if not provided, the tenantId will be obtained from the signature. */ - tenantId?: InputMaybe; + tenantId?: InputMaybe; /** Wallet address URL. This cannot be changed. */ url: Scalars['String']['input']; }; @@ -1251,6 +1251,7 @@ export type QueryReceiverArgs = { export type QueryWalletAddressArgs = { id: Scalars['String']['input']; + tenantId?: InputMaybe; }; diff --git a/packages/mock-account-service-lib/src/generated/graphql.ts b/packages/mock-account-service-lib/src/generated/graphql.ts index a958f5c1b9..1ee5eb4488 100644 --- a/packages/mock-account-service-lib/src/generated/graphql.ts +++ b/packages/mock-account-service-lib/src/generated/graphql.ts @@ -374,7 +374,7 @@ export type CreateWalletAddressInput = { /** Public name associated with the wallet address. This is visible to anyone with the wallet address URL. */ publicName?: InputMaybe; /** Unique identifier of the tenant associated with the wallet address. This cannot be changed. Optional, if not provided, the tenantId will be obtained from the signature. */ - tenantId?: InputMaybe; + tenantId?: InputMaybe; /** Wallet address URL. This cannot be changed. */ url: Scalars['String']['input']; }; @@ -1251,6 +1251,7 @@ export type QueryReceiverArgs = { export type QueryWalletAddressArgs = { id: Scalars['String']['input']; + tenantId?: InputMaybe; }; diff --git a/test/integration/lib/generated/graphql.ts b/test/integration/lib/generated/graphql.ts index a958f5c1b9..1ee5eb4488 100644 --- a/test/integration/lib/generated/graphql.ts +++ b/test/integration/lib/generated/graphql.ts @@ -374,7 +374,7 @@ export type CreateWalletAddressInput = { /** Public name associated with the wallet address. This is visible to anyone with the wallet address URL. */ publicName?: InputMaybe; /** Unique identifier of the tenant associated with the wallet address. This cannot be changed. Optional, if not provided, the tenantId will be obtained from the signature. */ - tenantId?: InputMaybe; + tenantId?: InputMaybe; /** Wallet address URL. This cannot be changed. */ url: Scalars['String']['input']; }; @@ -1251,6 +1251,7 @@ export type QueryReceiverArgs = { export type QueryWalletAddressArgs = { id: Scalars['String']['input']; + tenantId?: InputMaybe; };