diff --git a/bruno/collections/Rafiki/Rafiki Admin APIs/Create Wallet Address.bru b/bruno/collections/Rafiki/Rafiki Admin APIs/Create Wallet Address.bru index 3918a11c8b..5ea2508c7b 100644 --- a/bruno/collections/Rafiki/Rafiki Admin APIs/Create Wallet Address.bru +++ b/bruno/collections/Rafiki/Rafiki Admin APIs/Create Wallet Address.bru @@ -5,7 +5,7 @@ meta { } post { - url: {{RafikiGraphqlHost}}/graphql + address: {{RafikiGraphqlHost}}/graphql body: graphql auth: none } @@ -17,7 +17,7 @@ body:graphql { id createdAt publicName - url + address status asset { code diff --git a/bruno/collections/Rafiki/Rafiki Admin APIs/Get Tenant Settings.bru b/bruno/collections/Rafiki/Rafiki Admin APIs/Get Tenant Settings.bru new file mode 100644 index 0000000000..91a475a3d1 --- /dev/null +++ b/bruno/collections/Rafiki/Rafiki Admin APIs/Get Tenant Settings.bru @@ -0,0 +1,43 @@ +meta { + name: Get Tenant Settings + type: graphql + seq: 60 +} + +post { + url: {{RafikiGraphqlHost}}/graphql + body: graphql + auth: none +} + +headers { + tenant-id: 438fa74a-fa7d-4317-9ced-dde32ece1787 +} + +body:graphql { + mutation CreateTenantSettings($input: CreateTenantSettingsInput!) { + createTenantSettings(input:$input) { + settings { + key + value + } + } + } +} + +body:graphql:vars { + { + "input": { + "settings": [ + { "key": "EXCHANGE_RATES_URL", "value": "https://example.com" } + ] + } + } + +} + +script:pre-request { + const scripts = require('./scripts'); + + scripts.addApiSignatureHeader(); +} diff --git a/bruno/collections/Rafiki/Rafiki Admin APIs/Get Wallet Addresses Keys.bru b/bruno/collections/Rafiki/Rafiki Admin APIs/Get Wallet Addresses Keys.bru index e8ed641928..57d229102b 100644 --- a/bruno/collections/Rafiki/Rafiki Admin APIs/Get Wallet Addresses Keys.bru +++ b/bruno/collections/Rafiki/Rafiki Admin APIs/Get Wallet Addresses Keys.bru @@ -18,7 +18,7 @@ body:graphql { node { id publicName - url + address walletAddressKeys { edges { cursor diff --git a/bruno/collections/Rafiki/Rafiki Admin APIs/Get Wallet Addresses.bru b/bruno/collections/Rafiki/Rafiki Admin APIs/Get Wallet Addresses.bru index 462d4e81d5..318f38eaae 100644 --- a/bruno/collections/Rafiki/Rafiki Admin APIs/Get Wallet Addresses.bru +++ b/bruno/collections/Rafiki/Rafiki Admin APIs/Get Wallet Addresses.bru @@ -18,7 +18,7 @@ body:graphql { node { id publicName - url + address } } } diff --git a/localenv/mock-account-servicing-entity/app/lib/requesters.ts b/localenv/mock-account-servicing-entity/app/lib/requesters.ts index 14654de0a3..8118e2b4ce 100644 --- a/localenv/mock-account-servicing-entity/app/lib/requesters.ts +++ b/localenv/mock-account-servicing-entity/app/lib/requesters.ts @@ -88,7 +88,7 @@ export async function createWalletAddress( createWalletAddress(input: $input) { walletAddress { id - url + address publicName } } @@ -96,7 +96,7 @@ export async function createWalletAddress( ` const createWalletAddressInput: CreateWalletAddressInput = { assetId, - url: accountUrl, + address: accountUrl, publicName: accountName, additionalProperties: [] } diff --git a/localenv/mock-account-servicing-entity/app/lib/wallet.server.ts b/localenv/mock-account-servicing-entity/app/lib/wallet.server.ts index af3b1d7327..7da1c714cc 100644 --- a/localenv/mock-account-servicing-entity/app/lib/wallet.server.ts +++ b/localenv/mock-account-servicing-entity/app/lib/wallet.server.ts @@ -25,7 +25,7 @@ export async function createWallet({ await mockAccounts.setWalletAddress( accountId, walletAddress.id, - walletAddress.url + walletAddress.address ) await createWalletAddressKey({ diff --git a/localenv/mock-account-servicing-entity/app/lib/webhooks.server.ts b/localenv/mock-account-servicing-entity/app/lib/webhooks.server.ts index 72c44fd7fc..955bfb60ae 100644 --- a/localenv/mock-account-servicing-entity/app/lib/webhooks.server.ts +++ b/localenv/mock-account-servicing-entity/app/lib/webhooks.server.ts @@ -277,7 +277,7 @@ export async function handleWalletAddressNotFound(wh: Webhook) { await mockAccounts.setWalletAddress( account.id, walletAddress.id, - walletAddress.url + walletAddress.address ) } diff --git a/localenv/mock-account-servicing-entity/generated/graphql.ts b/localenv/mock-account-servicing-entity/generated/graphql.ts index 77a84f73a1..8db21d81f0 100644 --- a/localenv/mock-account-servicing-entity/generated/graphql.ts +++ b/localenv/mock-account-servicing-entity/generated/graphql.ts @@ -378,6 +378,8 @@ export type CreateTenantInput = { idpSecret?: InputMaybe; /** Public name for the tenant. */ publicName?: InputMaybe; + /** Initial settings for tenant. */ + settings?: InputMaybe>; }; export type CreateTenantSettingsInput = { @@ -394,6 +396,8 @@ export type CreateTenantSettingsMutationResponse = { export type CreateWalletAddressInput = { /** Additional properties associated with the wallet address. */ additionalProperties?: InputMaybe>; + /** Wallet address. This cannot be changed. */ + address: Scalars['String']['input']; /** Unique identifier of the asset associated with the wallet address. This cannot be changed. */ assetId: Scalars['String']['input']; /** Unique key to ensure duplicate or retried requests are processed only once. For more information, refer to [idempotency](https://rafiki.dev/apis/graphql/admin-api-overview/#idempotency). */ @@ -402,8 +406,6 @@ export type CreateWalletAddressInput = { 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; - /** Wallet address URL. This cannot be changed. */ - url: Scalars['String']['input']; }; export type CreateWalletAddressKeyInput = { @@ -1488,16 +1490,7 @@ export type Tenant = Model & { /** Public name for the tenant. */ publicName?: Maybe; /** List of settings for the tenant. */ - settings?: Maybe; -}; - - -export type TenantSettingsArgs = { - after?: InputMaybe; - before?: InputMaybe; - first?: InputMaybe; - last?: InputMaybe; - sortOrder?: InputMaybe; + settings: Array; }; export type TenantEdge = { @@ -1521,14 +1514,6 @@ export type TenantSetting = { value: Scalars['String']['output']; }; -export type TenantSettingEdge = { - __typename?: 'TenantSettingEdge'; - /** A cursor for paginating through the tenants. */ - cursor: Scalars['String']['output']; - /** A tenant setting node in the list. */ - node: TenantSetting; -}; - export type TenantSettingInput = { /** Key for this setting. */ key: Scalars['String']['input']; @@ -1536,14 +1521,6 @@ export type TenantSettingInput = { value: Scalars['String']['input']; }; -export type TenantSettingsConnection = { - __typename?: 'TenantSettingsConnection'; - /** A list of edges representing tenant settings and cursors for pagination. */ - edges: Array; - /** Information to aid in pagination. */ - pageInfo: PageInfo; -}; - export type TenantsConnection = { __typename?: 'TenantsConnection'; /** A list of edges representing tenants and cursors for pagination. */ @@ -1669,6 +1646,8 @@ export type WalletAddress = Model & { __typename?: 'WalletAddress'; /** Additional properties associated with the wallet address. */ additionalProperties?: Maybe>>; + /** Wallet Address. */ + address: Scalars['String']['output']; /** Asset of the wallet address. */ asset: Asset; /** The date and time when the wallet address was created. */ @@ -1689,8 +1668,6 @@ export type WalletAddress = Model & { status: WalletAddressStatus; /** Tenant ID of the wallet address. */ tenantId?: Maybe; - /** Wallet Address URL. */ - url: Scalars['String']['output']; /** List of keys associated with this wallet address */ walletAddressKeys?: Maybe; }; @@ -2032,9 +2009,7 @@ export type ResolversTypes = { TenantEdge: ResolverTypeWrapper>; TenantMutationResponse: ResolverTypeWrapper>; TenantSetting: ResolverTypeWrapper>; - TenantSettingEdge: ResolverTypeWrapper>; TenantSettingInput: ResolverTypeWrapper>; - TenantSettingsConnection: ResolverTypeWrapper>; TenantsConnection: ResolverTypeWrapper>; TransferState: ResolverTypeWrapper>; TransferType: ResolverTypeWrapper>; @@ -2170,9 +2145,7 @@ export type ResolversParentTypes = { TenantEdge: Partial; TenantMutationResponse: Partial; TenantSetting: Partial; - TenantSettingEdge: Partial; TenantSettingInput: Partial; - TenantSettingsConnection: Partial; TenantsConnection: Partial; TriggerWalletAddressEventsInput: Partial; TriggerWalletAddressEventsMutationResponse: Partial; @@ -2641,7 +2614,7 @@ export type TenantResolvers, ParentType, ContextType>; idpSecret?: Resolver, ParentType, ContextType>; publicName?: Resolver, ParentType, ContextType>; - settings?: Resolver, ParentType, ContextType, Partial>; + settings?: Resolver, ParentType, ContextType>; __isTypeOf?: IsTypeOfResolverFn; }; @@ -2662,18 +2635,6 @@ export type TenantSettingResolvers; }; -export type TenantSettingEdgeResolvers = { - cursor?: Resolver; - node?: Resolver; - __isTypeOf?: IsTypeOfResolverFn; -}; - -export type TenantSettingsConnectionResolvers = { - edges?: Resolver, ParentType, ContextType>; - pageInfo?: Resolver; - __isTypeOf?: IsTypeOfResolverFn; -}; - export type TenantsConnectionResolvers = { edges?: Resolver, ParentType, ContextType>; pageInfo?: Resolver; @@ -2705,6 +2666,7 @@ export type UpdateWalletAddressMutationResponseResolvers = { additionalProperties?: Resolver>>, ParentType, ContextType>; + address?: Resolver; asset?: Resolver; createdAt?: Resolver; id?: Resolver; @@ -2715,7 +2677,6 @@ export type WalletAddressResolvers, ParentType, ContextType, Partial>; status?: Resolver; tenantId?: Resolver, ParentType, ContextType>; - url?: Resolver; walletAddressKeys?: Resolver, ParentType, ContextType, Partial>; __isTypeOf?: IsTypeOfResolverFn; }; @@ -2849,8 +2810,6 @@ export type Resolvers = { TenantEdge?: TenantEdgeResolvers; TenantMutationResponse?: TenantMutationResponseResolvers; TenantSetting?: TenantSettingResolvers; - TenantSettingEdge?: TenantSettingEdgeResolvers; - TenantSettingsConnection?: TenantSettingsConnectionResolvers; TenantsConnection?: TenantsConnectionResolvers; TriggerWalletAddressEventsMutationResponse?: TriggerWalletAddressEventsMutationResponseResolvers; UInt8?: GraphQLScalarType; diff --git a/packages/backend/migrations/20250301103930_rename_wallet_address_url_to_address.js b/packages/backend/migrations/20250301103930_rename_wallet_address_url_to_address.js new file mode 100644 index 0000000000..b5bdee041d --- /dev/null +++ b/packages/backend/migrations/20250301103930_rename_wallet_address_url_to_address.js @@ -0,0 +1,19 @@ +/** + * @param { import("knex").Knex } knex + * @returns { Promise } + */ +exports.up = function (knex) { + return knex.schema.alterTable('walletAddresses', (table) => { + table.renameColumn('url', 'address') + }) +} + +/** + * @param { import("knex").Knex } knex + * @returns { Promise } + */ +exports.down = function (knex) { + return knex.schema.alterTable('walletAddresses', (table) => { + table.renameColumn('address', 'url') + }) +} diff --git a/packages/backend/migrations/20250301203110_unique_tenant_settings_key.js b/packages/backend/migrations/20250301203110_unique_tenant_settings_key.js new file mode 100644 index 0000000000..41d3ce1402 --- /dev/null +++ b/packages/backend/migrations/20250301203110_unique_tenant_settings_key.js @@ -0,0 +1,19 @@ +/** + * @param { import("knex").Knex } knex + * @returns { Promise } + */ +exports.up = function (knex) { + return knex.schema.alterTable('tenantSettings', function (table) { + table.unique(['tenantId', 'key']) + }) +} + +/** + * @param { import("knex").Knex } knex + * @returns { Promise } + */ +exports.down = function (knex) { + return knex.schema.alterTable('tenantSettings', function (table) { + table.dropUnique(['tenantId', 'key']) + }) +} diff --git a/packages/backend/src/asset/service.test.ts b/packages/backend/src/asset/service.test.ts index df93c6bd24..c7b6008c03 100644 --- a/packages/backend/src/asset/service.test.ts +++ b/packages/backend/src/asset/service.test.ts @@ -26,6 +26,8 @@ import { TenantSettingService } from '../tenants/settings/service' import { exchangeRatesSetting } from '../tests/tenantSettings' +import { createTenantSettings } from '../tests/tenantSettings' +import { TenantSettingKeys } from '../tenants/settings/model' describe('Asset Service', (): void => { let deps: IocContract @@ -71,6 +73,18 @@ describe('Asset Service', (): void => { await appContainer.shutdown() }) + beforeEach(async () => { + await createTenantSettings(deps, { + tenantId: Config.operatorTenantId, + setting: [ + { + key: TenantSettingKeys.WALLET_ADDRESS_URL.name, + value: 'https://alice.me' + } + ] + }) + }) + describe('create', (): void => { test.each` withdrawalThreshold | liquidityThreshold @@ -370,8 +384,8 @@ describe('Asset Service', (): void => { const newAssetId = newAsset.id // make sure there is at least 1 wallet address using asset - const walletAddress = walletAddressService.create({ - url: 'https://alice.me/.well-known/pay', + const walletAddress = await walletAddressService.create({ + address: 'https://alice.me/.well-known/pay', tenantId: Config.operatorTenantId, assetId: newAssetId }) diff --git a/packages/backend/src/graphql/generated/graphql.schema.json b/packages/backend/src/graphql/generated/graphql.schema.json index 77a408266d..4e0058a90d 100644 --- a/packages/backend/src/graphql/generated/graphql.schema.json +++ b/packages/backend/src/graphql/generated/graphql.schema.json @@ -2202,6 +2202,26 @@ "defaultValue": null, "isDeprecated": false, "deprecationReason": null + }, + { + "name": "settings", + "description": "Initial settings for tenant.", + "type": { + "kind": "LIST", + "name": null, + "ofType": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "INPUT_OBJECT", + "name": "TenantSettingInput", + "ofType": null + } + } + }, + "defaultValue": null, + "isDeprecated": false, + "deprecationReason": null } ], "interfaces": null, @@ -2304,6 +2324,22 @@ "isDeprecated": false, "deprecationReason": null }, + { + "name": "address", + "description": "Wallet address. This cannot be changed.", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + }, + "defaultValue": null, + "isDeprecated": false, + "deprecationReason": null + }, { "name": "assetId", "description": "Unique identifier of the asset associated with the wallet address. This cannot be changed.", @@ -2355,22 +2391,6 @@ "defaultValue": null, "isDeprecated": false, "deprecationReason": null - }, - { - "name": "url", - "description": "Wallet address URL. This cannot be changed.", - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "SCALAR", - "name": "String", - "ofType": null - } - }, - "defaultValue": null, - "isDeprecated": false, - "deprecationReason": null } ], "interfaces": null, @@ -8315,72 +8335,23 @@ { "name": "settings", "description": "List of settings for the tenant.", - "args": [ - { - "name": "after", - "description": "Forward pagination: Cursor (wallet address key ID) to start retrieving settings after this point.", - "type": { - "kind": "SCALAR", - "name": "String", - "ofType": null - }, - "defaultValue": null, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "before", - "description": "Backward pagination: Cursor (wallet address key ID) to start retrieving keys before this point.", - "type": { - "kind": "SCALAR", - "name": "String", - "ofType": null - }, - "defaultValue": null, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "first", - "description": "Forward pagination: Limit the result to the first **n** keys after the `after` cursor.", - "type": { - "kind": "SCALAR", - "name": "Int", - "ofType": null - }, - "defaultValue": null, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "last", - "description": "Backward pagination: Limit the result to the last **n** keys before the `before` cursor.", - "type": { - "kind": "SCALAR", - "name": "Int", - "ofType": null - }, - "defaultValue": null, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "sortOrder", - "description": "Specify the sort order of keys based on their creation data, either ascending or descending.", - "type": { - "kind": "ENUM", - "name": "SortOrder", - "ofType": null - }, - "defaultValue": null, - "isDeprecated": false, - "deprecationReason": null - } - ], + "args": [], "type": { - "kind": "OBJECT", - "name": "TenantSettingsConnection", - "ofType": null + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "LIST", + "name": null, + "ofType": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "OBJECT", + "name": "TenantSetting", + "ofType": null + } + } + } }, "isDeprecated": false, "deprecationReason": null @@ -8510,49 +8481,6 @@ "enumValues": null, "possibleTypes": null }, - { - "kind": "OBJECT", - "name": "TenantSettingEdge", - "description": null, - "fields": [ - { - "name": "cursor", - "description": "A cursor for paginating through the tenants.", - "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "SCALAR", - "name": "String", - "ofType": null - } - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "node", - "description": "A tenant setting node in the list.", - "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "OBJECT", - "name": "TenantSetting", - "ofType": null - } - }, - "isDeprecated": false, - "deprecationReason": null - } - ], - "inputFields": null, - "interfaces": [], - "enumValues": null, - "possibleTypes": null - }, { "kind": "INPUT_OBJECT", "name": "TenantSettingInput", @@ -8596,57 +8524,6 @@ "enumValues": null, "possibleTypes": null }, - { - "kind": "OBJECT", - "name": "TenantSettingsConnection", - "description": null, - "fields": [ - { - "name": "edges", - "description": "A list of edges representing tenant settings and cursors for pagination.", - "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "LIST", - "name": null, - "ofType": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "OBJECT", - "name": "TenantSettingEdge", - "ofType": null - } - } - } - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "pageInfo", - "description": "Information to aid in pagination.", - "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "OBJECT", - "name": "PageInfo", - "ofType": null - } - }, - "isDeprecated": false, - "deprecationReason": null - } - ], - "inputFields": null, - "interfaces": [], - "enumValues": null, - "possibleTypes": null - }, { "kind": "OBJECT", "name": "TenantsConnection", @@ -9323,6 +9200,22 @@ "isDeprecated": false, "deprecationReason": null }, + { + "name": "address", + "description": "Wallet Address.", + "args": [], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + }, + "isDeprecated": false, + "deprecationReason": null + }, { "name": "asset", "description": "Asset of the wallet address.", @@ -9642,22 +9535,6 @@ "isDeprecated": false, "deprecationReason": null }, - { - "name": "url", - "description": "Wallet Address URL.", - "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "SCALAR", - "name": "String", - "ofType": null - } - }, - "isDeprecated": false, - "deprecationReason": null - }, { "name": "walletAddressKeys", "description": "List of keys associated with this wallet address", diff --git a/packages/backend/src/graphql/generated/graphql.ts b/packages/backend/src/graphql/generated/graphql.ts index 77a84f73a1..8db21d81f0 100644 --- a/packages/backend/src/graphql/generated/graphql.ts +++ b/packages/backend/src/graphql/generated/graphql.ts @@ -378,6 +378,8 @@ export type CreateTenantInput = { idpSecret?: InputMaybe; /** Public name for the tenant. */ publicName?: InputMaybe; + /** Initial settings for tenant. */ + settings?: InputMaybe>; }; export type CreateTenantSettingsInput = { @@ -394,6 +396,8 @@ export type CreateTenantSettingsMutationResponse = { export type CreateWalletAddressInput = { /** Additional properties associated with the wallet address. */ additionalProperties?: InputMaybe>; + /** Wallet address. This cannot be changed. */ + address: Scalars['String']['input']; /** Unique identifier of the asset associated with the wallet address. This cannot be changed. */ assetId: Scalars['String']['input']; /** Unique key to ensure duplicate or retried requests are processed only once. For more information, refer to [idempotency](https://rafiki.dev/apis/graphql/admin-api-overview/#idempotency). */ @@ -402,8 +406,6 @@ export type CreateWalletAddressInput = { 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; - /** Wallet address URL. This cannot be changed. */ - url: Scalars['String']['input']; }; export type CreateWalletAddressKeyInput = { @@ -1488,16 +1490,7 @@ export type Tenant = Model & { /** Public name for the tenant. */ publicName?: Maybe; /** List of settings for the tenant. */ - settings?: Maybe; -}; - - -export type TenantSettingsArgs = { - after?: InputMaybe; - before?: InputMaybe; - first?: InputMaybe; - last?: InputMaybe; - sortOrder?: InputMaybe; + settings: Array; }; export type TenantEdge = { @@ -1521,14 +1514,6 @@ export type TenantSetting = { value: Scalars['String']['output']; }; -export type TenantSettingEdge = { - __typename?: 'TenantSettingEdge'; - /** A cursor for paginating through the tenants. */ - cursor: Scalars['String']['output']; - /** A tenant setting node in the list. */ - node: TenantSetting; -}; - export type TenantSettingInput = { /** Key for this setting. */ key: Scalars['String']['input']; @@ -1536,14 +1521,6 @@ export type TenantSettingInput = { value: Scalars['String']['input']; }; -export type TenantSettingsConnection = { - __typename?: 'TenantSettingsConnection'; - /** A list of edges representing tenant settings and cursors for pagination. */ - edges: Array; - /** Information to aid in pagination. */ - pageInfo: PageInfo; -}; - export type TenantsConnection = { __typename?: 'TenantsConnection'; /** A list of edges representing tenants and cursors for pagination. */ @@ -1669,6 +1646,8 @@ export type WalletAddress = Model & { __typename?: 'WalletAddress'; /** Additional properties associated with the wallet address. */ additionalProperties?: Maybe>>; + /** Wallet Address. */ + address: Scalars['String']['output']; /** Asset of the wallet address. */ asset: Asset; /** The date and time when the wallet address was created. */ @@ -1689,8 +1668,6 @@ export type WalletAddress = Model & { status: WalletAddressStatus; /** Tenant ID of the wallet address. */ tenantId?: Maybe; - /** Wallet Address URL. */ - url: Scalars['String']['output']; /** List of keys associated with this wallet address */ walletAddressKeys?: Maybe; }; @@ -2032,9 +2009,7 @@ export type ResolversTypes = { TenantEdge: ResolverTypeWrapper>; TenantMutationResponse: ResolverTypeWrapper>; TenantSetting: ResolverTypeWrapper>; - TenantSettingEdge: ResolverTypeWrapper>; TenantSettingInput: ResolverTypeWrapper>; - TenantSettingsConnection: ResolverTypeWrapper>; TenantsConnection: ResolverTypeWrapper>; TransferState: ResolverTypeWrapper>; TransferType: ResolverTypeWrapper>; @@ -2170,9 +2145,7 @@ export type ResolversParentTypes = { TenantEdge: Partial; TenantMutationResponse: Partial; TenantSetting: Partial; - TenantSettingEdge: Partial; TenantSettingInput: Partial; - TenantSettingsConnection: Partial; TenantsConnection: Partial; TriggerWalletAddressEventsInput: Partial; TriggerWalletAddressEventsMutationResponse: Partial; @@ -2641,7 +2614,7 @@ export type TenantResolvers, ParentType, ContextType>; idpSecret?: Resolver, ParentType, ContextType>; publicName?: Resolver, ParentType, ContextType>; - settings?: Resolver, ParentType, ContextType, Partial>; + settings?: Resolver, ParentType, ContextType>; __isTypeOf?: IsTypeOfResolverFn; }; @@ -2662,18 +2635,6 @@ export type TenantSettingResolvers; }; -export type TenantSettingEdgeResolvers = { - cursor?: Resolver; - node?: Resolver; - __isTypeOf?: IsTypeOfResolverFn; -}; - -export type TenantSettingsConnectionResolvers = { - edges?: Resolver, ParentType, ContextType>; - pageInfo?: Resolver; - __isTypeOf?: IsTypeOfResolverFn; -}; - export type TenantsConnectionResolvers = { edges?: Resolver, ParentType, ContextType>; pageInfo?: Resolver; @@ -2705,6 +2666,7 @@ export type UpdateWalletAddressMutationResponseResolvers = { additionalProperties?: Resolver>>, ParentType, ContextType>; + address?: Resolver; asset?: Resolver; createdAt?: Resolver; id?: Resolver; @@ -2715,7 +2677,6 @@ export type WalletAddressResolvers, ParentType, ContextType, Partial>; status?: Resolver; tenantId?: Resolver, ParentType, ContextType>; - url?: Resolver; walletAddressKeys?: Resolver, ParentType, ContextType, Partial>; __isTypeOf?: IsTypeOfResolverFn; }; @@ -2849,8 +2810,6 @@ export type Resolvers = { TenantEdge?: TenantEdgeResolvers; TenantMutationResponse?: TenantMutationResponseResolvers; TenantSetting?: TenantSettingResolvers; - TenantSettingEdge?: TenantSettingEdgeResolvers; - TenantSettingsConnection?: TenantSettingsConnectionResolvers; TenantsConnection?: TenantsConnectionResolvers; TriggerWalletAddressEventsMutationResponse?: TriggerWalletAddressEventsMutationResponseResolvers; UInt8?: GraphQLScalarType; diff --git a/packages/backend/src/graphql/resolvers/tenant.test.ts b/packages/backend/src/graphql/resolvers/tenant.test.ts index 99cda31197..2c76994ab9 100644 --- a/packages/backend/src/graphql/resolvers/tenant.test.ts +++ b/packages/backend/src/graphql/resolvers/tenant.test.ts @@ -10,56 +10,18 @@ import { } from '../generated/graphql' import { initIocContainer } from '../..' import { Config, IAppConfig } from '../../config/app' -import { createTenant, generateTenantInput } from '../../tests/tenant' +import { + createTenant, + createTenantedApolloClient, + generateTenantInput +} from '../../tests/tenant' import { ApolloError, gql, NormalizedCacheObject } from '@apollo/client' import { getPageTests } from './page.test' import { truncateTables } from '../../tests/tableManager' -import { - createHttpLink, - ApolloLink, - ApolloClient, - InMemoryCache -} from '@apollo/client' -import { setContext } from '@apollo/client/link/context' +import { ApolloClient } from '@apollo/client' import { GraphQLErrorCode } from '../errors' import { Tenant as TenantModel } from '../../tenants/model' -function createTenantedApolloClient( - appContainer: TestContainer, - tenantId: string -): ApolloClient { - const httpLink = createHttpLink({ - uri: `http://localhost:${appContainer.app.getAdminPort()}/graphql`, - fetch - }) - const authLink = setContext((_, { headers }) => { - return { - headers: { - ...headers, - 'tenant-id': tenantId - } - } - }) - - const link = ApolloLink.from([authLink, httpLink]) - - return new ApolloClient({ - cache: new InMemoryCache({}), - link: link, - defaultOptions: { - query: { - fetchPolicy: 'no-cache' - }, - mutate: { - fetchPolicy: 'no-cache' - }, - watchQuery: { - fetchPolicy: 'no-cache' - } - } - }) -} - describe('Tenant Resolvers', (): void => { let deps: IocContract let appContainer: TestContainer diff --git a/packages/backend/src/graphql/resolvers/tenant.ts b/packages/backend/src/graphql/resolvers/tenant.ts index eae36ebc3a..854accc83f 100644 --- a/packages/backend/src/graphql/resolvers/tenant.ts +++ b/packages/backend/src/graphql/resolvers/tenant.ts @@ -10,6 +10,7 @@ import { GraphQLErrorCode } from '../errors' import { Tenant } from '../../tenants/model' import { Pagination, SortOrder } from '../../shared/baseModel' import { getPageInfo } from '../../shared/pagination' +import { tenantSettingsToGraphql } from './tenant_settings' export const whoami: QueryResolvers['whoami'] = async ( parent, @@ -182,6 +183,7 @@ export function tenantToGraphQl(tenant: Tenant): SchemaTenant { idpConsentUrl: tenant.idpConsentUrl, idpSecret: tenant.idpSecret, publicName: tenant.publicName, + settings: tenantSettingsToGraphql(tenant.settings), createdAt: new Date(+tenant.createdAt).toISOString(), deletedAt: tenant.deletedAt ? new Date(+tenant.deletedAt).toISOString() diff --git a/packages/backend/src/graphql/resolvers/tenant_settings.test.ts b/packages/backend/src/graphql/resolvers/tenant_settings.test.ts index 473f1e0795..4fc96c90c8 100644 --- a/packages/backend/src/graphql/resolvers/tenant_settings.test.ts +++ b/packages/backend/src/graphql/resolvers/tenant_settings.test.ts @@ -85,7 +85,7 @@ describe('Tenant Settings Resolvers', (): void => { }) afterAll(async (): Promise => { - await appContainer.apolloClient.stop() + appContainer.apolloClient.stop() await appContainer.shutdown() }) @@ -123,4 +123,47 @@ describe('Tenant Settings Resolvers', (): void => { expect(response.settings.length).toBeGreaterThan(0) }) }) + + describe('Get Tenant Settings', (): void => { + test('can get tenant settings', async (): Promise => { + const tenant = await createTenant(deps) + const client = createTenantedApolloClient(appContainer, tenant.id) + + // Query the settings + const response = await client + .query({ + query: gql` + query GetTenantSettings($id: String!) { + tenant(id: $id) { + settings { + key + value + } + } + } + `, + variables: { id: tenant.id } + }) + .then((query): { key: string; value: string }[] => { + if (query.data && query.data.tenant) { + return query.data.tenant.settings + } + throw new Error('Data was empty') + }) + + expect(response.length).toBeGreaterThan(0) + expect(response).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + key: TenantSettingKeys.WEBHOOK_MAX_RETRY.name, + value: String(TenantSettingKeys.WEBHOOK_MAX_RETRY.default) + }), + expect.objectContaining({ + key: TenantSettingKeys.WEBHOOK_TIMEOUT.name, + value: String(TenantSettingKeys.WEBHOOK_TIMEOUT.default) + }) + ]) + ) + }) + }) }) diff --git a/packages/backend/src/graphql/resolvers/tenant_settings.ts b/packages/backend/src/graphql/resolvers/tenant_settings.ts index 37dfa2565b..ceb474c524 100644 --- a/packages/backend/src/graphql/resolvers/tenant_settings.ts +++ b/packages/backend/src/graphql/resolvers/tenant_settings.ts @@ -1,21 +1,14 @@ import { TenantedApolloContext } from '../../app' -import { Pagination } from '../../shared/baseModel' -import { getPageInfo } from '../../shared/pagination' import { TenantSetting } from '../../tenants/settings/model' import { ResolversTypes, - SortOrder, TenantResolvers, TenantSetting as SchemaTenantSetting, MutationResolvers } from '../generated/graphql' export const getTenantSettings: TenantResolvers['settings'] = - async ( - parent, - args, - ctx - ): Promise => { + async (parent, args, ctx): Promise => { if (!parent.id) { throw new Error('missing tenant id') } @@ -24,27 +17,11 @@ export const getTenantSettings: TenantResolvers['settings 'tenantSettingService' ) - const { sortOrder, ...pagination } = args - const order = sortOrder === 'ASC' ? SortOrder.Asc : SortOrder.Desc - - const tenantSettings = await tenantSettingsService.getPage( - parent.id, - pagination, - order - ) - const pageInfo = await getPageInfo({ - getPage: (pagination_?: Pagination, sortOrder_?: SortOrder) => - tenantSettingsService.getPage(parent.id!, pagination_, sortOrder_), - page: tenantSettings + const tenantSettings = await tenantSettingsService.get({ + tenantId: parent.id }) - return { - pageInfo, - edges: tenantSettings.map((ts: TenantSetting) => ({ - cursor: ts.id, - node: tenantSettingsToGraphql(ts) - })) - } + return tenantSettingsToGraphql(tenantSettings) } export const createTenantSettings: MutationResolvers['createTenantSettings'] = @@ -61,13 +38,22 @@ export const createTenantSettings: MutationResolvers['cre }) return { - settings: tenantSettings.map((x) => tenantSettingsToGraphql(x)) + settings: tenantSettingsToGraphql(tenantSettings) } } -export const tenantSettingsToGraphql = ( +const tenantSettingToGraphql = ( tenantSetting: TenantSetting ): SchemaTenantSetting => ({ key: tenantSetting.key, value: tenantSetting.value }) + +export const tenantSettingsToGraphql = ( + tenantSettings?: TenantSetting[] +): SchemaTenantSetting[] => { + if (!tenantSettings) { + return [] + } + return tenantSettings.map((x) => tenantSettingToGraphql(x)) +} diff --git a/packages/backend/src/graphql/resolvers/wallet_address.test.ts b/packages/backend/src/graphql/resolvers/wallet_address.test.ts index 045956da46..d5b4212cf6 100644 --- a/packages/backend/src/graphql/resolvers/wallet_address.test.ts +++ b/packages/backend/src/graphql/resolvers/wallet_address.test.ts @@ -42,6 +42,8 @@ import { GraphQLErrorCode } from '../errors' import { AssetService } from '../../asset/service' import { faker } from '@faker-js/faker' import { Tenant } from '../../tenants/model' +import { createTenantSettings } from '../../tests/tenantSettings' +import { TenantSettingKeys } from '../../tenants/settings/model' import { createTenant } from '../../tests/tenant' describe('Wallet Address Resolvers', (): void => { @@ -71,6 +73,18 @@ describe('Wallet Address Resolvers', (): void => { await appContainer.shutdown() }) + beforeEach(async () => { + await createTenantSettings(deps, { + tenantId: Config.operatorTenantId, + setting: [ + { + key: TenantSettingKeys.WALLET_ADDRESS_URL.name, + value: 'https://alice.me' + } + ] + }) + }) + describe('Create Wallet Address', (): void => { let asset: Asset let input: CreateWalletAddressInput @@ -80,7 +94,7 @@ describe('Wallet Address Resolvers', (): void => { input = { assetId: asset.id, tenantId: Config.operatorTenantId, - url: 'https://alice.me/.well-known/pay' + address: 'https://alice.me/.well-known/pay' } }) @@ -103,7 +117,7 @@ describe('Wallet Address Resolvers', (): void => { code scale } - url + address publicName } } @@ -125,7 +139,7 @@ describe('Wallet Address Resolvers', (): void => { expect(response.walletAddress).toEqual({ __typename: 'WalletAddress', id: response.walletAddress.id, - url: input.url, + address: input.address, asset: { __typename: 'Asset', code: asset.code, @@ -169,7 +183,7 @@ describe('Wallet Address Resolvers', (): void => { code scale } - url + address publicName additionalProperties { key @@ -196,7 +210,7 @@ describe('Wallet Address Resolvers', (): void => { expect(response.walletAddress).toEqual({ __typename: 'WalletAddress', id: response.walletAddress.id, - url: input.url, + address: input.address, asset: { __typename: 'Asset', code: asset.code, @@ -330,7 +344,7 @@ describe('Wallet Address Resolvers', (): void => { const badInputData = { tenantId: uuid(), // some tenant other than requestor assetId: input.assetId, - url: input.url + address: input.address } try { expect.assertions(2) @@ -386,10 +400,20 @@ describe('Wallet Address Resolvers', (): void => { }, nonOperatorTenant.id ) + await createTenantSettings(deps, { + tenantId: nonOperatorTenant.id, + setting: [ + { + key: TenantSettingKeys.WALLET_ADDRESS_URL.name, + value: 'https://bob.me' + } + ] + }) + const input = { tenantId: nonOperatorTenant.id, assetId: asset.id, - url: 'https://bob.me/.well-known/pay' + address: 'https://bob.me/.well-known/pay' } const response = await appContainer.apolloClient // operator client .mutate({ @@ -402,7 +426,7 @@ describe('Wallet Address Resolvers', (): void => { code scale } - url + address } } } @@ -423,7 +447,7 @@ describe('Wallet Address Resolvers', (): void => { expect(response.walletAddress).toEqual({ __typename: 'WalletAddress', id: response.walletAddress.id, - url: input.url, + address: input.address, asset: { __typename: 'Asset', code: asset.code, @@ -786,10 +810,21 @@ describe('Wallet Address Resolvers', (): void => { scale: 2, tenantId: newTenant!.id }) + + await createTenantSettings(deps, { + tenantId: newTenant.id, + setting: [ + { + key: TenantSettingKeys.WALLET_ADDRESS_URL.name, + value: 'https://alice.me' + } + ] + }) + const newWalletAddress = await walletAddressService.create({ assetId: (newAsset as Asset).id, tenantId: newTenant!.id, - url: 'https://alice.me/.well-known/pay-2' + address: 'https://alice.me/.well-known/pay-2' }) const id = (newWalletAddress as WalletAddressModel).id @@ -852,10 +887,10 @@ describe('Wallet Address Resolvers', (): void => { const additionalProperties = [walletProp01, walletProp02] const walletAddress = await createWalletAddress(deps, { - tenantId: Config.operatorTenantId, publicName, createLiquidityAccount: true, - additionalProperties + additionalProperties, + tenantId: Config.operatorTenantId }) const query = await appContainer.apolloClient .query({ @@ -868,7 +903,7 @@ describe('Wallet Address Resolvers', (): void => { code scale } - url + address publicName additionalProperties { key @@ -899,7 +934,7 @@ describe('Wallet Address Resolvers', (): void => { code: walletAddress.asset.code, scale: walletAddress.asset.scale }, - url: walletAddress.url, + address: walletAddress.address, publicName: publicName ?? null, additionalProperties: [ { @@ -931,7 +966,7 @@ describe('Wallet Address Resolvers', (): void => { publicName, createLiquidityAccount: true }) - const args = { url: walletAddress.url } + const args = { url: walletAddress.address } const query = await appContainer.apolloClient .query({ query: gql` @@ -943,7 +978,7 @@ describe('Wallet Address Resolvers', (): void => { code scale } - url + address publicName additionalProperties { key @@ -972,7 +1007,7 @@ describe('Wallet Address Resolvers', (): void => { code: walletAddress.asset.code, scale: walletAddress.asset.scale }, - url: walletAddress.url, + address: walletAddress.address, publicName: publicName ?? null, additionalProperties: [] }) @@ -1042,7 +1077,7 @@ describe('Wallet Address Resolvers', (): void => { code scale } - url + address publicName } cursor @@ -1071,7 +1106,7 @@ describe('Wallet Address Resolvers', (): void => { code: walletAddress.asset.code, scale: walletAddress.asset.scale }, - url: walletAddress.url, + address: walletAddress.address, publicName: walletAddress.publicName }) }) @@ -1097,7 +1132,7 @@ describe('Wallet Address Resolvers', (): void => { code scale } - url + address publicName } cursor @@ -1129,7 +1164,7 @@ describe('Wallet Address Resolvers', (): void => { code: walletAddress.asset.code, scale: walletAddress.asset.scale }, - url: walletAddress.url, + address: walletAddress.address, publicName: walletAddress.publicName }) }) diff --git a/packages/backend/src/graphql/resolvers/wallet_address.ts b/packages/backend/src/graphql/resolvers/wallet_address.ts index d4766b521d..1fe36e61b7 100644 --- a/packages/backend/src/graphql/resolvers/wallet_address.ts +++ b/packages/backend/src/graphql/resolvers/wallet_address.ts @@ -107,6 +107,7 @@ export const createWalletAddress: MutationResolvers['createW }) const tenantId = ctx.forTenantId + if (!tenantId) throw new GraphQLError( `Assignment to the specified tenant is not permitted`, @@ -122,7 +123,8 @@ export const createWalletAddress: MutationResolvers['createW tenantId, additionalProperties: addProps, publicName: args.input.publicName, - url: args.input.url + address: args.input.address, + isOperator: ctx.isOperator } const walletAddressOrError = await walletAddressService.create(options) @@ -206,7 +208,7 @@ export function walletAddressToGraphql( ): SchemaWalletAddress { return { id: walletAddress.id, - url: walletAddress.url, + address: walletAddress.address, asset: assetToGraphql(walletAddress.asset), publicName: walletAddress.publicName ?? undefined, createdAt: new Date(+walletAddress.createdAt).toISOString(), diff --git a/packages/backend/src/graphql/schema.graphql b/packages/backend/src/graphql/schema.graphql index 397c1feb61..7142a4d762 100644 --- a/packages/backend/src/graphql/schema.graphql +++ b/packages/backend/src/graphql/schema.graphql @@ -796,8 +796,8 @@ type WalletAddress implements Model { "Current amount of liquidity available for this wallet address." liquidity: UInt64 - "Wallet Address URL." - url: String! + "Wallet Address." + address: String! "Public name associated with the wallet address. This is visible to anyone with the wallet address URL." publicName: String @@ -1302,8 +1302,8 @@ input CreateWalletAddressInput { 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." - url: String! + "Wallet address. This cannot be changed." + address: String! "Public name associated with the wallet address. This is visible to anyone with the wallet address URL." publicName: String "Unique key to ensure duplicate or retried requests are processed only once. For more information, refer to [idempotency](https://rafiki.dev/apis/graphql/admin-api-overview/#idempotency)." @@ -1590,32 +1590,7 @@ type Tenant implements Model { "The date and time that this tenant was deleted." deletedAt: String "List of settings for the tenant." - settings( - "Forward pagination: Cursor (wallet address key ID) to start retrieving settings after this point." - after: String - "Backward pagination: Cursor (wallet address key ID) to start retrieving keys before this point." - before: String - "Forward pagination: Limit the result to the first **n** keys after the `after` cursor." - first: Int - "Backward pagination: Limit the result to the last **n** keys before the `before` cursor." - last: Int - "Specify the sort order of keys based on their creation data, either ascending or descending." - sortOrder: SortOrder - ): TenantSettingsConnection -} - -type TenantSettingsConnection { - "Information to aid in pagination." - pageInfo: PageInfo! - "A list of edges representing tenant settings and cursors for pagination." - edges: [TenantSettingEdge!]! -} - -type TenantSettingEdge { - "A tenant setting node in the list." - node: TenantSetting! - "A cursor for paginating through the tenants." - cursor: String! + settings: [TenantSetting!]! } type TenantsConnection { @@ -1650,6 +1625,8 @@ input CreateTenantInput { idpSecret: String "Public name for the tenant." publicName: String + "Initial settings for tenant." + settings: [TenantSettingInput!] } input UpdateTenantInput { diff --git a/packages/backend/src/index.ts b/packages/backend/src/index.ts index d84b7c2c7b..8583d7fd09 100644 --- a/packages/backend/src/index.ts +++ b/packages/backend/src/index.ts @@ -420,7 +420,8 @@ export function initIocContainer( accountingService: await deps.use('accountingService'), webhookService: await deps.use('webhookService'), assetService: await deps.use('assetService'), - walletAddressCache: await deps.use('walletAddressCache') + walletAddressCache: await deps.use('walletAddressCache'), + tenantSettingService: await deps.use('tenantSettingService') }) }) container.singleton('spspRoutes', async (deps) => { diff --git a/packages/backend/src/open_payments/payment/incoming/model.test.ts b/packages/backend/src/open_payments/payment/incoming/model.test.ts index 3f1f91c76e..351d7150e4 100644 --- a/packages/backend/src/open_payments/payment/incoming/model.test.ts +++ b/packages/backend/src/open_payments/payment/incoming/model.test.ts @@ -45,7 +45,7 @@ describe('Models', (): void => { walletAddress = await createWalletAddress(deps, { tenantId: Config.operatorTenantId }) - baseUrl = new URL(walletAddress.url).origin + baseUrl = new URL(walletAddress.address).origin incomingPayment = await createIncomingPayment(deps, { walletAddressId: walletAddress.id, metadata: { description: 'my payment' }, @@ -57,7 +57,7 @@ describe('Models', (): void => { test('returns incoming payment', async () => { expect(incomingPayment.toOpenPaymentsType(walletAddress)).toEqual({ id: `${baseUrl}/${Config.operatorTenantId}${IncomingPayment.urlPath}/${incomingPayment.id}`, - walletAddress: walletAddress.url, + walletAddress: walletAddress.address, completed: incomingPayment.completed, receivedAmount: serializeAmount(incomingPayment.receivedAmount), incomingAmount: incomingPayment.incomingAmount @@ -85,7 +85,7 @@ describe('Models', (): void => { ) ).toEqual({ id: `${baseUrl}/${Config.operatorTenantId}${IncomingPayment.urlPath}/${incomingPayment.id}`, - walletAddress: walletAddress.url, + walletAddress: walletAddress.address, completed: incomingPayment.completed, receivedAmount: serializeAmount(incomingPayment.receivedAmount), incomingAmount: incomingPayment.incomingAmount @@ -110,7 +110,7 @@ describe('Models', (): void => { incomingPayment.toOpenPaymentsTypeWithMethods(walletAddress) ).toEqual({ id: `${baseUrl}/${Config.operatorTenantId}${IncomingPayment.urlPath}/${incomingPayment.id}`, - walletAddress: walletAddress.url, + walletAddress: walletAddress.address, completed: incomingPayment.completed, receivedAmount: serializeAmount(incomingPayment.receivedAmount), incomingAmount: incomingPayment.incomingAmount @@ -141,7 +141,7 @@ describe('Models', (): void => { ) ).toEqual({ id: `${baseUrl}/${Config.operatorTenantId}${IncomingPayment.urlPath}/${incomingPayment.id}`, - walletAddress: walletAddress.url, + walletAddress: walletAddress.address, completed: incomingPayment.completed, receivedAmount: serializeAmount(incomingPayment.receivedAmount), incomingAmount: incomingPayment.incomingAmount diff --git a/packages/backend/src/open_payments/payment/incoming/model.ts b/packages/backend/src/open_payments/payment/incoming/model.ts index 4739efc9dc..3f833d97ac 100644 --- a/packages/backend/src/open_payments/payment/incoming/model.ts +++ b/packages/backend/src/open_payments/payment/incoming/model.ts @@ -144,7 +144,7 @@ export class IncomingPayment } public getUrl(walletAddress: WalletAddress): string { - const url = new URL(walletAddress.url) + const url = new URL(walletAddress.address) return `${url.origin}/${walletAddress.tenantId}${IncomingPayment.urlPath}/${this.id}` } @@ -225,7 +225,7 @@ export class IncomingPayment ): OpenPaymentsIncomingPayment { return { id: this.getUrl(walletAddress), - walletAddress: walletAddress.url, + walletAddress: walletAddress.address, incomingAmount: this.incomingAmount ? serializeAmount(this.incomingAmount) : undefined, diff --git a/packages/backend/src/open_payments/payment/incoming/routes.test.ts b/packages/backend/src/open_payments/payment/incoming/routes.test.ts index 506aadf3ea..0b02c103f7 100644 --- a/packages/backend/src/open_payments/payment/incoming/routes.test.ts +++ b/packages/backend/src/open_payments/payment/incoming/routes.test.ts @@ -59,7 +59,7 @@ describe('Incoming Payment Routes', (): void => { tenantId, assetId: asset.id }) - baseUrl = new URL(walletAddress.url).origin + baseUrl = new URL(walletAddress.address).origin incomingAmount = { value: BigInt('123'), assetScale: asset.scale, @@ -97,7 +97,7 @@ describe('Incoming Payment Routes', (): void => { const response: Partial = { id: incomingPayment.getUrl(walletAddress), - walletAddress: walletAddress.url, + walletAddress: walletAddress.address, completed: false, incomingAmount: incomingPayment.incomingAmount && @@ -280,7 +280,7 @@ describe('Incoming Payment Routes', (): void => { expect(ctx.response.body).toEqual({ id: `${baseUrl}/${tenantId}/incoming-payments/${incomingPaymentId}`, - walletAddress: walletAddress.url, + walletAddress: walletAddress.address, incomingAmount: incomingAmount ? amount : undefined, expiresAt: expiresAt || expect.any(String), createdAt: expect.any(String), @@ -335,7 +335,7 @@ describe('Incoming Payment Routes', (): void => { expect(ctx.response).toSatisfyApiSpec() expect(ctx.body).toEqual({ id: incomingPayment.getUrl(walletAddress), - walletAddress: walletAddress.url, + walletAddress: walletAddress.address, incomingAmount: { value: '123', assetCode: asset.code, diff --git a/packages/backend/src/open_payments/payment/incoming/service.test.ts b/packages/backend/src/open_payments/payment/incoming/service.test.ts index 39b436341b..59754ed899 100644 --- a/packages/backend/src/open_payments/payment/incoming/service.test.ts +++ b/packages/backend/src/open_payments/payment/incoming/service.test.ts @@ -60,7 +60,7 @@ describe('Incoming Payment Service', (): void => { assetId: asset.id }) walletAddressId = address.id - client = address.url + client = address.address }) afterEach(async (): Promise => { diff --git a/packages/backend/src/open_payments/payment/outgoing/model.ts b/packages/backend/src/open_payments/payment/outgoing/model.ts index 302d8ba72e..3a9b8201d4 100644 --- a/packages/backend/src/open_payments/payment/outgoing/model.ts +++ b/packages/backend/src/open_payments/payment/outgoing/model.ts @@ -108,7 +108,7 @@ export class OutgoingPayment } public getUrl(walletAddress: WalletAddress): string { - const url = new URL(walletAddress.url) + const url = new URL(walletAddress.address) return `${url.origin}/${this.tenantId}${OutgoingPayment.urlPath}/${this.id}` } @@ -206,7 +206,7 @@ export class OutgoingPayment ): OpenPaymentsOutgoingPayment { return { id: this.getUrl(walletAddress), - walletAddress: walletAddress.url, + walletAddress: walletAddress.address, quoteId: this.quote?.getUrl(walletAddress) ?? undefined, receiveAmount: serializeAmount(this.receiveAmount), debitAmount: serializeAmount(this.debitAmount), diff --git a/packages/backend/src/open_payments/payment/outgoing/routes.test.ts b/packages/backend/src/open_payments/payment/outgoing/routes.test.ts index 4f04a3642c..87015c93b9 100644 --- a/packages/backend/src/open_payments/payment/outgoing/routes.test.ts +++ b/packages/backend/src/open_payments/payment/outgoing/routes.test.ts @@ -84,7 +84,7 @@ describe('Outgoing Payment Routes', (): void => { tenantId, assetId: asset.id }) - baseUrl = new URL(walletAddress.url).origin + baseUrl = new URL(walletAddress.address).origin }) afterEach(async (): Promise => { @@ -121,7 +121,7 @@ describe('Outgoing Payment Routes', (): void => { getBody: (outgoingPayment) => { return { id: `${baseUrl}/${tenantId}/outgoing-payments/${outgoingPayment.id}`, - walletAddress: walletAddress.url, + walletAddress: walletAddress.address, receiver: outgoingPayment.receiver, quoteId: outgoingPayment.quote.getUrl(walletAddress), debitAmount: serializeAmount(outgoingPayment.debitAmount), @@ -252,7 +252,7 @@ describe('Outgoing Payment Routes', (): void => { .pop() expect(ctx.response.body).toEqual({ id: `${baseUrl}/${tenantId}/outgoing-payments/${outgoingPaymentId}`, - walletAddress: walletAddress.url, + walletAddress: walletAddress.address, receiver: payment.receiver, quoteId: 'quoteId' in options ? options.quoteId : expect.any(String), diff --git a/packages/backend/src/open_payments/payment/outgoing/service.test.ts b/packages/backend/src/open_payments/payment/outgoing/service.test.ts index ae6e86fd54..a2fa3150ce 100644 --- a/packages/backend/src/open_payments/payment/outgoing/service.test.ts +++ b/packages/backend/src/open_payments/payment/outgoing/service.test.ts @@ -289,7 +289,7 @@ describe('OutgoingPaymentService', (): void => { assetId: sendAssetId }) walletAddressId = walletAddress.id - client = walletAddress.url + client = walletAddress.address const { id: destinationAssetId } = await createAsset(deps, destinationAsset) receiverWalletAddress = await createWalletAddress(deps, { tenantId, diff --git a/packages/backend/src/open_payments/quote/model.ts b/packages/backend/src/open_payments/quote/model.ts index a05352e228..104430496b 100644 --- a/packages/backend/src/open_payments/quote/model.ts +++ b/packages/backend/src/open_payments/quote/model.ts @@ -66,7 +66,7 @@ export class Quote extends WalletAddressSubresource { private debitAmountValue!: bigint public getUrl(walletAddress: WalletAddress): string { - const url = new URL(walletAddress.url) + const url = new URL(walletAddress.address) return `${url.origin}/${this.tenantId}${Quote.urlPath}/${this.id}` } @@ -134,7 +134,7 @@ export class Quote extends WalletAddressSubresource { public toOpenPaymentsType(walletAddress: WalletAddress): OpenPaymentsQuote { return { id: this.getUrl(walletAddress), - walletAddress: walletAddress.url, + walletAddress: walletAddress.address, receiveAmount: serializeAmount(this.receiveAmount), debitAmount: serializeAmount(this.debitAmount), receiver: this.receiver, diff --git a/packages/backend/src/open_payments/quote/routes.test.ts b/packages/backend/src/open_payments/quote/routes.test.ts index 2c480bd45d..9e71da8419 100644 --- a/packages/backend/src/open_payments/quote/routes.test.ts +++ b/packages/backend/src/open_payments/quote/routes.test.ts @@ -85,7 +85,7 @@ describe('Quote Routes', (): void => { tenantId, assetId }) - baseUrl = new URL(walletAddress.url).origin + baseUrl = new URL(walletAddress.address).origin }) afterEach(async (): Promise => { @@ -109,7 +109,7 @@ describe('Quote Routes', (): void => { getBody: (quote) => { return { id: `${baseUrl}/${quote.tenantId}/quotes/${quote.id}`, - walletAddress: walletAddress.url, + walletAddress: walletAddress.address, receiver: quote.receiver, debitAmount: serializeAmount(quote.debitAmount), receiveAmount: serializeAmount(quote.receiveAmount), @@ -145,7 +145,7 @@ describe('Quote Routes', (): void => { test('returns error on invalid debitAmount asset', async (): Promise => { options = { - walletAddress: walletAddress.url, + walletAddress: walletAddress.address, receiver, debitAmount: { ...debitAmount, @@ -174,7 +174,7 @@ describe('Quote Routes', (): void => { '$description', async ({ debitAmount, receiveAmount }): Promise => { options = { - walletAddress: walletAddress.url, + walletAddress: walletAddress.address, receiver, method: 'ilp' } @@ -227,7 +227,7 @@ describe('Quote Routes', (): void => { assert.ok(quote) expect(ctx.response.body).toEqual({ id: `${baseUrl}/${tenantId}/quotes/${quoteId}`, - walletAddress: walletAddress.url, + walletAddress: walletAddress.address, receiver: quote.receiver, debitAmount: { ...quote.debitAmount, @@ -246,7 +246,7 @@ describe('Quote Routes', (): void => { test('receiver.incomingAmount', async (): Promise => { options = { - walletAddress: walletAddress.url, + walletAddress: walletAddress.address, receiver, method: 'ilp' } @@ -279,7 +279,7 @@ describe('Quote Routes', (): void => { assert.ok(quote) expect(ctx.response.body).toEqual({ id: `${baseUrl}/${tenantId}/quotes/${quoteId}`, - walletAddress: walletAddress.url, + walletAddress: walletAddress.address, receiver: options.receiver, debitAmount: { ...quote.debitAmount, diff --git a/packages/backend/src/open_payments/quote/service.test.ts b/packages/backend/src/open_payments/quote/service.test.ts index 9ecc0601ba..f2f11b21c0 100644 --- a/packages/backend/src/open_payments/quote/service.test.ts +++ b/packages/backend/src/open_payments/quote/service.test.ts @@ -144,7 +144,7 @@ describe('QuoteService', (): void => { createQuote(deps, { tenantId, walletAddressId: sendingWalletAddress.id, - receiver: `${receivingWalletAddress.url}/incoming-payments/${uuid()}`, + receiver: `${receivingWalletAddress.address}/incoming-payments/${uuid()}`, debitAmount: { value: BigInt(56), assetCode: asset.code, @@ -446,7 +446,7 @@ describe('QuoteService', (): void => { quoteService.create({ tenantId: unknownTenantId, walletAddressId: walletAddress.id, - receiver: `${receivingWalletAddress.url}/incoming-payments/${uuid()}`, + receiver: `${receivingWalletAddress.address}/incoming-payments/${uuid()}`, debitAmount, method: 'ilp' }) @@ -466,7 +466,7 @@ describe('QuoteService', (): void => { quoteService.create({ tenantId, walletAddressId: unknownWalletAddressId, - receiver: `${receivingWalletAddress.url}/incoming-payments/${uuid()}`, + receiver: `${receivingWalletAddress.address}/incoming-payments/${uuid()}`, debitAmount, method: 'ilp' }) @@ -490,7 +490,7 @@ describe('QuoteService', (): void => { quoteService.create({ tenantId, walletAddressId: walletAddress.id, - receiver: `${receivingWalletAddress.url}/incoming-payments/${uuid()}`, + receiver: `${receivingWalletAddress.address}/incoming-payments/${uuid()}`, debitAmount, method: 'ilp' }) @@ -502,7 +502,7 @@ describe('QuoteService', (): void => { quoteService.create({ tenantId, walletAddressId: sendingWalletAddress.id, - receiver: `${receivingWalletAddress.url}/incoming-payments/${uuid()}`, + receiver: `${receivingWalletAddress.address}/incoming-payments/${uuid()}`, debitAmount, method: 'ilp' }) diff --git a/packages/backend/src/open_payments/receiver/model.test.ts b/packages/backend/src/open_payments/receiver/model.test.ts index 295de1dec9..a3dfbf5d8e 100644 --- a/packages/backend/src/open_payments/receiver/model.test.ts +++ b/packages/backend/src/open_payments/receiver/model.test.ts @@ -65,7 +65,7 @@ describe('Receiver Model', (): void => { sharedSecret: expect.any(Buffer), incomingPayment: { id: incomingPayment.getUrl(walletAddress), - walletAddress: walletAddress.url, + walletAddress: walletAddress.address, updatedAt: incomingPayment.updatedAt, createdAt: incomingPayment.createdAt, completed: incomingPayment.completed, diff --git a/packages/backend/src/open_payments/receiver/service.test.ts b/packages/backend/src/open_payments/receiver/service.test.ts index 4881e0617f..3bde3215a5 100644 --- a/packages/backend/src/open_payments/receiver/service.test.ts +++ b/packages/backend/src/open_payments/receiver/service.test.ts @@ -105,7 +105,7 @@ describe('Receiver Service', (): void => { sharedSecret: expect.any(Buffer), incomingPayment: { id: incomingPayment.getUrl(walletAddress), - walletAddress: walletAddress.url, + walletAddress: walletAddress.address, incomingAmount: incomingPayment.incomingAmount, receivedAmount: incomingPayment.receivedAmount, completed: false, @@ -315,7 +315,7 @@ describe('Receiver Service', (): void => { 'create' ) const receiver = await receiverService.create({ - walletAddressUrl: walletAddress.url, + walletAddressUrl: walletAddress.address, incomingAmount, expiresAt, metadata, @@ -367,7 +367,7 @@ describe('Receiver Service', (): void => { await expect( receiverService.create({ - walletAddressUrl: walletAddress.url, + walletAddressUrl: walletAddress.address, tenantId }) ).resolves.toEqual(ReceiverError.InvalidAmount) @@ -380,7 +380,7 @@ describe('Receiver Service', (): void => { await expect( receiverService.create({ - walletAddressUrl: walletAddress.url, + walletAddressUrl: walletAddress.address, tenantId }) ).rejects.toThrow( diff --git a/packages/backend/src/open_payments/wallet_address/errors.ts b/packages/backend/src/open_payments/wallet_address/errors.ts index 03d672762c..6d0f2151cd 100644 --- a/packages/backend/src/open_payments/wallet_address/errors.ts +++ b/packages/backend/src/open_payments/wallet_address/errors.ts @@ -4,7 +4,8 @@ export enum WalletAddressError { InvalidUrl = 'InvalidUrl', UnknownAsset = 'UnknownAsset', UnknownWalletAddress = 'UnknownWalletAddress', - DuplicateWalletAddress = 'DuplicateWalletAddress' + DuplicateWalletAddress = 'DuplicateWalletAddress', + WalletAddressSettingNotFound = 'WalletAddressSettingNotFound' } // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/explicit-module-boundary-types @@ -17,7 +18,8 @@ export const errorToCode: { [WalletAddressError.InvalidUrl]: GraphQLErrorCode.BadUserInput, [WalletAddressError.UnknownAsset]: GraphQLErrorCode.BadUserInput, [WalletAddressError.UnknownWalletAddress]: GraphQLErrorCode.NotFound, - [WalletAddressError.DuplicateWalletAddress]: GraphQLErrorCode.Duplicate + [WalletAddressError.DuplicateWalletAddress]: GraphQLErrorCode.Duplicate, + [WalletAddressError.WalletAddressSettingNotFound]: GraphQLErrorCode.NotFound } export const errorToMessage: { @@ -27,5 +29,7 @@ export const errorToMessage: { [WalletAddressError.UnknownAsset]: 'unknown asset', [WalletAddressError.UnknownWalletAddress]: 'unknown wallet address', [WalletAddressError.DuplicateWalletAddress]: - 'Duplicate wallet address found with the same url' + 'Duplicate wallet address found with the same url', + [WalletAddressError.WalletAddressSettingNotFound]: + 'Setting for wallet address has not been found.' } diff --git a/packages/backend/src/open_payments/wallet_address/key/routes.test.ts b/packages/backend/src/open_payments/wallet_address/key/routes.test.ts index 1608b3580f..73222c87cb 100644 --- a/packages/backend/src/open_payments/wallet_address/key/routes.test.ts +++ b/packages/backend/src/open_payments/wallet_address/key/routes.test.ts @@ -61,7 +61,7 @@ describe('Wallet Address Keys Routes', (): void => { headers: { Accept: 'application/json' }, url: `/jwks.json` }) - ctx.walletAddressUrl = walletAddress.url + ctx.walletAddressUrl = walletAddress.address await expect(walletAddressKeyRoutes.get(ctx)).resolves.toBeUndefined() expect(ctx.response).toSatisfyApiSpec() @@ -79,7 +79,7 @@ describe('Wallet Address Keys Routes', (): void => { headers: { Accept: 'application/json' }, url: `/jwks.json` }) - ctx.walletAddressUrl = walletAddress.url + ctx.walletAddressUrl = walletAddress.address await expect(walletAddressKeyRoutes.get(ctx)).resolves.toBeUndefined() expect(ctx.body).toEqual({ @@ -134,7 +134,7 @@ describe('Wallet Address Keys Routes', (): void => { const ctx = createContext({ headers: { Accept: 'application/json' } }) - ctx.walletAddressUrl = walletAddress.url + ctx.walletAddressUrl = walletAddress.address const getOrPollByUrlSpy = jest.spyOn( walletAddressService, diff --git a/packages/backend/src/open_payments/wallet_address/middleware.test.ts b/packages/backend/src/open_payments/wallet_address/middleware.test.ts index af62847107..97c6b0f869 100644 --- a/packages/backend/src/open_payments/wallet_address/middleware.test.ts +++ b/packages/backend/src/open_payments/wallet_address/middleware.test.ts @@ -142,7 +142,7 @@ describe('Wallet Address Middleware', (): void => { jest.spyOn(incomingPaymentService, 'get').mockResolvedValueOnce({ id: incomingPaymentId, walletAddress: { - url: walletAddressUrl + address: walletAddressUrl } } as IncomingPayment) @@ -203,7 +203,7 @@ describe('Wallet Address Middleware', (): void => { jest.spyOn(quoteService, 'get').mockResolvedValueOnce({ id: quoteId, walletAddress: { - url: walletAddressUrl + address: walletAddressUrl } } as Quote) @@ -319,7 +319,7 @@ describe('Wallet Address Middleware', (): void => { jest.spyOn(outgoingPaymentService, 'get').mockResolvedValueOnce({ id: outgoingPaymentId, walletAddress: { - url: walletAddressUrl + address: walletAddressUrl } } as OutgoingPayment) @@ -465,7 +465,7 @@ describe('Wallet Address Middleware', (): void => { const walletAddress = await createWalletAddress(deps, { tenantId: Config.operatorTenantId }) - ctx.walletAddressUrl = walletAddress.url + ctx.walletAddressUrl = walletAddress.address await walletAddress.$query().patch({ deactivatedAt: new Date() }) @@ -484,7 +484,7 @@ describe('Wallet Address Middleware', (): void => { const walletAddress = await createWalletAddress(deps, { tenantId: Config.operatorTenantId }) - ctx.walletAddressUrl = walletAddress.url + ctx.walletAddressUrl = walletAddress.address await expect( getWalletAddressForSubresource(ctx, next) diff --git a/packages/backend/src/open_payments/wallet_address/middleware.ts b/packages/backend/src/open_payments/wallet_address/middleware.ts index 49fa7c3f21..fefadbe10c 100644 --- a/packages/backend/src/open_payments/wallet_address/middleware.ts +++ b/packages/backend/src/open_payments/wallet_address/middleware.ts @@ -54,7 +54,7 @@ export async function getWalletAddressUrlFromIncomingPayment( }) } - ctx.walletAddressUrl = incomingPayment.walletAddress.url + ctx.walletAddressUrl = incomingPayment.walletAddress.address await next() } @@ -77,7 +77,7 @@ export async function getWalletAddressUrlFromOutgoingPayment( }) } - ctx.walletAddressUrl = outgoingPayment.walletAddress.url + ctx.walletAddressUrl = outgoingPayment.walletAddress.address await next() } @@ -98,7 +98,7 @@ export async function getWalletAddressUrlFromQuote( }) } - ctx.walletAddressUrl = quote.walletAddress.url + ctx.walletAddressUrl = quote.walletAddress.address await next() } diff --git a/packages/backend/src/open_payments/wallet_address/model.test.ts b/packages/backend/src/open_payments/wallet_address/model.test.ts index c46499557b..cc7bd279b4 100644 --- a/packages/backend/src/open_payments/wallet_address/model.test.ts +++ b/packages/backend/src/open_payments/wallet_address/model.test.ts @@ -58,7 +58,7 @@ export const setup = < options.params ) ctx.walletAddress = options.walletAddress - ctx.walletAddressUrl = options.walletAddress.url + ctx.walletAddressUrl = options.walletAddress.address ctx.grant = options.grant ctx.client = options.client ctx.accessAction = options.accessAction diff --git a/packages/backend/src/open_payments/wallet_address/model.ts b/packages/backend/src/open_payments/wallet_address/model.ts index 3991c16f53..972c1ce2f5 100644 --- a/packages/backend/src/open_payments/wallet_address/model.ts +++ b/packages/backend/src/open_payments/wallet_address/model.ts @@ -56,7 +56,7 @@ export class WalletAddress public keys?: WalletAddressKey[] public additionalProperties?: WalletAddressAdditionalProperty[] - public url!: string + public address!: string public publicName?: string public readonly assetId!: string @@ -124,7 +124,7 @@ export class WalletAddress resourceServer: string }): OpenPaymentsWalletAddress { const returnVal: OpenPaymentsWalletAddress = { - id: this.url, + id: this.address, publicName: this.publicName, assetCode: this.asset.code, assetScale: this.asset.scale, diff --git a/packages/backend/src/open_payments/wallet_address/routes.test.ts b/packages/backend/src/open_payments/wallet_address/routes.test.ts index 87da55b2ec..227fc40430 100644 --- a/packages/backend/src/open_payments/wallet_address/routes.test.ts +++ b/packages/backend/src/open_payments/wallet_address/routes.test.ts @@ -71,7 +71,7 @@ describe('Wallet Address Routes', (): void => { const ctx = createContext({ headers: { Accept: 'application/json' } }) - ctx.walletAddressUrl = walletAddress.url + ctx.walletAddressUrl = walletAddress.address const getOrPollByUrlSpy = jest.spyOn( walletAddressService, @@ -112,11 +112,11 @@ describe('Wallet Address Routes', (): void => { headers: { Accept: 'application/json' }, url: '/' }) - ctx.walletAddressUrl = walletAddress.url + ctx.walletAddressUrl = walletAddress.address await expect(walletAddressRoutes.get(ctx)).resolves.toBeUndefined() expect(ctx.response).toSatisfyApiSpec() expect(ctx.body).toEqual({ - id: walletAddress.url, + id: walletAddress.address, publicName: walletAddress.publicName, assetCode: walletAddress.asset.code, assetScale: walletAddress.asset.scale, @@ -156,11 +156,11 @@ describe('Wallet Address Routes', (): void => { headers: { Accept: 'application/json' }, url: '/' }) - ctx.walletAddressUrl = walletAddress.url + ctx.walletAddressUrl = walletAddress.address await expect(walletAddressRoutes.get(ctx)).resolves.toBeUndefined() expect(ctx.response).toSatisfyApiSpec() expect(ctx.body).toEqual({ - id: walletAddress.url, + id: walletAddress.address, publicName: walletAddress.publicName, assetCode: walletAddress.asset.code, assetScale: walletAddress.asset.scale, diff --git a/packages/backend/src/open_payments/wallet_address/service.test.ts b/packages/backend/src/open_payments/wallet_address/service.test.ts index 1fb01fc48d..c81b810786 100644 --- a/packages/backend/src/open_payments/wallet_address/service.test.ts +++ b/packages/backend/src/open_payments/wallet_address/service.test.ts @@ -27,6 +27,8 @@ import { sleep } from '../../shared/utils' import { withConfigOverride } from '../../tests/helpers' import { WalletAddressAdditionalProperty } from './additional_property/model' import { CacheDataStore } from '../../middleware/cache/data-stores' +import { createTenantSettings } from '../../tests/tenantSettings' +import { TenantSettingKeys } from '../../tenants/settings/model' describe('Open Payments Wallet Address Service', (): void => { let deps: IocContract @@ -57,14 +59,26 @@ describe('Open Payments Wallet Address Service', (): void => { await appContainer.shutdown() }) - describe('Create or Get Wallet Address', (): void => { + describe('Create or Get Wallet Address3', (): void => { + let tenantId: string let options: CreateOptions beforeEach(async (): Promise => { - const { id: tenantId } = await createTenant(deps) + tenantId = (await createTenant(deps)).id const { id: assetId } = await createAsset(deps, undefined, tenantId) + + await createTenantSettings(deps, { + tenantId: tenantId, + setting: [ + { + key: TenantSettingKeys.WALLET_ADDRESS_URL.name, + value: 'https://alice.me' + } + ] + }) + options = { - url: 'https://alice.me/.well-known/pay', + address: 'https://alice.me/.well-known/pay', assetId, tenantId } @@ -89,47 +103,126 @@ describe('Open Payments Wallet Address Service', (): void => { } ) - test('Cannot create wallet address with unknown asset', async (): Promise => { - await expect( - walletAddressService.create({ + test.each` + isOperator | tenantSettingUrl + ${false} | ${undefined} + ${true} | ${undefined} + ${true} | ${'https://alice.me'} + `( + 'operator - $isOperator with tenantSettingUrl - $tenantSettingUrl', + async ({ isOperator, tenantSettingUrl }): Promise => { + const address = 'test' + const tempTenant = await createTenant(deps) + const { id: tempAssetId } = await createAsset( + deps, + undefined, + tempTenant.id + ) + + let expected: string = WalletAddressError.WalletAddressSettingNotFound + if (tenantSettingUrl) { + await createTenantSettings(deps, { + tenantId: tempTenant.id, + setting: [ + { + key: TenantSettingKeys.WALLET_ADDRESS_URL.name, + value: tenantSettingUrl + } + ] + }) + expected = `${tenantSettingUrl}/${address}` + } else { + if (isOperator) { + expected = `https://op.example/${address}` + } + } + + const created = await walletAddressService.create({ ...options, - assetId: uuid() + address, + isOperator, + assetId: tempAssetId, + tenantId: tempTenant.id }) - ).resolves.toEqual(WalletAddressError.UnknownAsset) + + if (isWalletAddressError(expected)) { + expect(created).toEqual(expected) + } else { + assert.ok(!isWalletAddressError(created)) + expect(created.address).toEqual(expected) + } + } + ) + + test('should return error without tenant settings if caller is not an operator', async () => { + const tempTenant = await createTenant(deps) + + expect( + await walletAddressService.create({ + ...options, + tenantId: tempTenant.id + }) + ).toEqual(WalletAddressError.WalletAddressSettingNotFound) + }) + + test('should return InvalidUrl error if wallet address URL does not start with tenant wallet address URL', async (): Promise => { + const result = await walletAddressService.create({ + ...options, + address: 'https://bob.me/.well-known/pay' + }) + expect(result).toEqual(WalletAddressError.InvalidUrl) }) test.each` - url | description - ${'not a url'} | ${'without a valid url'} - ${'http://alice.me/pay'} | ${'with a non-https url'} - ${'https://alice.me'} | ${'with a url without a path'} - ${'https://alice.me/'} | ${'with a url without a path'} + setting | address | generated + ${'https://alice.me/ilp'} | ${'https://alice.me/ilp/test'} | ${'https://alice.me/ilp/test'} + ${'https://alice.me/ilp'} | ${'test'} | ${'https://alice.me/ilp/test'} + ${'https://alice.me/ilp'} | ${'/test'} | ${'https://alice.me/ilp/test'} + ${'https://alice.me/ilp/'} | ${'test'} | ${'https://alice.me/ilp/test'} + ${'https://alice.me/ilp/'} | ${'/test'} | ${'https://alice.me/ilp/test'} `( - 'Wallet address cannot be created $description ($url)', - async ({ url }): Promise => { - await expect( - walletAddressService.create({ - ...options, - url - }) - ).resolves.toEqual(WalletAddressError.InvalidUrl) + 'should create address $generated with address $address and setting $setting', + async ({ setting, address, generated }): Promise => { + await createTenantSettings(deps, { + tenantId: tenantId, + setting: [ + { key: TenantSettingKeys.WALLET_ADDRESS_URL.name, value: setting } + ] + }) + + const walletAddress = await walletAddressService.create({ + ...options, + address + }) + + assert.ok(!isWalletAddressError(walletAddress)) + expect(walletAddress.address).toEqual(generated) } ) + test('Cannot create wallet address with unknown asset', async (): Promise => { + await expect( + walletAddressService.create({ + ...options, + assetId: uuid() + }) + ).resolves.toEqual(WalletAddressError.UnknownAsset) + }) + test.each(FORBIDDEN_PATHS.map((path) => [path]))( 'Wallet address cannot be created with forbidden url path (%s)', async (path): Promise => { - const url = `https://alice.me${path}` + const address = `https://alice.me${path}` await expect( walletAddressService.create({ ...options, - url + address }) ).resolves.toEqual(WalletAddressError.InvalidUrl) await expect( walletAddressService.create({ ...options, - url: `${url}/more/path` + address: `${address}/more/path` }) ).resolves.toEqual(WalletAddressError.InvalidUrl) } @@ -144,26 +237,26 @@ describe('Open Payments Wallet Address Service', (): void => { }) test('Creating wallet address with case insensitiveness', async (): Promise => { - const url = 'https://Alice.me/pay' + const address = 'https://Alice.me/pay' await expect( walletAddressService.create({ ...options, - url + address }) - ).resolves.toMatchObject({ url: url.toLowerCase() }) + ).resolves.toMatchObject({ address: address.toLowerCase() }) }) test('Wallet address cannot be created if the url is duplicated', async (): Promise => { - const url = 'https://Alice.me/pay' + const address = 'https://Alice.me/pay' const wallet = walletAddressService.create({ ...options, - url + address }) assert.ok(!isWalletAddressError(wallet)) await expect( walletAddressService.create({ ...options, - url + address }) ).resolves.toEqual(WalletAddressError.DuplicateWalletAddress) }) @@ -443,15 +536,15 @@ describe('Open Payments Wallet Address Service', (): void => { tenantId: Config.operatorTenantId }) await expect( - walletAddressService.getByUrl(walletAddress.url) + walletAddressService.getByUrl(walletAddress.address) ).resolves.toEqual(walletAddress) await expect( - walletAddressService.getByUrl(walletAddress.url + '/path') + walletAddressService.getByUrl(walletAddress.address + '/path') ).resolves.toBeUndefined() await expect( - walletAddressService.getByUrl('prefix+' + walletAddress.url) + walletAddressService.getByUrl('prefix+' + walletAddress.address) ).resolves.toBeUndefined() }) @@ -476,7 +569,7 @@ describe('Open Payments Wallet Address Service', (): void => { tenantId: Config.operatorTenantId }) await expect( - walletAddressService.getOrPollByUrl(walletAddress.url) + walletAddressService.getOrPollByUrl(walletAddress.address) ).resolves.toEqual(walletAddress) }) }) @@ -521,7 +614,7 @@ describe('Open Payments Wallet Address Service', (): void => { await sleep(5) return createWalletAddress(deps, { tenantId: Config.operatorTenantId, - url: walletAddressUrl + address: walletAddressUrl }) })() ]) @@ -905,7 +998,7 @@ describe('Open Payments Wallet Address Service using Cache', (): void => { walletAddress.id, expect.objectContaining({ id: walletAddress.id, - url: walletAddress.url + address: walletAddress.address }) ) diff --git a/packages/backend/src/open_payments/wallet_address/service.ts b/packages/backend/src/open_payments/wallet_address/service.ts index c5f5d852d8..aa3f756d3a 100644 --- a/packages/backend/src/open_payments/wallet_address/service.ts +++ b/packages/backend/src/open_payments/wallet_address/service.ts @@ -6,7 +6,7 @@ import { } from 'objection' import { URL } from 'url' -import { WalletAddressError } from './errors' +import { isWalletAddressError, WalletAddressError } from './errors' import { WalletAddress, WalletAddressEvent, @@ -28,6 +28,8 @@ import { poll } from '../../shared/utils' import { WalletAddressAdditionalProperty } from './additional_property/model' import { AssetService } from '../../asset/service' import { CacheDataStore } from '../../middleware/cache/data-stores' +import { TenantSettingKeys } from '../../tenants/settings/model' +import { TenantSettingService } from '../../tenants/settings/service' interface Options { publicName?: string @@ -40,9 +42,10 @@ export type WalletAddressAdditionalPropertyInput = Pick< export interface CreateOptions extends Options { tenantId: string - url: string + address: string assetId: string additionalProperties?: WalletAddressAdditionalPropertyInput[] + isOperator?: boolean } type Status = 'ACTIVE' | 'INACTIVE' @@ -84,6 +87,7 @@ interface ServiceDependencies extends BaseService { webhookService: WebhookService assetService: AssetService walletAddressCache: CacheDataStore + tenantSettingService: TenantSettingService } export async function createWalletAddressService({ @@ -93,7 +97,8 @@ export async function createWalletAddressService({ accountingService, webhookService, assetService, - walletAddressCache + walletAddressCache, + tenantSettingService }: ServiceDependencies): Promise { const log = logger.child({ service: 'WalletAddressService' @@ -105,7 +110,8 @@ export async function createWalletAddressService({ accountingService, webhookService, assetService, - walletAddressCache + walletAddressCache, + tenantSettingService } return { create: (options) => createWalletAddress(deps, options), @@ -161,12 +167,76 @@ function cleanAdditionalProperties( .filter((prop) => prop.fieldKey.length > 0 && prop.fieldValue.length > 0) } +async function createWalletAddressUrl( + deps: ServiceDependencies, + options: CreateOptions +): Promise { + let tenantWalletAddressUrl = new URL(deps.config.openPaymentsUrl) + + const found = await deps.tenantSettingService.get({ + tenantId: options.tenantId, + key: TenantSettingKeys.WALLET_ADDRESS_URL.name + }) + + if (!found || found.length === 0) { + if (!options.isOperator) { + return WalletAddressError.WalletAddressSettingNotFound + } + } else { + tenantWalletAddressUrl = new URL(found[0].value) + } + + let tenantBaseUrl = tenantWalletAddressUrl.toString() + if (!tenantWalletAddressUrl.pathname.endsWith('/')) { + tenantBaseUrl = + tenantWalletAddressUrl.origin + tenantWalletAddressUrl.pathname + '/' + } + + const isValidUrl = (str: string): boolean => { + try { + new URL(str) + return true + } catch { + return false + } + } + + let finalWalletAddressUrl: string + if (isValidUrl(options.address)) { + // in case that client provided full url, verify that it starts with the tenant's URL + const walletAddressUrl = new URL(options.address) + if (!walletAddressUrl.href.startsWith(tenantWalletAddressUrl.href)) { + return WalletAddressError.InvalidUrl + } + finalWalletAddressUrl = walletAddressUrl.toString() + } else { + // in case that client provided just the path / wallet address name, construct the address using the wallet address url from tenant setting + try { + let relativePath = options.address + if (relativePath.startsWith('/')) { + relativePath = relativePath.substring(1) + } + finalWalletAddressUrl = tenantBaseUrl + relativePath + } catch (err) { + return WalletAddressError.InvalidUrl + } + } + + if (!isValidWalletAddressUrl(finalWalletAddressUrl)) { + return WalletAddressError.InvalidUrl + } + + return finalWalletAddressUrl +} + async function createWalletAddress( deps: ServiceDependencies, options: CreateOptions ): Promise { - if (!isValidWalletAddressUrl(options.url)) { - return WalletAddressError.InvalidUrl + const finalWalletAddressUrl = await createWalletAddressUrl(deps, options) + + if (isWalletAddressError(finalWalletAddressUrl)) { + return finalWalletAddressUrl } try { @@ -182,7 +252,7 @@ async function createWalletAddress( deps.knex ).insertGraphAndFetch({ tenantId: options.tenantId, - url: options.url.toLowerCase(), + address: finalWalletAddressUrl.toLowerCase(), publicName: options.publicName, assetId: asset.id, additionalProperties: additionalProperties @@ -341,7 +411,7 @@ async function getWalletAddressByUrl( if (tenantId) query.andWhere({ tenantId }) const walletAddress = await query.findOne({ - url: url.toLowerCase() + address: url.toLowerCase() }) if (walletAddress) { const asset = await deps.assetService.get(walletAddress.assetId) diff --git a/packages/backend/src/shared/pagination.test.ts b/packages/backend/src/shared/pagination.test.ts index 1136e50a7a..ae645ecbff 100644 --- a/packages/backend/src/shared/pagination.test.ts +++ b/packages/backend/src/shared/pagination.test.ts @@ -66,9 +66,9 @@ describe('Pagination', (): void => { first, last, cursor, - 'wallet-address': walletAddress.url + 'wallet-address': walletAddress.address }) - ).toEqual({ ...result, walletAddress: walletAddress.url }) + ).toEqual({ ...result, walletAddress: walletAddress.address }) } ) }) @@ -144,7 +144,7 @@ describe('Pagination', (): void => { pagination }), page, - walletAddress: defaultWalletAddress.url + walletAddress: defaultWalletAddress.address }) expect(pageInfo).toEqual({ startCursor: paymentIds[start], @@ -179,7 +179,7 @@ describe('Pagination', (): void => { const payment = await createOutgoingPayment(deps, { tenantId, walletAddressId: defaultWalletAddress.id, - receiver: secondaryWalletAddress.url, + receiver: secondaryWalletAddress.address, method: 'ilp', debitAmount, validDestination: false @@ -202,7 +202,7 @@ describe('Pagination', (): void => { pagination }), page, - walletAddress: defaultWalletAddress.url + walletAddress: defaultWalletAddress.address }) expect(pageInfo).toEqual({ startCursor: paymentIds[start], @@ -237,7 +237,7 @@ describe('Pagination', (): void => { const quote = await createQuote(deps, { tenantId, walletAddressId: defaultWalletAddress.id, - receiver: secondaryWalletAddress.url, + receiver: secondaryWalletAddress.address, debitAmount, validDestination: false, method: 'ilp' @@ -260,7 +260,7 @@ describe('Pagination', (): void => { pagination }), page, - walletAddress: defaultWalletAddress.url + walletAddress: defaultWalletAddress.address }) expect(pageInfo).toEqual({ startCursor: quoteIds[start], diff --git a/packages/backend/src/tenants/service.test.ts b/packages/backend/src/tenants/service.test.ts index 6dcb645737..19fce7b38f 100644 --- a/packages/backend/src/tenants/service.test.ts +++ b/packages/backend/src/tenants/service.test.ts @@ -15,7 +15,7 @@ import { createTenant } from '../tests/tenant' import { CacheDataStore } from '../middleware/cache/data-stores' import { AuthServiceClient } from '../auth-service-client/client' import { withConfigOverride } from '../tests/helpers' -import { TenantSetting } from './settings/model' +import { TenantSetting, TenantSettingKeys } from './settings/model' import { TenantSettingService } from './settings/service' import { isTenantError, TenantError } from './errors' @@ -146,6 +146,35 @@ describe('Tenant Service', (): void => { expect(tenantSettings.length).toBeGreaterThan(0) }) + test('can create a tenant with a setting', async () => { + const walletAddressUrl = 'https://example.com' + const createOptions = { + apiSecret: 'test-api-secret', + publicName: 'test tenant', + email: faker.internet.email(), + idpConsentUrl: faker.internet.url(), + idpSecret: 'test-idp-secret', + settings: [ + { + key: TenantSettingKeys.WALLET_ADDRESS_URL.name, + value: walletAddressUrl + } + ] + } + + jest + .spyOn(authServiceClient.tenant, 'create') + .mockImplementationOnce(async () => undefined) + + const tenant = await tenantService.create(createOptions) + const tenantSetting = await TenantSetting.query() + .where('tenantId', tenant.id) + .andWhere('key', TenantSettingKeys.WALLET_ADDRESS_URL.name) + + expect(tenantSetting.length).toBe(1) + expect(tenantSetting[0].value).toEqual(walletAddressUrl) + }) + test('tenant creation rolls back if auth tenant create fails', async (): Promise => { const createOptions = { apiSecret: 'test-api-secret', diff --git a/packages/backend/src/tenants/service.ts b/packages/backend/src/tenants/service.ts index 4cd56dce85..f0b4c647ed 100644 --- a/packages/backend/src/tenants/service.ts +++ b/packages/backend/src/tenants/service.ts @@ -8,6 +8,7 @@ import { TenantSettingService } from './settings/service' import { TenantSetting } from './settings/model' import type { IAppConfig } from '../config/app' import { TenantError } from './errors' +import { TenantSettingInput } from '../graphql/generated/graphql' export interface TenantService { get: (id: string, includeDeleted?: boolean) => Promise @@ -79,6 +80,7 @@ interface CreateTenantOptions { idpSecret?: string idpConsentUrl?: string publicName?: string + settings?: TenantSettingInput[] } async function createTenant( @@ -87,7 +89,8 @@ async function createTenant( ): Promise { const trx = await deps.knex.transaction() try { - const { email, apiSecret, publicName, idpSecret, idpConsentUrl } = options + const { email, apiSecret, publicName, idpSecret, idpConsentUrl, settings } = + options const tenant = await Tenant.query(trx).insertAndFetch({ email, publicName, @@ -102,13 +105,19 @@ async function createTenant( idpConsentUrl }) - await deps.tenantSettingService.create( - { - tenantId: tenant.id, - setting: TenantSetting.default() - }, - { trx } - ) + const createInitialTenantSettingsOptions = { + tenantId: tenant.id, + setting: TenantSetting.default() + } + + if (settings) { + createInitialTenantSettingsOptions.setting = + createInitialTenantSettingsOptions.setting.concat(settings) + } + + await deps.tenantSettingService.create(createInitialTenantSettingsOptions, { + trx + }) await trx.commit() diff --git a/packages/backend/src/tenants/settings/model.ts b/packages/backend/src/tenants/settings/model.ts index e91e24c057..3dba4062a4 100644 --- a/packages/backend/src/tenants/settings/model.ts +++ b/packages/backend/src/tenants/settings/model.ts @@ -2,11 +2,17 @@ import { Pojo } from 'objection' import { BaseModel } from '../../shared/baseModel' import { KeyValuePair } from './service' -export const TenantSettingKeys = { +interface TenantSettingKeyType { + name: string + default?: unknown +} + +export const TenantSettingKeys: { [key: string]: TenantSettingKeyType } = { EXCHANGE_RATES_URL: { name: 'EXCHANGE_RATES_URL' }, WEBHOOK_URL: { name: 'WEBHOOK_URL' }, WEBHOOK_TIMEOUT: { name: 'WEBHOOK_TIMEOUT', default: 2000 }, - WEBHOOK_MAX_RETRY: { name: 'WEBHOOK_MAX_RETRY', default: 10 } + WEBHOOK_MAX_RETRY: { name: 'WEBHOOK_MAX_RETRY', default: 10 }, + WALLET_ADDRESS_URL: { name: 'WALLET_ADDRESS_URL' } } export class TenantSetting extends BaseModel { @@ -29,15 +35,19 @@ export class TenantSetting extends BaseModel { } static default(): KeyValuePair[] { - return [ - { - key: TenantSettingKeys.WEBHOOK_TIMEOUT.name, - value: TenantSettingKeys.WEBHOOK_TIMEOUT.default.toString() - }, - { - key: TenantSettingKeys.WEBHOOK_MAX_RETRY.name, - value: TenantSettingKeys.WEBHOOK_MAX_RETRY.default.toString() + const settings = [] + for (const key of Object.keys(TenantSettingKeys)) { + const data = TenantSettingKeys[key] + if (!data.default) { + continue } - ] + + settings.push({ + key: data.name, + value: String(data.default) + }) + } + + return settings } } diff --git a/packages/backend/src/tenants/settings/service.test.ts b/packages/backend/src/tenants/settings/service.test.ts index 92592e3f54..f0631b14fc 100644 --- a/packages/backend/src/tenants/settings/service.test.ts +++ b/packages/backend/src/tenants/settings/service.test.ts @@ -8,13 +8,7 @@ import { truncateTables } from '../../tests/tableManager' import { Tenant } from '../model' import { TenantService } from '../service' import { faker } from '@faker-js/faker' -import { getPageTests } from '../../shared/baseModel.test' -import { Pagination, SortOrder } from '../../shared/baseModel' -import { - createTenantSettings, - exchangeRatesSetting, - randomSetting -} from '../../tests/tenantSettings' +import { exchangeRatesSetting, randomSetting } from '../../tests/tenantSettings' import { TenantSetting } from './model' import { CreateOptions, @@ -97,6 +91,36 @@ describe('TenantSetting Service', (): void => { expect(tenantSetting).toEqual([]) }) + + test('should update existing tenant settings on conflict - upsert', async (): Promise => { + const initialOptions: CreateOptions = { + tenantId: tenant.id, + setting: [exchangeRatesSetting()] + } + + await tenantSettingService.create(initialOptions) + + const newValue = faker.internet.url() + const updatedOptions: CreateOptions = { + tenantId: tenant.id, + setting: [ + { + key: initialOptions.setting[0].key, + value: newValue + } + ] + } + + await tenantSettingService.create(updatedOptions) + const result = (await tenantSettingService.get({ + tenantId: tenant.id, + key: initialOptions.setting[0].key + })) as TenantSetting[] + + expect(result).toHaveLength(1) + expect(result[0].key).toEqual(initialOptions.setting[0].key) + expect(result[0].value).toEqual(newValue) + }) }) describe('get', () => { @@ -277,20 +301,74 @@ describe('TenantSetting Service', (): void => { }) }) - describe('pagination', (): void => { - beforeEach(async () => { + describe('getTenantSettings', () => { + let tenantSetting: TenantSetting[] + + beforeEach(async (): Promise => { + const createOptions: CreateOptions = { + tenantId: tenant.id, + setting: [exchangeRatesSetting()] + } + + tenantSetting = await tenantSettingService.create(createOptions) + }) + + afterEach(async (): Promise => { await tenantSettingService.delete({ tenantId: tenant.id }) }) - describe('getPage', (): void => { - getPageTests({ - createModel: () => - createTenantSettings(deps, { + + test('should retrieve tenant settings by tenantId', async (): Promise => { + const result = await tenantSettingService.get({ + tenantId: tenant.id + }) + + expect(result).toEqual( + expect.arrayContaining([ + expect.objectContaining({ tenantId: tenant.id, - setting: [exchangeRatesSetting()] - }) as Promise, - getPage: (pagination?: Pagination, sortOrder?: SortOrder) => - tenantSettingService.getPage(tenant.id, pagination, sortOrder) + key: tenantSetting[0].key, + value: tenantSetting[0].value + }) + ]) + ) + }) + + test('should retrieve tenant settings by tenantId and key', async (): Promise => { + const result = await tenantSettingService.get({ + tenantId: tenant.id, + key: tenantSetting[0].key + }) + + expect(result).toEqual([ + expect.objectContaining({ + tenantId: tenant.id, + key: tenantSetting[0].key, + value: tenantSetting[0].value + }) + ]) + }) + + test('should return an empty array if no settings match', async (): Promise => { + const result = await tenantSettingService.get({ + tenantId: tenant.id, + key: 'nonexistent-key' + }) + + expect(result).toEqual([]) + }) + + test('should not retrieve deleted tenant settings', async (): Promise => { + await tenantSettingService.delete({ + tenantId: tenant.id, + key: tenantSetting[0].key }) + + const result = await tenantSettingService.get({ + tenantId: tenant.id, + key: tenantSetting[0].key + }) + + expect(result).toEqual([]) }) }) }) diff --git a/packages/backend/src/tenants/settings/service.ts b/packages/backend/src/tenants/settings/service.ts index bf2abb1935..5623928bf1 100644 --- a/packages/backend/src/tenants/settings/service.ts +++ b/packages/backend/src/tenants/settings/service.ts @@ -118,20 +118,22 @@ async function createTenantSetting( options: CreateOptions, extra?: ExtraOptions ) { - const dataToInsert = options.setting + const dataToUpsert = options.setting .filter((setting) => Object.keys(TenantSettingKeys).includes(setting.key)) .map((s) => ({ tenantId: options.tenantId, ...s })) - if (Object.keys(dataToInsert).length <= 0) { + if (Object.keys(dataToUpsert).length <= 0) { return [] } - return TenantSetting.query(extra?.trx ?? deps.knex).insertAndFetch( - dataToInsert - ) + return TenantSetting.query(extra?.trx ?? deps.knex) + .insert(dataToUpsert) + .onConflict(['tenantId', 'key']) + .merge() + .returning('*') } async function getTenantSettingPageForTenant( diff --git a/packages/backend/src/tests/tenant.ts b/packages/backend/src/tests/tenant.ts index d8874b4573..a5a3394f26 100644 --- a/packages/backend/src/tests/tenant.ts +++ b/packages/backend/src/tests/tenant.ts @@ -2,6 +2,15 @@ import { IocContract } from '@adonisjs/fold' import { faker } from '@faker-js/faker' import { AppServices } from '../app' import { Tenant } from '../tenants/model' +import { + ApolloClient, + ApolloLink, + createHttpLink, + InMemoryCache, + NormalizedCacheObject +} from '@apollo/client' +import { setContext } from '@apollo/client/link/context' +import { TestContainer } from './app' interface CreateOptions { email: string @@ -11,6 +20,42 @@ interface CreateOptions { idpSecret: string } +export function createTenantedApolloClient( + appContainer: TestContainer, + tenantId: string +): ApolloClient { + const httpLink = createHttpLink({ + uri: `http://localhost:${appContainer.app.getAdminPort()}/graphql`, + fetch + }) + const authLink = setContext((_, { headers }) => { + return { + headers: { + ...headers, + 'tenant-id': tenantId + } + } + }) + + const link = ApolloLink.from([authLink, httpLink]) + + return new ApolloClient({ + cache: new InMemoryCache({}), + link: link, + defaultOptions: { + query: { + fetchPolicy: 'no-cache' + }, + mutate: { + fetchPolicy: 'no-cache' + }, + watchQuery: { + fetchPolicy: 'no-cache' + } + } + }) +} + export function generateTenantInput() { return { email: faker.internet.email(), diff --git a/packages/backend/src/tests/tenantSettings.ts b/packages/backend/src/tests/tenantSettings.ts index b678cc4ec1..aa685b14b3 100644 --- a/packages/backend/src/tests/tenantSettings.ts +++ b/packages/backend/src/tests/tenantSettings.ts @@ -1,6 +1,6 @@ import { IocContract } from '@adonisjs/fold' import { AppServices } from '../app' -import { TenantSetting } from '../tenants/settings/model' +import { TenantSetting, TenantSettingKeys } from '../tenants/settings/model' import { CreateOptions, KeyValuePair } from '../tenants/settings/service' import { faker } from '@faker-js/faker' import { isTenantSettingError } from '../tenants/settings/errors' @@ -16,7 +16,7 @@ export function randomSetting(): KeyValuePair { export function exchangeRatesSetting(): KeyValuePair { return { - key: 'EXCHANGE_RATES_URL', + key: TenantSettingKeys.EXCHANGE_RATES_URL.name, value: faker.internet.url() } } diff --git a/packages/backend/src/tests/walletAddress.ts b/packages/backend/src/tests/walletAddress.ts index cc3c55092d..9ca836514c 100644 --- a/packages/backend/src/tests/walletAddress.ts +++ b/packages/backend/src/tests/walletAddress.ts @@ -12,6 +12,8 @@ import { isWalletAddressError } from '../open_payments/wallet_address/errors' import { WalletAddress } from '../open_payments/wallet_address/model' import { CreateOptions as BaseCreateOptions } from '../open_payments/wallet_address/service' import { LiquidityAccountType } from '../accounting/service' +import { createTenantSettings } from './tenantSettings' +import { TenantSettingKeys } from '../tenants/settings/model' const nock = (global as unknown as { nock: typeof import('nock') }).nock @@ -31,12 +33,25 @@ export async function createWalletAddress( ): Promise { const walletAddressService = await deps.use('walletAddressService') const tenantIdToUse = options.tenantId || (await createTenant(deps)).id + + const baseWalletAddressUrl = new URL( + options.address || `https://${faker.internet.domainName()}` + ) + await createTenantSettings(deps, { + tenantId: tenantIdToUse, + setting: [ + { + key: TenantSettingKeys.WALLET_ADDRESS_URL.name, + value: baseWalletAddressUrl.origin + } + ] + }) const walletAddressOrError = (await walletAddressService.create({ ...options, assetId: options.assetId || (await createAsset(deps, undefined, tenantIdToUse)).id, tenantId: tenantIdToUse, - url: options.url || `https://${faker.internet.domainName()}/.well-known/pay` + address: options.address || `${baseWalletAddressUrl.origin}/.well-known/pay` })) as MockWalletAddress if (isWalletAddressError(walletAddressOrError)) { throw new Error(walletAddressOrError) @@ -52,7 +67,7 @@ export async function createWalletAddress( ) } if (options.mockServerPort) { - const url = new URL(walletAddressOrError.url) + const url = new URL(walletAddressOrError.address) walletAddressOrError.scope = nock(url.origin) .get((uri) => uri.startsWith(url.pathname)) .matchHeader('Accept', /application\/((ilp-stream|spsp4)\+)?json*./) diff --git a/packages/frontend/app/generated/graphql.ts b/packages/frontend/app/generated/graphql.ts index edf1b67584..6587a706db 100644 --- a/packages/frontend/app/generated/graphql.ts +++ b/packages/frontend/app/generated/graphql.ts @@ -378,6 +378,8 @@ export type CreateTenantInput = { idpSecret?: InputMaybe; /** Public name for the tenant. */ publicName?: InputMaybe; + /** Initial settings for tenant. */ + settings?: InputMaybe>; }; export type CreateTenantSettingsInput = { @@ -394,6 +396,8 @@ export type CreateTenantSettingsMutationResponse = { export type CreateWalletAddressInput = { /** Additional properties associated with the wallet address. */ additionalProperties?: InputMaybe>; + /** Wallet address. This cannot be changed. */ + address: Scalars['String']['input']; /** Unique identifier of the asset associated with the wallet address. This cannot be changed. */ assetId: Scalars['String']['input']; /** Unique key to ensure duplicate or retried requests are processed only once. For more information, refer to [idempotency](https://rafiki.dev/apis/graphql/admin-api-overview/#idempotency). */ @@ -402,8 +406,6 @@ export type CreateWalletAddressInput = { 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; - /** Wallet address URL. This cannot be changed. */ - url: Scalars['String']['input']; }; export type CreateWalletAddressKeyInput = { @@ -1488,16 +1490,7 @@ export type Tenant = Model & { /** Public name for the tenant. */ publicName?: Maybe; /** List of settings for the tenant. */ - settings?: Maybe; -}; - - -export type TenantSettingsArgs = { - after?: InputMaybe; - before?: InputMaybe; - first?: InputMaybe; - last?: InputMaybe; - sortOrder?: InputMaybe; + settings: Array; }; export type TenantEdge = { @@ -1521,14 +1514,6 @@ export type TenantSetting = { value: Scalars['String']['output']; }; -export type TenantSettingEdge = { - __typename?: 'TenantSettingEdge'; - /** A cursor for paginating through the tenants. */ - cursor: Scalars['String']['output']; - /** A tenant setting node in the list. */ - node: TenantSetting; -}; - export type TenantSettingInput = { /** Key for this setting. */ key: Scalars['String']['input']; @@ -1536,14 +1521,6 @@ export type TenantSettingInput = { value: Scalars['String']['input']; }; -export type TenantSettingsConnection = { - __typename?: 'TenantSettingsConnection'; - /** A list of edges representing tenant settings and cursors for pagination. */ - edges: Array; - /** Information to aid in pagination. */ - pageInfo: PageInfo; -}; - export type TenantsConnection = { __typename?: 'TenantsConnection'; /** A list of edges representing tenants and cursors for pagination. */ @@ -1669,6 +1646,8 @@ export type WalletAddress = Model & { __typename?: 'WalletAddress'; /** Additional properties associated with the wallet address. */ additionalProperties?: Maybe>>; + /** Wallet Address. */ + address: Scalars['String']['output']; /** Asset of the wallet address. */ asset: Asset; /** The date and time when the wallet address was created. */ @@ -1689,8 +1668,6 @@ export type WalletAddress = Model & { status: WalletAddressStatus; /** Tenant ID of the wallet address. */ tenantId?: Maybe; - /** Wallet Address URL. */ - url: Scalars['String']['output']; /** List of keys associated with this wallet address */ walletAddressKeys?: Maybe; }; @@ -2032,9 +2009,7 @@ export type ResolversTypes = { TenantEdge: ResolverTypeWrapper>; TenantMutationResponse: ResolverTypeWrapper>; TenantSetting: ResolverTypeWrapper>; - TenantSettingEdge: ResolverTypeWrapper>; TenantSettingInput: ResolverTypeWrapper>; - TenantSettingsConnection: ResolverTypeWrapper>; TenantsConnection: ResolverTypeWrapper>; TransferState: ResolverTypeWrapper>; TransferType: ResolverTypeWrapper>; @@ -2170,9 +2145,7 @@ export type ResolversParentTypes = { TenantEdge: Partial; TenantMutationResponse: Partial; TenantSetting: Partial; - TenantSettingEdge: Partial; TenantSettingInput: Partial; - TenantSettingsConnection: Partial; TenantsConnection: Partial; TriggerWalletAddressEventsInput: Partial; TriggerWalletAddressEventsMutationResponse: Partial; @@ -2641,7 +2614,7 @@ export type TenantResolvers, ParentType, ContextType>; idpSecret?: Resolver, ParentType, ContextType>; publicName?: Resolver, ParentType, ContextType>; - settings?: Resolver, ParentType, ContextType, Partial>; + settings?: Resolver, ParentType, ContextType>; __isTypeOf?: IsTypeOfResolverFn; }; @@ -2662,18 +2635,6 @@ export type TenantSettingResolvers; }; -export type TenantSettingEdgeResolvers = { - cursor?: Resolver; - node?: Resolver; - __isTypeOf?: IsTypeOfResolverFn; -}; - -export type TenantSettingsConnectionResolvers = { - edges?: Resolver, ParentType, ContextType>; - pageInfo?: Resolver; - __isTypeOf?: IsTypeOfResolverFn; -}; - export type TenantsConnectionResolvers = { edges?: Resolver, ParentType, ContextType>; pageInfo?: Resolver; @@ -2705,6 +2666,7 @@ export type UpdateWalletAddressMutationResponseResolvers = { additionalProperties?: Resolver>>, ParentType, ContextType>; + address?: Resolver; asset?: Resolver; createdAt?: Resolver; id?: Resolver; @@ -2715,7 +2677,6 @@ export type WalletAddressResolvers, ParentType, ContextType, Partial>; status?: Resolver; tenantId?: Resolver, ParentType, ContextType>; - url?: Resolver; walletAddressKeys?: Resolver, ParentType, ContextType, Partial>; __isTypeOf?: IsTypeOfResolverFn; }; @@ -2849,8 +2810,6 @@ export type Resolvers = { TenantEdge?: TenantEdgeResolvers; TenantMutationResponse?: TenantMutationResponseResolvers; TenantSetting?: TenantSettingResolvers; - TenantSettingEdge?: TenantSettingEdgeResolvers; - TenantSettingsConnection?: TenantSettingsConnectionResolvers; TenantsConnection?: TenantsConnectionResolvers; TriggerWalletAddressEventsMutationResponse?: TriggerWalletAddressEventsMutationResponseResolvers; UInt8?: GraphQLScalarType; @@ -3088,7 +3047,7 @@ export type GetWalletAddressQueryVariables = Exact<{ }>; -export type GetWalletAddressQuery = { __typename?: 'Query', walletAddress?: { __typename?: 'WalletAddress', id: string, url: string, publicName?: string | null, status: WalletAddressStatus, createdAt: string, liquidity?: bigint | null, asset: { __typename?: 'Asset', id: string, code: string, scale: number, withdrawalThreshold?: bigint | null } } | null }; +export type GetWalletAddressQuery = { __typename?: 'Query', walletAddress?: { __typename?: 'WalletAddress', id: string, address: string, publicName?: string | null, status: WalletAddressStatus, createdAt: string, liquidity?: bigint | null, asset: { __typename?: 'Asset', id: string, code: string, scale: number, withdrawalThreshold?: bigint | null } } | null }; export type ListWalletAddresssQueryVariables = Exact<{ after?: InputMaybe; @@ -3098,7 +3057,7 @@ export type ListWalletAddresssQueryVariables = Exact<{ }>; -export type ListWalletAddresssQuery = { __typename?: 'Query', walletAddresses: { __typename?: 'WalletAddressesConnection', edges: Array<{ __typename?: 'WalletAddressEdge', cursor: string, node: { __typename?: 'WalletAddress', id: string, publicName?: string | null, status: WalletAddressStatus, url: string } }>, pageInfo: { __typename?: 'PageInfo', startCursor?: string | null, endCursor?: string | null, hasNextPage: boolean, hasPreviousPage: boolean } } }; +export type ListWalletAddresssQuery = { __typename?: 'Query', walletAddresses: { __typename?: 'WalletAddressesConnection', edges: Array<{ __typename?: 'WalletAddressEdge', cursor: string, node: { __typename?: 'WalletAddress', id: string, publicName?: string | null, status: WalletAddressStatus, address: string } }>, pageInfo: { __typename?: 'PageInfo', startCursor?: string | null, endCursor?: string | null, hasNextPage: boolean, hasPreviousPage: boolean } } }; export type UpdateWalletAddressMutationVariables = Exact<{ input: UpdateWalletAddressInput; diff --git a/packages/frontend/app/lib/api/wallet-address.server.ts b/packages/frontend/app/lib/api/wallet-address.server.ts index d87387a920..4ce2733911 100644 --- a/packages/frontend/app/lib/api/wallet-address.server.ts +++ b/packages/frontend/app/lib/api/wallet-address.server.ts @@ -29,7 +29,7 @@ export const getWalletAddress = async ( query GetWalletAddressQuery($id: String!) { walletAddress(id: $id) { id - url + address publicName status createdAt @@ -77,7 +77,7 @@ export const listWalletAddresses = async ( id publicName status - url + address } } pageInfo { diff --git a/packages/frontend/app/routes/wallet-addresses.$walletAddressId.tsx b/packages/frontend/app/routes/wallet-addresses.$walletAddressId.tsx index 77339ff04f..88b4950ee4 100644 --- a/packages/frontend/app/routes/wallet-addresses.$walletAddressId.tsx +++ b/packages/frontend/app/routes/wallet-addresses.$walletAddressId.tsx @@ -86,7 +86,7 @@ export default function ViewWalletAddressPage() { /> diff --git a/packages/frontend/app/routes/wallet-addresses._index.tsx b/packages/frontend/app/routes/wallet-addresses._index.tsx index 0771f895bb..e5946d0ca4 100644 --- a/packages/frontend/app/routes/wallet-addresses._index.tsx +++ b/packages/frontend/app/routes/wallet-addresses._index.tsx @@ -69,7 +69,7 @@ export default function WalletAddressesPage() { className='cursor-pointer' onClick={() => navigate(`/wallet-addresses/${pp.node.id}`)} > - {pp.node.url} + {pp.node.address}
{pp.node.publicName ? ( diff --git a/packages/frontend/app/routes/wallet-addresses.create.tsx b/packages/frontend/app/routes/wallet-addresses.create.tsx index 174d5ae8ca..b199ef44da 100644 --- a/packages/frontend/app/routes/wallet-addresses.create.tsx +++ b/packages/frontend/app/routes/wallet-addresses.create.tsx @@ -162,7 +162,7 @@ export async function action({ request }: ActionFunctionArgs) { const path = removeTrailingAndLeadingSlash(result.data.name) const response = await createWalletAddress(request, { - url: `${baseUrl}/${path}`, + address: `${baseUrl}/${path}`, publicName: result.data.publicName, assetId: result.data.asset, tenantId: result.data.tenantId, diff --git a/packages/mock-account-service-lib/src/generated/graphql.ts b/packages/mock-account-service-lib/src/generated/graphql.ts index 77a84f73a1..8db21d81f0 100644 --- a/packages/mock-account-service-lib/src/generated/graphql.ts +++ b/packages/mock-account-service-lib/src/generated/graphql.ts @@ -378,6 +378,8 @@ export type CreateTenantInput = { idpSecret?: InputMaybe; /** Public name for the tenant. */ publicName?: InputMaybe; + /** Initial settings for tenant. */ + settings?: InputMaybe>; }; export type CreateTenantSettingsInput = { @@ -394,6 +396,8 @@ export type CreateTenantSettingsMutationResponse = { export type CreateWalletAddressInput = { /** Additional properties associated with the wallet address. */ additionalProperties?: InputMaybe>; + /** Wallet address. This cannot be changed. */ + address: Scalars['String']['input']; /** Unique identifier of the asset associated with the wallet address. This cannot be changed. */ assetId: Scalars['String']['input']; /** Unique key to ensure duplicate or retried requests are processed only once. For more information, refer to [idempotency](https://rafiki.dev/apis/graphql/admin-api-overview/#idempotency). */ @@ -402,8 +406,6 @@ export type CreateWalletAddressInput = { 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; - /** Wallet address URL. This cannot be changed. */ - url: Scalars['String']['input']; }; export type CreateWalletAddressKeyInput = { @@ -1488,16 +1490,7 @@ export type Tenant = Model & { /** Public name for the tenant. */ publicName?: Maybe; /** List of settings for the tenant. */ - settings?: Maybe; -}; - - -export type TenantSettingsArgs = { - after?: InputMaybe; - before?: InputMaybe; - first?: InputMaybe; - last?: InputMaybe; - sortOrder?: InputMaybe; + settings: Array; }; export type TenantEdge = { @@ -1521,14 +1514,6 @@ export type TenantSetting = { value: Scalars['String']['output']; }; -export type TenantSettingEdge = { - __typename?: 'TenantSettingEdge'; - /** A cursor for paginating through the tenants. */ - cursor: Scalars['String']['output']; - /** A tenant setting node in the list. */ - node: TenantSetting; -}; - export type TenantSettingInput = { /** Key for this setting. */ key: Scalars['String']['input']; @@ -1536,14 +1521,6 @@ export type TenantSettingInput = { value: Scalars['String']['input']; }; -export type TenantSettingsConnection = { - __typename?: 'TenantSettingsConnection'; - /** A list of edges representing tenant settings and cursors for pagination. */ - edges: Array; - /** Information to aid in pagination. */ - pageInfo: PageInfo; -}; - export type TenantsConnection = { __typename?: 'TenantsConnection'; /** A list of edges representing tenants and cursors for pagination. */ @@ -1669,6 +1646,8 @@ export type WalletAddress = Model & { __typename?: 'WalletAddress'; /** Additional properties associated with the wallet address. */ additionalProperties?: Maybe>>; + /** Wallet Address. */ + address: Scalars['String']['output']; /** Asset of the wallet address. */ asset: Asset; /** The date and time when the wallet address was created. */ @@ -1689,8 +1668,6 @@ export type WalletAddress = Model & { status: WalletAddressStatus; /** Tenant ID of the wallet address. */ tenantId?: Maybe; - /** Wallet Address URL. */ - url: Scalars['String']['output']; /** List of keys associated with this wallet address */ walletAddressKeys?: Maybe; }; @@ -2032,9 +2009,7 @@ export type ResolversTypes = { TenantEdge: ResolverTypeWrapper>; TenantMutationResponse: ResolverTypeWrapper>; TenantSetting: ResolverTypeWrapper>; - TenantSettingEdge: ResolverTypeWrapper>; TenantSettingInput: ResolverTypeWrapper>; - TenantSettingsConnection: ResolverTypeWrapper>; TenantsConnection: ResolverTypeWrapper>; TransferState: ResolverTypeWrapper>; TransferType: ResolverTypeWrapper>; @@ -2170,9 +2145,7 @@ export type ResolversParentTypes = { TenantEdge: Partial; TenantMutationResponse: Partial; TenantSetting: Partial; - TenantSettingEdge: Partial; TenantSettingInput: Partial; - TenantSettingsConnection: Partial; TenantsConnection: Partial; TriggerWalletAddressEventsInput: Partial; TriggerWalletAddressEventsMutationResponse: Partial; @@ -2641,7 +2614,7 @@ export type TenantResolvers, ParentType, ContextType>; idpSecret?: Resolver, ParentType, ContextType>; publicName?: Resolver, ParentType, ContextType>; - settings?: Resolver, ParentType, ContextType, Partial>; + settings?: Resolver, ParentType, ContextType>; __isTypeOf?: IsTypeOfResolverFn; }; @@ -2662,18 +2635,6 @@ export type TenantSettingResolvers; }; -export type TenantSettingEdgeResolvers = { - cursor?: Resolver; - node?: Resolver; - __isTypeOf?: IsTypeOfResolverFn; -}; - -export type TenantSettingsConnectionResolvers = { - edges?: Resolver, ParentType, ContextType>; - pageInfo?: Resolver; - __isTypeOf?: IsTypeOfResolverFn; -}; - export type TenantsConnectionResolvers = { edges?: Resolver, ParentType, ContextType>; pageInfo?: Resolver; @@ -2705,6 +2666,7 @@ export type UpdateWalletAddressMutationResponseResolvers = { additionalProperties?: Resolver>>, ParentType, ContextType>; + address?: Resolver; asset?: Resolver; createdAt?: Resolver; id?: Resolver; @@ -2715,7 +2677,6 @@ export type WalletAddressResolvers, ParentType, ContextType, Partial>; status?: Resolver; tenantId?: Resolver, ParentType, ContextType>; - url?: Resolver; walletAddressKeys?: Resolver, ParentType, ContextType, Partial>; __isTypeOf?: IsTypeOfResolverFn; }; @@ -2849,8 +2810,6 @@ export type Resolvers = { TenantEdge?: TenantEdgeResolvers; TenantMutationResponse?: TenantMutationResponseResolvers; TenantSetting?: TenantSettingResolvers; - TenantSettingEdge?: TenantSettingEdgeResolvers; - TenantSettingsConnection?: TenantSettingsConnectionResolvers; TenantsConnection?: TenantsConnectionResolvers; TriggerWalletAddressEventsMutationResponse?: TriggerWalletAddressEventsMutationResponseResolvers; UInt8?: GraphQLScalarType; diff --git a/packages/mock-account-service-lib/src/requesters.ts b/packages/mock-account-service-lib/src/requesters.ts index 2f79324190..eba8f77fc7 100644 --- a/packages/mock-account-service-lib/src/requesters.ts +++ b/packages/mock-account-service-lib/src/requesters.ts @@ -333,15 +333,16 @@ export async function createWalletAddress( createWalletAddress(input: $input) { walletAddress { id - url + address publicName } } } ` + const createWalletAddressInput: CreateWalletAddressInput = { assetId, - url: accountUrl, + address: accountUrl, publicName: accountName, additionalProperties: [] } @@ -485,7 +486,7 @@ async function getWalletAddressByURL( walletAddressByUrl(url: $url) { id liquidity - url + address publicName asset { id diff --git a/packages/mock-account-service-lib/src/seed.ts b/packages/mock-account-service-lib/src/seed.ts index 6ccc50681a..db21d7ea74 100644 --- a/packages/mock-account-service-lib/src/seed.ts +++ b/packages/mock-account-service-lib/src/seed.ts @@ -152,7 +152,7 @@ export async function setupFromSeed( await mockAccounts.setWalletAddress( account.id, walletAddress.id, - walletAddress.url + walletAddress.address ) await createWalletAddressKey({ diff --git a/test/integration/lib/admin-client.ts b/test/integration/lib/admin-client.ts index 589e6b5ae6..d7bb9faf4b 100644 --- a/test/integration/lib/admin-client.ts +++ b/test/integration/lib/admin-client.ts @@ -240,7 +240,7 @@ export class AdminClient { createWalletAddress(input: $input) { walletAddress { id - url + address publicName } } diff --git a/test/integration/lib/generated/graphql.ts b/test/integration/lib/generated/graphql.ts index 77a84f73a1..8db21d81f0 100644 --- a/test/integration/lib/generated/graphql.ts +++ b/test/integration/lib/generated/graphql.ts @@ -378,6 +378,8 @@ export type CreateTenantInput = { idpSecret?: InputMaybe; /** Public name for the tenant. */ publicName?: InputMaybe; + /** Initial settings for tenant. */ + settings?: InputMaybe>; }; export type CreateTenantSettingsInput = { @@ -394,6 +396,8 @@ export type CreateTenantSettingsMutationResponse = { export type CreateWalletAddressInput = { /** Additional properties associated with the wallet address. */ additionalProperties?: InputMaybe>; + /** Wallet address. This cannot be changed. */ + address: Scalars['String']['input']; /** Unique identifier of the asset associated with the wallet address. This cannot be changed. */ assetId: Scalars['String']['input']; /** Unique key to ensure duplicate or retried requests are processed only once. For more information, refer to [idempotency](https://rafiki.dev/apis/graphql/admin-api-overview/#idempotency). */ @@ -402,8 +406,6 @@ export type CreateWalletAddressInput = { 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; - /** Wallet address URL. This cannot be changed. */ - url: Scalars['String']['input']; }; export type CreateWalletAddressKeyInput = { @@ -1488,16 +1490,7 @@ export type Tenant = Model & { /** Public name for the tenant. */ publicName?: Maybe; /** List of settings for the tenant. */ - settings?: Maybe; -}; - - -export type TenantSettingsArgs = { - after?: InputMaybe; - before?: InputMaybe; - first?: InputMaybe; - last?: InputMaybe; - sortOrder?: InputMaybe; + settings: Array; }; export type TenantEdge = { @@ -1521,14 +1514,6 @@ export type TenantSetting = { value: Scalars['String']['output']; }; -export type TenantSettingEdge = { - __typename?: 'TenantSettingEdge'; - /** A cursor for paginating through the tenants. */ - cursor: Scalars['String']['output']; - /** A tenant setting node in the list. */ - node: TenantSetting; -}; - export type TenantSettingInput = { /** Key for this setting. */ key: Scalars['String']['input']; @@ -1536,14 +1521,6 @@ export type TenantSettingInput = { value: Scalars['String']['input']; }; -export type TenantSettingsConnection = { - __typename?: 'TenantSettingsConnection'; - /** A list of edges representing tenant settings and cursors for pagination. */ - edges: Array; - /** Information to aid in pagination. */ - pageInfo: PageInfo; -}; - export type TenantsConnection = { __typename?: 'TenantsConnection'; /** A list of edges representing tenants and cursors for pagination. */ @@ -1669,6 +1646,8 @@ export type WalletAddress = Model & { __typename?: 'WalletAddress'; /** Additional properties associated with the wallet address. */ additionalProperties?: Maybe>>; + /** Wallet Address. */ + address: Scalars['String']['output']; /** Asset of the wallet address. */ asset: Asset; /** The date and time when the wallet address was created. */ @@ -1689,8 +1668,6 @@ export type WalletAddress = Model & { status: WalletAddressStatus; /** Tenant ID of the wallet address. */ tenantId?: Maybe; - /** Wallet Address URL. */ - url: Scalars['String']['output']; /** List of keys associated with this wallet address */ walletAddressKeys?: Maybe; }; @@ -2032,9 +2009,7 @@ export type ResolversTypes = { TenantEdge: ResolverTypeWrapper>; TenantMutationResponse: ResolverTypeWrapper>; TenantSetting: ResolverTypeWrapper>; - TenantSettingEdge: ResolverTypeWrapper>; TenantSettingInput: ResolverTypeWrapper>; - TenantSettingsConnection: ResolverTypeWrapper>; TenantsConnection: ResolverTypeWrapper>; TransferState: ResolverTypeWrapper>; TransferType: ResolverTypeWrapper>; @@ -2170,9 +2145,7 @@ export type ResolversParentTypes = { TenantEdge: Partial; TenantMutationResponse: Partial; TenantSetting: Partial; - TenantSettingEdge: Partial; TenantSettingInput: Partial; - TenantSettingsConnection: Partial; TenantsConnection: Partial; TriggerWalletAddressEventsInput: Partial; TriggerWalletAddressEventsMutationResponse: Partial; @@ -2641,7 +2614,7 @@ export type TenantResolvers, ParentType, ContextType>; idpSecret?: Resolver, ParentType, ContextType>; publicName?: Resolver, ParentType, ContextType>; - settings?: Resolver, ParentType, ContextType, Partial>; + settings?: Resolver, ParentType, ContextType>; __isTypeOf?: IsTypeOfResolverFn; }; @@ -2662,18 +2635,6 @@ export type TenantSettingResolvers; }; -export type TenantSettingEdgeResolvers = { - cursor?: Resolver; - node?: Resolver; - __isTypeOf?: IsTypeOfResolverFn; -}; - -export type TenantSettingsConnectionResolvers = { - edges?: Resolver, ParentType, ContextType>; - pageInfo?: Resolver; - __isTypeOf?: IsTypeOfResolverFn; -}; - export type TenantsConnectionResolvers = { edges?: Resolver, ParentType, ContextType>; pageInfo?: Resolver; @@ -2705,6 +2666,7 @@ export type UpdateWalletAddressMutationResponseResolvers = { additionalProperties?: Resolver>>, ParentType, ContextType>; + address?: Resolver; asset?: Resolver; createdAt?: Resolver; id?: Resolver; @@ -2715,7 +2677,6 @@ export type WalletAddressResolvers, ParentType, ContextType, Partial>; status?: Resolver; tenantId?: Resolver, ParentType, ContextType>; - url?: Resolver; walletAddressKeys?: Resolver, ParentType, ContextType, Partial>; __isTypeOf?: IsTypeOfResolverFn; }; @@ -2849,8 +2810,6 @@ export type Resolvers = { TenantEdge?: TenantEdgeResolvers; TenantMutationResponse?: TenantMutationResponseResolvers; TenantSetting?: TenantSettingResolvers; - TenantSettingEdge?: TenantSettingEdgeResolvers; - TenantSettingsConnection?: TenantSettingsConnectionResolvers; TenantsConnection?: TenantsConnectionResolvers; TriggerWalletAddressEventsMutationResponse?: TriggerWalletAddressEventsMutationResponseResolvers; UInt8?: GraphQLScalarType; diff --git a/test/integration/lib/integration-server.ts b/test/integration/lib/integration-server.ts index 54ba96559c..b835c25fe3 100644 --- a/test/integration/lib/integration-server.ts +++ b/test/integration/lib/integration-server.ts @@ -129,7 +129,7 @@ export class WebhookEventHandler { const response = await this.adminClient.createWalletAddress({ assetId, publicName, - url, + address: url, additionalProperties: [] }) const { walletAddress } = response @@ -141,7 +141,7 @@ export class WebhookEventHandler { await this.accounts.setWalletAddress( account.id, walletAddress.id, - walletAddress.url + walletAddress.address ) }