From 23e34b24054ab3921b10364a7cd09149b23f092b Mon Sep 17 00:00:00 2001 From: Nathan Lie Date: Tue, 21 Jan 2025 14:06:45 -0800 Subject: [PATCH] feat: backend tenant graphql resolvers --- .../generated/graphql.ts | 193 +++- packages/backend/src/app.ts | 51 +- .../src/graphql/generated/graphql.schema.json | 828 ++++++++++++++++-- .../backend/src/graphql/generated/graphql.ts | 193 +++- .../backend/src/graphql/resolvers/index.ts | 18 +- .../src/graphql/resolvers/tenant.test.ts | 508 +++++++++++ .../backend/src/graphql/resolvers/tenant.ts | 179 ++++ packages/backend/src/graphql/schema.graphql | 103 +++ packages/backend/src/tests/app.ts | 4 +- packages/backend/src/tests/tenant.ts | 10 + packages/frontend/app/generated/graphql.ts | 193 +++- .../src/generated/graphql.ts | 193 +++- test/integration/lib/generated/graphql.ts | 193 +++- 13 files changed, 2593 insertions(+), 73 deletions(-) create mode 100644 packages/backend/src/graphql/resolvers/tenant.test.ts create mode 100644 packages/backend/src/graphql/resolvers/tenant.ts diff --git a/localenv/mock-account-servicing-entity/generated/graphql.ts b/localenv/mock-account-servicing-entity/generated/graphql.ts index 46dad2dedb..0d37f19a34 100644 --- a/localenv/mock-account-servicing-entity/generated/graphql.ts +++ b/localenv/mock-account-servicing-entity/generated/graphql.ts @@ -364,6 +364,19 @@ export type CreateReceiverResponse = { receiver?: Maybe; }; +export type CreateTenantInput = { + /** Secret used to secure requests made for this tenant. */ + apiSecret: Scalars['String']['input']; + /** Contact email of the tenant owner. */ + email: Scalars['String']['input']; + /** URL of the tenant's identity provider's consent screen. */ + idpConsentUrl: Scalars['String']['input']; + /** Secret used to secure requests from the tenant's identity provider. */ + idpSecret: Scalars['String']['input']; + /** Public name for the tenant. */ + publicName?: InputMaybe; +}; + export type CreateWalletAddressInput = { /** Additional properties associated with the wallet address. */ additionalProperties?: InputMaybe>; @@ -440,6 +453,11 @@ export type DeletePeerMutationResponse = { success: Scalars['Boolean']['output']; }; +export type DeleteTenantMutationResponse = { + __typename?: 'DeleteTenantMutationResponse'; + success: Scalars['Boolean']['output']; +}; + export type DepositAssetLiquidityInput = { /** Amount of liquidity to deposit. */ amount: Scalars['UInt64']['input']; @@ -721,6 +739,8 @@ export type Mutation = { createQuote: QuoteResponse; /** Create an internal or external Open Payments incoming payment. The receiver has a wallet address on either this or another Open Payments resource server. */ createReceiver: CreateReceiverResponse; + /** Create a tenant. */ + createTenant: TenantMutationResponse; /** Create a new wallet address. */ createWalletAddress: CreateWalletAddressMutationResponse; /** Add a public key to a wallet address that is used to verify Open Payments requests. */ @@ -731,6 +751,8 @@ export type Mutation = { deleteAsset: DeleteAssetMutationResponse; /** Delete a peer. */ deletePeer: DeletePeerMutationResponse; + /** Delete a tenant. */ + deleteTenant: DeleteTenantMutationResponse; /** Deposit asset liquidity. */ depositAssetLiquidity?: Maybe; /** @@ -756,6 +778,8 @@ export type Mutation = { updateIncomingPayment: IncomingPaymentResponse; /** Update an existing peer. */ updatePeer: UpdatePeerMutationResponse; + /** Update a tenant. */ + updateTenant: TenantMutationResponse; /** Update an existing wallet address. */ updateWalletAddress: UpdateWalletAddressMutationResponse; /** Void liquidity withdrawal. Withdrawals are two-phase commits and are rolled back via this mutation. */ @@ -843,6 +867,11 @@ export type MutationCreateReceiverArgs = { }; +export type MutationCreateTenantArgs = { + input: CreateTenantInput; +}; + + export type MutationCreateWalletAddressArgs = { input: CreateWalletAddressInput; }; @@ -868,6 +897,11 @@ export type MutationDeletePeerArgs = { }; +export type MutationDeleteTenantArgs = { + id: Scalars['String']['input']; +}; + + export type MutationDepositAssetLiquidityArgs = { input: DepositAssetLiquidityInput; }; @@ -923,6 +957,11 @@ export type MutationUpdatePeerArgs = { }; +export type MutationUpdateTenantArgs = { + input: UpdateTenantInput; +}; + + export type MutationUpdateWalletAddressArgs = { input: UpdateWalletAddressInput; }; @@ -1150,6 +1189,10 @@ export type Query = { quote?: Maybe; /** Retrieve an Open Payments incoming payment by receiver ID. The receiver's wallet address can be hosted on this server or a remote Open Payments resource server. */ receiver?: Maybe; + /** Retrieve a tenant of the instance. */ + tenant?: Maybe; + /** Fetch a paginated list of tenants on the instance. */ + tenants: TenantsConnection; /** Fetch a wallet address by its ID. */ walletAddress?: Maybe; /** Get a wallet address by its url if it exists */ @@ -1158,6 +1201,8 @@ export type Query = { walletAddresses: WalletAddressesConnection; /** Fetch a paginated list of webhook events. */ webhookEvents: WebhookEventsConnection; + /** Determine if the requester has operator permissions */ + whoami: WhoamiResponse; }; @@ -1247,6 +1292,20 @@ export type QueryReceiverArgs = { }; +export type QueryTenantArgs = { + id: Scalars['String']['input']; +}; + + +export type QueryTenantsArgs = { + after?: InputMaybe; + before?: InputMaybe; + first?: InputMaybe; + last?: InputMaybe; + sortOrder?: InputMaybe; +}; + + export type QueryWalletAddressArgs = { id: Scalars['String']['input']; }; @@ -1376,6 +1435,47 @@ export enum SortOrder { Desc = 'DESC' } +export type Tenant = Model & { + __typename?: 'Tenant'; + /** Secret used to secure requests made for this tenant. */ + apiSecret: Scalars['String']['output']; + /** The date and time that this tenant was created. */ + createdAt: Scalars['String']['output']; + /** The date and time that this tenant was deleted. */ + deletedAt?: Maybe; + /** Contact email of the tenant owner. */ + email: Scalars['String']['output']; + /** Unique identifier of the tenant. */ + id: Scalars['ID']['output']; + /** URL of the tenant's identity provider's consent screen. */ + idpConsentUrl: Scalars['String']['output']; + /** Secret used to secure requests from the tenant's identity provider. */ + idpSecret: Scalars['String']['output']; + /** Public name for the tenant. */ + publicName?: Maybe; +}; + +export type TenantEdge = { + __typename?: 'TenantEdge'; + /** A cursor for paginating through the tenants. */ + cursor: Scalars['String']['output']; + /** A tenant node in the list. */ + node: Tenant; +}; + +export type TenantMutationResponse = { + __typename?: 'TenantMutationResponse'; + tenant: Tenant; +}; + +export type TenantsConnection = { + __typename?: 'TenantsConnection'; + /** A list of edges representing tenants and cursors for pagination. */ + edges: Array; + /** Information to aid in pagination. */ + pageInfo: PageInfo; +}; + export enum TransferState { /** The accounting transfer is pending */ Pending = 'PENDING', @@ -1448,6 +1548,21 @@ export type UpdatePeerMutationResponse = { peer?: Maybe; }; +export type UpdateTenantInput = { + /** Secret used to secure requests made for this tenant. */ + apiSecret?: InputMaybe; + /** Contact email of the tenant owner. */ + email?: InputMaybe; + /** Unique identifier of the tenant. */ + id: Scalars['ID']['input']; + /** URL of the tenant's identity provider's consent screen. */ + idpConsentUrl?: InputMaybe; + /** Secret used to secure requests from the tenant's identity provider. */ + idpSecret?: InputMaybe; + /** Public name for the tenant. */ + publicName?: InputMaybe; +}; + export type UpdateWalletAddressInput = { /** Additional properties associated with this wallet address. */ additionalProperties?: InputMaybe>; @@ -1640,6 +1755,12 @@ export type WebhookEventsEdge = { node: WebhookEvent; }; +export type WhoamiResponse = { + __typename?: 'WhoamiResponse'; + id: Scalars['String']['output']; + isOperator: Scalars['Boolean']['output']; +}; + export type WithdrawEventLiquidityInput = { /** Unique identifier of the event to withdraw liquidity from. */ eventId: Scalars['String']['input']; @@ -1718,7 +1839,7 @@ export type DirectiveResolverFn> = { BasePayment: ( Partial ) | ( Partial ) | ( Partial ); - Model: ( Partial ) | ( Partial ) | ( Partial ) | ( Partial ) | ( Partial ) | ( Partial ) | ( Partial ) | ( Partial ) | ( Partial ) | ( Partial ); + Model: ( Partial ) | ( Partial ) | ( Partial ) | ( Partial ) | ( Partial ) | ( Partial ) | ( Partial ) | ( Partial ) | ( Partial ) | ( Partial ) | ( Partial ); }; /** Mapping between all available schema types and the resolvers types */ @@ -1756,6 +1877,7 @@ export type ResolversTypes = { CreateQuoteInput: ResolverTypeWrapper>; CreateReceiverInput: ResolverTypeWrapper>; CreateReceiverResponse: ResolverTypeWrapper>; + CreateTenantInput: ResolverTypeWrapper>; CreateWalletAddressInput: ResolverTypeWrapper>; CreateWalletAddressKeyInput: ResolverTypeWrapper>; CreateWalletAddressKeyMutationResponse: ResolverTypeWrapper>; @@ -1766,6 +1888,7 @@ export type ResolversTypes = { DeleteAssetMutationResponse: ResolverTypeWrapper>; DeletePeerInput: ResolverTypeWrapper>; DeletePeerMutationResponse: ResolverTypeWrapper>; + DeleteTenantMutationResponse: ResolverTypeWrapper>; DepositAssetLiquidityInput: ResolverTypeWrapper>; DepositEventLiquidityInput: ResolverTypeWrapper>; DepositOutgoingPaymentLiquidityInput: ResolverTypeWrapper>; @@ -1825,6 +1948,10 @@ export type ResolversTypes = { SetFeeResponse: ResolverTypeWrapper>; SortOrder: ResolverTypeWrapper>; String: ResolverTypeWrapper>; + Tenant: ResolverTypeWrapper>; + TenantEdge: ResolverTypeWrapper>; + TenantMutationResponse: ResolverTypeWrapper>; + TenantsConnection: ResolverTypeWrapper>; TransferState: ResolverTypeWrapper>; TransferType: ResolverTypeWrapper>; TriggerWalletAddressEventsInput: ResolverTypeWrapper>; @@ -1835,6 +1962,7 @@ export type ResolversTypes = { UpdateIncomingPaymentInput: ResolverTypeWrapper>; UpdatePeerInput: ResolverTypeWrapper>; UpdatePeerMutationResponse: ResolverTypeWrapper>; + UpdateTenantInput: ResolverTypeWrapper>; UpdateWalletAddressInput: ResolverTypeWrapper>; UpdateWalletAddressMutationResponse: ResolverTypeWrapper>; VoidLiquidityWithdrawalInput: ResolverTypeWrapper>; @@ -1851,6 +1979,7 @@ export type ResolversTypes = { WebhookEventFilter: ResolverTypeWrapper>; WebhookEventsConnection: ResolverTypeWrapper>; WebhookEventsEdge: ResolverTypeWrapper>; + WhoamiResponse: ResolverTypeWrapper>; WithdrawEventLiquidityInput: ResolverTypeWrapper>; }; @@ -1888,6 +2017,7 @@ export type ResolversParentTypes = { CreateQuoteInput: Partial; CreateReceiverInput: Partial; CreateReceiverResponse: Partial; + CreateTenantInput: Partial; CreateWalletAddressInput: Partial; CreateWalletAddressKeyInput: Partial; CreateWalletAddressKeyMutationResponse: Partial; @@ -1897,6 +2027,7 @@ export type ResolversParentTypes = { DeleteAssetMutationResponse: Partial; DeletePeerInput: Partial; DeletePeerMutationResponse: Partial; + DeleteTenantMutationResponse: Partial; DepositAssetLiquidityInput: Partial; DepositEventLiquidityInput: Partial; DepositOutgoingPaymentLiquidityInput: Partial; @@ -1949,6 +2080,10 @@ export type ResolversParentTypes = { SetFeeInput: Partial; SetFeeResponse: Partial; String: Partial; + Tenant: Partial; + TenantEdge: Partial; + TenantMutationResponse: Partial; + TenantsConnection: Partial; TriggerWalletAddressEventsInput: Partial; TriggerWalletAddressEventsMutationResponse: Partial; UInt8: Partial; @@ -1957,6 +2092,7 @@ export type ResolversParentTypes = { UpdateIncomingPaymentInput: Partial; UpdatePeerInput: Partial; UpdatePeerMutationResponse: Partial; + UpdateTenantInput: Partial; UpdateWalletAddressInput: Partial; UpdateWalletAddressMutationResponse: Partial; VoidLiquidityWithdrawalInput: Partial; @@ -1972,6 +2108,7 @@ export type ResolversParentTypes = { WebhookEventFilter: Partial; WebhookEventsConnection: Partial; WebhookEventsEdge: Partial; + WhoamiResponse: Partial; WithdrawEventLiquidityInput: Partial; }; @@ -2093,6 +2230,11 @@ export type DeletePeerMutationResponseResolvers; }; +export type DeleteTenantMutationResponseResolvers = { + success?: Resolver; + __isTypeOf?: IsTypeOfResolverFn; +}; + export type FeeResolvers = { assetId?: Resolver; basisPoints?: Resolver; @@ -2176,7 +2318,7 @@ export type LiquidityMutationResponseResolvers = { - __resolveType: TypeResolveFn<'AccountingTransfer' | 'Asset' | 'Fee' | 'IncomingPayment' | 'OutgoingPayment' | 'Payment' | 'Peer' | 'WalletAddress' | 'WalletAddressKey' | 'WebhookEvent', ParentType, ContextType>; + __resolveType: TypeResolveFn<'AccountingTransfer' | 'Asset' | 'Fee' | 'IncomingPayment' | 'OutgoingPayment' | 'Payment' | 'Peer' | 'Tenant' | 'WalletAddress' | 'WalletAddressKey' | 'WebhookEvent', ParentType, ContextType>; createdAt?: Resolver; id?: Resolver; }; @@ -2197,11 +2339,13 @@ export type MutationResolvers, ParentType, ContextType, RequireFields>; createQuote?: Resolver>; createReceiver?: Resolver>; + createTenant?: Resolver>; createWalletAddress?: Resolver>; createWalletAddressKey?: Resolver, ParentType, ContextType, RequireFields>; createWalletAddressWithdrawal?: Resolver, ParentType, ContextType, RequireFields>; deleteAsset?: Resolver>; deletePeer?: Resolver>; + deleteTenant?: Resolver>; depositAssetLiquidity?: Resolver, ParentType, ContextType, RequireFields>; depositEventLiquidity?: Resolver, ParentType, ContextType, RequireFields>; depositOutgoingPaymentLiquidity?: Resolver, ParentType, ContextType, RequireFields>; @@ -2213,6 +2357,7 @@ export type MutationResolvers>; updateIncomingPayment?: Resolver>; updatePeer?: Resolver>; + updateTenant?: Resolver>; updateWalletAddress?: Resolver>; voidLiquidityWithdrawal?: Resolver, ParentType, ContextType, RequireFields>; withdrawEventLiquidity?: Resolver, ParentType, ContextType, RequireFields>; @@ -2325,10 +2470,13 @@ export type QueryResolvers>; quote?: Resolver, ParentType, ContextType, RequireFields>; receiver?: Resolver, ParentType, ContextType, RequireFields>; + tenant?: Resolver, ParentType, ContextType, RequireFields>; + tenants?: Resolver>; walletAddress?: Resolver, ParentType, ContextType, RequireFields>; walletAddressByUrl?: Resolver, ParentType, ContextType, RequireFields>; walletAddresses?: Resolver>; webhookEvents?: Resolver>; + whoami?: Resolver; }; export type QuoteResolvers = { @@ -2383,6 +2531,35 @@ export type SetFeeResponseResolvers; }; +export type TenantResolvers = { + apiSecret?: Resolver; + createdAt?: Resolver; + deletedAt?: Resolver, ParentType, ContextType>; + email?: Resolver; + id?: Resolver; + idpConsentUrl?: Resolver; + idpSecret?: Resolver; + publicName?: Resolver, ParentType, ContextType>; + __isTypeOf?: IsTypeOfResolverFn; +}; + +export type TenantEdgeResolvers = { + cursor?: Resolver; + node?: Resolver; + __isTypeOf?: IsTypeOfResolverFn; +}; + +export type TenantMutationResponseResolvers = { + tenant?: Resolver; + __isTypeOf?: IsTypeOfResolverFn; +}; + +export type TenantsConnectionResolvers = { + edges?: Resolver, ParentType, ContextType>; + pageInfo?: Resolver; + __isTypeOf?: IsTypeOfResolverFn; +}; + export type TriggerWalletAddressEventsMutationResponseResolvers = { count?: Resolver, ParentType, ContextType>; __isTypeOf?: IsTypeOfResolverFn; @@ -2487,6 +2664,12 @@ export type WebhookEventsEdgeResolvers; }; +export type WhoamiResponseResolvers = { + id?: Resolver; + isOperator?: Resolver; + __isTypeOf?: IsTypeOfResolverFn; +}; + export type Resolvers = { AccountingTransfer?: AccountingTransferResolvers; AccountingTransferConnection?: AccountingTransferConnectionResolvers; @@ -2506,6 +2689,7 @@ export type Resolvers = { CreateWalletAddressMutationResponse?: CreateWalletAddressMutationResponseResolvers; DeleteAssetMutationResponse?: DeleteAssetMutationResponseResolvers; DeletePeerMutationResponse?: DeletePeerMutationResponseResolvers; + DeleteTenantMutationResponse?: DeleteTenantMutationResponseResolvers; Fee?: FeeResolvers; FeeEdge?: FeeEdgeResolvers; FeesConnection?: FeesConnectionResolvers; @@ -2539,6 +2723,10 @@ export type Resolvers = { Receiver?: ReceiverResolvers; RevokeWalletAddressKeyMutationResponse?: RevokeWalletAddressKeyMutationResponseResolvers; SetFeeResponse?: SetFeeResponseResolvers; + Tenant?: TenantResolvers; + TenantEdge?: TenantEdgeResolvers; + TenantMutationResponse?: TenantMutationResponseResolvers; + TenantsConnection?: TenantsConnectionResolvers; TriggerWalletAddressEventsMutationResponse?: TriggerWalletAddressEventsMutationResponseResolvers; UInt8?: GraphQLScalarType; UInt64?: GraphQLScalarType; @@ -2555,5 +2743,6 @@ export type Resolvers = { WebhookEvent?: WebhookEventResolvers; WebhookEventsConnection?: WebhookEventsConnectionResolvers; WebhookEventsEdge?: WebhookEventsEdgeResolvers; + WhoamiResponse?: WhoamiResponseResolvers; }; diff --git a/packages/backend/src/app.ts b/packages/backend/src/app.ts index 03fbbcf1b6..a68dcedf0f 100644 --- a/packages/backend/src/app.ts +++ b/packages/backend/src/app.ts @@ -393,21 +393,52 @@ export class App { ) let tenantApiSignatureResult: TenantApiSignatureResult - if (this.config.env !== 'test') { - koa.use(async (ctx, next: Koa.Next): Promise => { - const result = await getTenantFromApiSignature(ctx, this.config) - if (!result) { - ctx.throw(401, 'Unauthorized') - } else { + const tenantSignatureMiddleware = async ( + ctx: AppContext, + next: Koa.Next + ): Promise => { + const result = await getTenantFromApiSignature(ctx, this.config) + if (!result) { + ctx.throw(401, 'Unauthorized') + } else { + tenantApiSignatureResult = { + tenant: result.tenant, + isOperator: result.isOperator ? true : false + } + } + return next() + } + + const testTenantSignatureMiddleware = async ( + ctx: AppContext, + next: Koa.Next + ): Promise => { + if (ctx.headers['tenant-id']) { + const tenantService = await ctx.container.use('tenantService') + const tenant = await tenantService.get( + ctx.headers['tenant-id'] as string + ) + + if (tenant) { tenantApiSignatureResult = { - tenant: result.tenant, - isOperator: result.isOperator ? true : false + tenant, + isOperator: tenant.apiSecret === this.config.adminApiSecret } + } else { + ctx.throw(401, 'Unauthorized') } - return next() - }) + } + return next() } + // For tests, we still need to get the tenant in the middleware, but + // we don't need to verify the signature nor prevent replay attacks + koa.use( + this.config.env !== 'test' + ? tenantSignatureMiddleware + : testTenantSignatureMiddleware + ) + koa.use( koaMiddleware(this.apolloServer, { context: async (): Promise => { diff --git a/packages/backend/src/graphql/generated/graphql.schema.json b/packages/backend/src/graphql/generated/graphql.schema.json index 1745966aa2..c745f24e15 100644 --- a/packages/backend/src/graphql/generated/graphql.schema.json +++ b/packages/backend/src/graphql/generated/graphql.schema.json @@ -2105,6 +2105,93 @@ "enumValues": null, "possibleTypes": null }, + { + "kind": "INPUT_OBJECT", + "name": "CreateTenantInput", + "description": null, + "fields": null, + "inputFields": [ + { + "name": "apiSecret", + "description": "Secret used to secure requests made for this tenant.", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + }, + "defaultValue": null, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "email", + "description": "Contact email of the tenant owner.", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + }, + "defaultValue": null, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "idpConsentUrl", + "description": "URL of the tenant's identity provider's consent screen.", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + }, + "defaultValue": null, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "idpSecret", + "description": "Secret used to secure requests from the tenant's identity provider.", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + }, + "defaultValue": null, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "publicName", + "description": "Public name for the tenant.", + "type": { + "kind": "SCALAR", + "name": "String", + "ofType": null + }, + "defaultValue": null, + "isDeprecated": false, + "deprecationReason": null + } + ], + "interfaces": null, + "enumValues": null, + "possibleTypes": null + }, { "kind": "INPUT_OBJECT", "name": "CreateWalletAddressInput", @@ -2513,6 +2600,33 @@ "enumValues": null, "possibleTypes": null }, + { + "kind": "OBJECT", + "name": "DeleteTenantMutationResponse", + "description": null, + "fields": [ + { + "name": "success", + "description": null, + "args": [], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "Boolean", + "ofType": null + } + }, + "isDeprecated": false, + "deprecationReason": null + } + ], + "inputFields": null, + "interfaces": [], + "enumValues": null, + "possibleTypes": null + }, { "kind": "INPUT_OBJECT", "name": "DepositAssetLiquidityInput", @@ -3988,6 +4102,11 @@ "name": "Peer", "ofType": null }, + { + "kind": "OBJECT", + "name": "Tenant", + "ofType": null + }, { "kind": "OBJECT", "name": "WalletAddress", @@ -4489,6 +4608,39 @@ "isDeprecated": false, "deprecationReason": null }, + { + "name": "createTenant", + "description": "Create a tenant.", + "args": [ + { + "name": "input", + "description": null, + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "INPUT_OBJECT", + "name": "CreateTenantInput", + "ofType": null + } + }, + "defaultValue": null, + "isDeprecated": false, + "deprecationReason": null + } + ], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "OBJECT", + "name": "TenantMutationResponse", + "ofType": null + } + }, + "isDeprecated": false, + "deprecationReason": null + }, { "name": "createWalletAddress", "description": "Create a new wallet address.", @@ -4646,6 +4798,39 @@ "isDeprecated": false, "deprecationReason": null }, + { + "name": "deleteTenant", + "description": "Delete a tenant.", + "args": [ + { + "name": "id", + "description": null, + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + }, + "defaultValue": null, + "isDeprecated": false, + "deprecationReason": null + } + ], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "OBJECT", + "name": "DeleteTenantMutationResponse", + "ofType": null + } + }, + "isDeprecated": false, + "deprecationReason": null + }, { "name": "depositAssetLiquidity", "description": "Deposit asset liquidity.", @@ -4985,6 +5170,39 @@ "isDeprecated": false, "deprecationReason": null }, + { + "name": "updateTenant", + "description": "Update a tenant.", + "args": [ + { + "name": "input", + "description": null, + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "INPUT_OBJECT", + "name": "UpdateTenantInput", + "ofType": null + } + }, + "defaultValue": null, + "isDeprecated": false, + "deprecationReason": null + } + ], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "OBJECT", + "name": "TenantMutationResponse", + "ofType": null + } + }, + "isDeprecated": false, + "deprecationReason": null + }, { "name": "updateWalletAddress", "description": "Update an existing wallet address.", @@ -6811,41 +7029,12 @@ "deprecationReason": null }, { - "name": "walletAddress", - "description": "Fetch a wallet address by its ID.", + "name": "tenant", + "description": "Retrieve a tenant of the instance.", "args": [ { "name": "id", - "description": "Unique identifier of the wallet address.", - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "SCALAR", - "name": "String", - "ofType": null - } - }, - "defaultValue": null, - "isDeprecated": false, - "deprecationReason": null - } - ], - "type": { - "kind": "OBJECT", - "name": "WalletAddress", - "ofType": null - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "walletAddressByUrl", - "description": "Get a wallet address by its url if it exists", - "args": [ - { - "name": "url", - "description": "Wallet Address URL.", + "description": "Unique identifier of the tenant.", "type": { "kind": "NON_NULL", "name": null, @@ -6862,19 +7051,19 @@ ], "type": { "kind": "OBJECT", - "name": "WalletAddress", + "name": "Tenant", "ofType": null }, "isDeprecated": false, "deprecationReason": null }, { - "name": "walletAddresses", - "description": "Fetch a paginated list of wallet addresses.", + "name": "tenants", + "description": "Fetch a paginated list of tenants on the instance.", "args": [ { "name": "after", - "description": "Forward pagination: Cursor (wallet address ID) to start retrieving wallet addresses after this point.", + "description": "Forward pagination: Cursor (tenant ID) to start retrieving tenants after this point.", "type": { "kind": "SCALAR", "name": "String", @@ -6886,7 +7075,7 @@ }, { "name": "before", - "description": "Backward pagination: Cursor (wallet address ID) to start retrieving wallet addresses before this point.", + "description": "Backward pagination: Cursor (tenant ID) to start retrieving tenants before this point.", "type": { "kind": "SCALAR", "name": "String", @@ -6898,7 +7087,7 @@ }, { "name": "first", - "description": "Forward pagination: Limit the result to the first **n** wallet addresses after the `after` cursor.", + "description": "Forward pagination: Limit the result to the first **n** tenants after the `after` cursor.", "type": { "kind": "SCALAR", "name": "Int", @@ -6910,7 +7099,7 @@ }, { "name": "last", - "description": "Backward pagination: Limit the result to the last **n** wallet addresses before the `before` cursor.", + "description": "Backward pagination: Limit the result to the last **n** tenants before the `before` cursor.", "type": { "kind": "SCALAR", "name": "Int", @@ -6922,7 +7111,7 @@ }, { "name": "sortOrder", - "description": "Specify the sort order of wallet addresses based on their creation date, either ascending or descending.", + "description": "Specify the sort order of tenants based on their creation date, either ascending or descending.", "type": { "kind": "ENUM", "name": "SortOrder", @@ -6938,7 +7127,7 @@ "name": null, "ofType": { "kind": "OBJECT", - "name": "WalletAddressesConnection", + "name": "TenantsConnection", "ofType": null } }, @@ -6946,22 +7135,157 @@ "deprecationReason": null }, { - "name": "webhookEvents", - "description": "Fetch a paginated list of webhook events.", + "name": "walletAddress", + "description": "Fetch a wallet address by its ID.", "args": [ { - "name": "after", - "description": "Forward pagination: Cursor (webhook event ID) to start retrieving webhook events after this point.", + "name": "id", + "description": "Unique identifier of the wallet address.", "type": { - "kind": "SCALAR", - "name": "String", - "ofType": null + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } }, "defaultValue": null, "isDeprecated": false, "deprecationReason": null - }, - { + } + ], + "type": { + "kind": "OBJECT", + "name": "WalletAddress", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "walletAddressByUrl", + "description": "Get a wallet address by its url if it exists", + "args": [ + { + "name": "url", + "description": "Wallet Address URL.", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + }, + "defaultValue": null, + "isDeprecated": false, + "deprecationReason": null + } + ], + "type": { + "kind": "OBJECT", + "name": "WalletAddress", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "walletAddresses", + "description": "Fetch a paginated list of wallet addresses.", + "args": [ + { + "name": "after", + "description": "Forward pagination: Cursor (wallet address ID) to start retrieving wallet addresses after this point.", + "type": { + "kind": "SCALAR", + "name": "String", + "ofType": null + }, + "defaultValue": null, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "before", + "description": "Backward pagination: Cursor (wallet address ID) to start retrieving wallet addresses 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** wallet addresses 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** wallet addresses 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 wallet addresses based on their creation date, either ascending or descending.", + "type": { + "kind": "ENUM", + "name": "SortOrder", + "ofType": null + }, + "defaultValue": null, + "isDeprecated": false, + "deprecationReason": null + } + ], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "OBJECT", + "name": "WalletAddressesConnection", + "ofType": null + } + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "webhookEvents", + "description": "Fetch a paginated list of webhook events.", + "args": [ + { + "name": "after", + "description": "Forward pagination: Cursor (webhook event ID) to start retrieving webhook events after this point.", + "type": { + "kind": "SCALAR", + "name": "String", + "ofType": null + }, + "defaultValue": null, + "isDeprecated": false, + "deprecationReason": null + }, + { "name": "before", "description": "Backward pagination: Cursor (webhook event ID) to start retrieving webhook events before this point.", "type": { @@ -7033,6 +7357,22 @@ }, "isDeprecated": false, "deprecationReason": null + }, + { + "name": "whoami", + "description": "Determine if the requester has operator permissions", + "args": [], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "OBJECT", + "name": "WhoamiResponse", + "ofType": null + } + }, + "isDeprecated": false, + "deprecationReason": null } ], "inputFields": null, @@ -7624,6 +7964,264 @@ "enumValues": null, "possibleTypes": null }, + { + "kind": "OBJECT", + "name": "Tenant", + "description": null, + "fields": [ + { + "name": "apiSecret", + "description": "Secret used to secure requests made for this tenant.", + "args": [], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "createdAt", + "description": "The date and time that this tenant was created.", + "args": [], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "deletedAt", + "description": "The date and time that this tenant was deleted.", + "args": [], + "type": { + "kind": "SCALAR", + "name": "String", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "email", + "description": "Contact email of the tenant owner.", + "args": [], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "id", + "description": "Unique identifier of the tenant.", + "args": [], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "ID", + "ofType": null + } + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "idpConsentUrl", + "description": "URL of the tenant's identity provider's consent screen.", + "args": [], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "idpSecret", + "description": "Secret used to secure requests from the tenant's identity provider.", + "args": [], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "publicName", + "description": "Public name for the tenant.", + "args": [], + "type": { + "kind": "SCALAR", + "name": "String", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + } + ], + "inputFields": null, + "interfaces": [ + { + "kind": "INTERFACE", + "name": "Model", + "ofType": null + } + ], + "enumValues": null, + "possibleTypes": null + }, + { + "kind": "OBJECT", + "name": "TenantEdge", + "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 node in the list.", + "args": [], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "OBJECT", + "name": "Tenant", + "ofType": null + } + }, + "isDeprecated": false, + "deprecationReason": null + } + ], + "inputFields": null, + "interfaces": [], + "enumValues": null, + "possibleTypes": null + }, + { + "kind": "OBJECT", + "name": "TenantMutationResponse", + "description": null, + "fields": [ + { + "name": "tenant", + "description": null, + "args": [], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "OBJECT", + "name": "Tenant", + "ofType": null + } + }, + "isDeprecated": false, + "deprecationReason": null + } + ], + "inputFields": null, + "interfaces": [], + "enumValues": null, + "possibleTypes": null + }, + { + "kind": "OBJECT", + "name": "TenantsConnection", + "description": null, + "fields": [ + { + "name": "edges", + "description": "A list of edges representing tenants 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": "TenantEdge", + "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": "ENUM", "name": "TransferState", @@ -7992,6 +8590,93 @@ "enumValues": null, "possibleTypes": null }, + { + "kind": "INPUT_OBJECT", + "name": "UpdateTenantInput", + "description": null, + "fields": null, + "inputFields": [ + { + "name": "apiSecret", + "description": "Secret used to secure requests made for this tenant.", + "type": { + "kind": "SCALAR", + "name": "String", + "ofType": null + }, + "defaultValue": null, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "email", + "description": "Contact email of the tenant owner.", + "type": { + "kind": "SCALAR", + "name": "String", + "ofType": null + }, + "defaultValue": null, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "id", + "description": "Unique identifier of the tenant.", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "ID", + "ofType": null + } + }, + "defaultValue": null, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "idpConsentUrl", + "description": "URL of the tenant's identity provider's consent screen.", + "type": { + "kind": "SCALAR", + "name": "String", + "ofType": null + }, + "defaultValue": null, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "idpSecret", + "description": "Secret used to secure requests from the tenant's identity provider.", + "type": { + "kind": "SCALAR", + "name": "String", + "ofType": null + }, + "defaultValue": null, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "publicName", + "description": "Public name for the tenant.", + "type": { + "kind": "SCALAR", + "name": "String", + "ofType": null + }, + "defaultValue": null, + "isDeprecated": false, + "deprecationReason": null + } + ], + "interfaces": null, + "enumValues": null, + "possibleTypes": null + }, { "kind": "INPUT_OBJECT", "name": "UpdateWalletAddressInput", @@ -9158,6 +9843,49 @@ "enumValues": null, "possibleTypes": null }, + { + "kind": "OBJECT", + "name": "WhoamiResponse", + "description": null, + "fields": [ + { + "name": "id", + "description": null, + "args": [], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "isOperator", + "description": null, + "args": [], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "Boolean", + "ofType": null + } + }, + "isDeprecated": false, + "deprecationReason": null + } + ], + "inputFields": null, + "interfaces": [], + "enumValues": null, + "possibleTypes": null + }, { "kind": "INPUT_OBJECT", "name": "WithdrawEventLiquidityInput", diff --git a/packages/backend/src/graphql/generated/graphql.ts b/packages/backend/src/graphql/generated/graphql.ts index 46dad2dedb..0d37f19a34 100644 --- a/packages/backend/src/graphql/generated/graphql.ts +++ b/packages/backend/src/graphql/generated/graphql.ts @@ -364,6 +364,19 @@ export type CreateReceiverResponse = { receiver?: Maybe; }; +export type CreateTenantInput = { + /** Secret used to secure requests made for this tenant. */ + apiSecret: Scalars['String']['input']; + /** Contact email of the tenant owner. */ + email: Scalars['String']['input']; + /** URL of the tenant's identity provider's consent screen. */ + idpConsentUrl: Scalars['String']['input']; + /** Secret used to secure requests from the tenant's identity provider. */ + idpSecret: Scalars['String']['input']; + /** Public name for the tenant. */ + publicName?: InputMaybe; +}; + export type CreateWalletAddressInput = { /** Additional properties associated with the wallet address. */ additionalProperties?: InputMaybe>; @@ -440,6 +453,11 @@ export type DeletePeerMutationResponse = { success: Scalars['Boolean']['output']; }; +export type DeleteTenantMutationResponse = { + __typename?: 'DeleteTenantMutationResponse'; + success: Scalars['Boolean']['output']; +}; + export type DepositAssetLiquidityInput = { /** Amount of liquidity to deposit. */ amount: Scalars['UInt64']['input']; @@ -721,6 +739,8 @@ export type Mutation = { createQuote: QuoteResponse; /** Create an internal or external Open Payments incoming payment. The receiver has a wallet address on either this or another Open Payments resource server. */ createReceiver: CreateReceiverResponse; + /** Create a tenant. */ + createTenant: TenantMutationResponse; /** Create a new wallet address. */ createWalletAddress: CreateWalletAddressMutationResponse; /** Add a public key to a wallet address that is used to verify Open Payments requests. */ @@ -731,6 +751,8 @@ export type Mutation = { deleteAsset: DeleteAssetMutationResponse; /** Delete a peer. */ deletePeer: DeletePeerMutationResponse; + /** Delete a tenant. */ + deleteTenant: DeleteTenantMutationResponse; /** Deposit asset liquidity. */ depositAssetLiquidity?: Maybe; /** @@ -756,6 +778,8 @@ export type Mutation = { updateIncomingPayment: IncomingPaymentResponse; /** Update an existing peer. */ updatePeer: UpdatePeerMutationResponse; + /** Update a tenant. */ + updateTenant: TenantMutationResponse; /** Update an existing wallet address. */ updateWalletAddress: UpdateWalletAddressMutationResponse; /** Void liquidity withdrawal. Withdrawals are two-phase commits and are rolled back via this mutation. */ @@ -843,6 +867,11 @@ export type MutationCreateReceiverArgs = { }; +export type MutationCreateTenantArgs = { + input: CreateTenantInput; +}; + + export type MutationCreateWalletAddressArgs = { input: CreateWalletAddressInput; }; @@ -868,6 +897,11 @@ export type MutationDeletePeerArgs = { }; +export type MutationDeleteTenantArgs = { + id: Scalars['String']['input']; +}; + + export type MutationDepositAssetLiquidityArgs = { input: DepositAssetLiquidityInput; }; @@ -923,6 +957,11 @@ export type MutationUpdatePeerArgs = { }; +export type MutationUpdateTenantArgs = { + input: UpdateTenantInput; +}; + + export type MutationUpdateWalletAddressArgs = { input: UpdateWalletAddressInput; }; @@ -1150,6 +1189,10 @@ export type Query = { quote?: Maybe; /** Retrieve an Open Payments incoming payment by receiver ID. The receiver's wallet address can be hosted on this server or a remote Open Payments resource server. */ receiver?: Maybe; + /** Retrieve a tenant of the instance. */ + tenant?: Maybe; + /** Fetch a paginated list of tenants on the instance. */ + tenants: TenantsConnection; /** Fetch a wallet address by its ID. */ walletAddress?: Maybe; /** Get a wallet address by its url if it exists */ @@ -1158,6 +1201,8 @@ export type Query = { walletAddresses: WalletAddressesConnection; /** Fetch a paginated list of webhook events. */ webhookEvents: WebhookEventsConnection; + /** Determine if the requester has operator permissions */ + whoami: WhoamiResponse; }; @@ -1247,6 +1292,20 @@ export type QueryReceiverArgs = { }; +export type QueryTenantArgs = { + id: Scalars['String']['input']; +}; + + +export type QueryTenantsArgs = { + after?: InputMaybe; + before?: InputMaybe; + first?: InputMaybe; + last?: InputMaybe; + sortOrder?: InputMaybe; +}; + + export type QueryWalletAddressArgs = { id: Scalars['String']['input']; }; @@ -1376,6 +1435,47 @@ export enum SortOrder { Desc = 'DESC' } +export type Tenant = Model & { + __typename?: 'Tenant'; + /** Secret used to secure requests made for this tenant. */ + apiSecret: Scalars['String']['output']; + /** The date and time that this tenant was created. */ + createdAt: Scalars['String']['output']; + /** The date and time that this tenant was deleted. */ + deletedAt?: Maybe; + /** Contact email of the tenant owner. */ + email: Scalars['String']['output']; + /** Unique identifier of the tenant. */ + id: Scalars['ID']['output']; + /** URL of the tenant's identity provider's consent screen. */ + idpConsentUrl: Scalars['String']['output']; + /** Secret used to secure requests from the tenant's identity provider. */ + idpSecret: Scalars['String']['output']; + /** Public name for the tenant. */ + publicName?: Maybe; +}; + +export type TenantEdge = { + __typename?: 'TenantEdge'; + /** A cursor for paginating through the tenants. */ + cursor: Scalars['String']['output']; + /** A tenant node in the list. */ + node: Tenant; +}; + +export type TenantMutationResponse = { + __typename?: 'TenantMutationResponse'; + tenant: Tenant; +}; + +export type TenantsConnection = { + __typename?: 'TenantsConnection'; + /** A list of edges representing tenants and cursors for pagination. */ + edges: Array; + /** Information to aid in pagination. */ + pageInfo: PageInfo; +}; + export enum TransferState { /** The accounting transfer is pending */ Pending = 'PENDING', @@ -1448,6 +1548,21 @@ export type UpdatePeerMutationResponse = { peer?: Maybe; }; +export type UpdateTenantInput = { + /** Secret used to secure requests made for this tenant. */ + apiSecret?: InputMaybe; + /** Contact email of the tenant owner. */ + email?: InputMaybe; + /** Unique identifier of the tenant. */ + id: Scalars['ID']['input']; + /** URL of the tenant's identity provider's consent screen. */ + idpConsentUrl?: InputMaybe; + /** Secret used to secure requests from the tenant's identity provider. */ + idpSecret?: InputMaybe; + /** Public name for the tenant. */ + publicName?: InputMaybe; +}; + export type UpdateWalletAddressInput = { /** Additional properties associated with this wallet address. */ additionalProperties?: InputMaybe>; @@ -1640,6 +1755,12 @@ export type WebhookEventsEdge = { node: WebhookEvent; }; +export type WhoamiResponse = { + __typename?: 'WhoamiResponse'; + id: Scalars['String']['output']; + isOperator: Scalars['Boolean']['output']; +}; + export type WithdrawEventLiquidityInput = { /** Unique identifier of the event to withdraw liquidity from. */ eventId: Scalars['String']['input']; @@ -1718,7 +1839,7 @@ export type DirectiveResolverFn> = { BasePayment: ( Partial ) | ( Partial ) | ( Partial ); - Model: ( Partial ) | ( Partial ) | ( Partial ) | ( Partial ) | ( Partial ) | ( Partial ) | ( Partial ) | ( Partial ) | ( Partial ) | ( Partial ); + Model: ( Partial ) | ( Partial ) | ( Partial ) | ( Partial ) | ( Partial ) | ( Partial ) | ( Partial ) | ( Partial ) | ( Partial ) | ( Partial ) | ( Partial ); }; /** Mapping between all available schema types and the resolvers types */ @@ -1756,6 +1877,7 @@ export type ResolversTypes = { CreateQuoteInput: ResolverTypeWrapper>; CreateReceiverInput: ResolverTypeWrapper>; CreateReceiverResponse: ResolverTypeWrapper>; + CreateTenantInput: ResolverTypeWrapper>; CreateWalletAddressInput: ResolverTypeWrapper>; CreateWalletAddressKeyInput: ResolverTypeWrapper>; CreateWalletAddressKeyMutationResponse: ResolverTypeWrapper>; @@ -1766,6 +1888,7 @@ export type ResolversTypes = { DeleteAssetMutationResponse: ResolverTypeWrapper>; DeletePeerInput: ResolverTypeWrapper>; DeletePeerMutationResponse: ResolverTypeWrapper>; + DeleteTenantMutationResponse: ResolverTypeWrapper>; DepositAssetLiquidityInput: ResolverTypeWrapper>; DepositEventLiquidityInput: ResolverTypeWrapper>; DepositOutgoingPaymentLiquidityInput: ResolverTypeWrapper>; @@ -1825,6 +1948,10 @@ export type ResolversTypes = { SetFeeResponse: ResolverTypeWrapper>; SortOrder: ResolverTypeWrapper>; String: ResolverTypeWrapper>; + Tenant: ResolverTypeWrapper>; + TenantEdge: ResolverTypeWrapper>; + TenantMutationResponse: ResolverTypeWrapper>; + TenantsConnection: ResolverTypeWrapper>; TransferState: ResolverTypeWrapper>; TransferType: ResolverTypeWrapper>; TriggerWalletAddressEventsInput: ResolverTypeWrapper>; @@ -1835,6 +1962,7 @@ export type ResolversTypes = { UpdateIncomingPaymentInput: ResolverTypeWrapper>; UpdatePeerInput: ResolverTypeWrapper>; UpdatePeerMutationResponse: ResolverTypeWrapper>; + UpdateTenantInput: ResolverTypeWrapper>; UpdateWalletAddressInput: ResolverTypeWrapper>; UpdateWalletAddressMutationResponse: ResolverTypeWrapper>; VoidLiquidityWithdrawalInput: ResolverTypeWrapper>; @@ -1851,6 +1979,7 @@ export type ResolversTypes = { WebhookEventFilter: ResolverTypeWrapper>; WebhookEventsConnection: ResolverTypeWrapper>; WebhookEventsEdge: ResolverTypeWrapper>; + WhoamiResponse: ResolverTypeWrapper>; WithdrawEventLiquidityInput: ResolverTypeWrapper>; }; @@ -1888,6 +2017,7 @@ export type ResolversParentTypes = { CreateQuoteInput: Partial; CreateReceiverInput: Partial; CreateReceiverResponse: Partial; + CreateTenantInput: Partial; CreateWalletAddressInput: Partial; CreateWalletAddressKeyInput: Partial; CreateWalletAddressKeyMutationResponse: Partial; @@ -1897,6 +2027,7 @@ export type ResolversParentTypes = { DeleteAssetMutationResponse: Partial; DeletePeerInput: Partial; DeletePeerMutationResponse: Partial; + DeleteTenantMutationResponse: Partial; DepositAssetLiquidityInput: Partial; DepositEventLiquidityInput: Partial; DepositOutgoingPaymentLiquidityInput: Partial; @@ -1949,6 +2080,10 @@ export type ResolversParentTypes = { SetFeeInput: Partial; SetFeeResponse: Partial; String: Partial; + Tenant: Partial; + TenantEdge: Partial; + TenantMutationResponse: Partial; + TenantsConnection: Partial; TriggerWalletAddressEventsInput: Partial; TriggerWalletAddressEventsMutationResponse: Partial; UInt8: Partial; @@ -1957,6 +2092,7 @@ export type ResolversParentTypes = { UpdateIncomingPaymentInput: Partial; UpdatePeerInput: Partial; UpdatePeerMutationResponse: Partial; + UpdateTenantInput: Partial; UpdateWalletAddressInput: Partial; UpdateWalletAddressMutationResponse: Partial; VoidLiquidityWithdrawalInput: Partial; @@ -1972,6 +2108,7 @@ export type ResolversParentTypes = { WebhookEventFilter: Partial; WebhookEventsConnection: Partial; WebhookEventsEdge: Partial; + WhoamiResponse: Partial; WithdrawEventLiquidityInput: Partial; }; @@ -2093,6 +2230,11 @@ export type DeletePeerMutationResponseResolvers; }; +export type DeleteTenantMutationResponseResolvers = { + success?: Resolver; + __isTypeOf?: IsTypeOfResolverFn; +}; + export type FeeResolvers = { assetId?: Resolver; basisPoints?: Resolver; @@ -2176,7 +2318,7 @@ export type LiquidityMutationResponseResolvers = { - __resolveType: TypeResolveFn<'AccountingTransfer' | 'Asset' | 'Fee' | 'IncomingPayment' | 'OutgoingPayment' | 'Payment' | 'Peer' | 'WalletAddress' | 'WalletAddressKey' | 'WebhookEvent', ParentType, ContextType>; + __resolveType: TypeResolveFn<'AccountingTransfer' | 'Asset' | 'Fee' | 'IncomingPayment' | 'OutgoingPayment' | 'Payment' | 'Peer' | 'Tenant' | 'WalletAddress' | 'WalletAddressKey' | 'WebhookEvent', ParentType, ContextType>; createdAt?: Resolver; id?: Resolver; }; @@ -2197,11 +2339,13 @@ export type MutationResolvers, ParentType, ContextType, RequireFields>; createQuote?: Resolver>; createReceiver?: Resolver>; + createTenant?: Resolver>; createWalletAddress?: Resolver>; createWalletAddressKey?: Resolver, ParentType, ContextType, RequireFields>; createWalletAddressWithdrawal?: Resolver, ParentType, ContextType, RequireFields>; deleteAsset?: Resolver>; deletePeer?: Resolver>; + deleteTenant?: Resolver>; depositAssetLiquidity?: Resolver, ParentType, ContextType, RequireFields>; depositEventLiquidity?: Resolver, ParentType, ContextType, RequireFields>; depositOutgoingPaymentLiquidity?: Resolver, ParentType, ContextType, RequireFields>; @@ -2213,6 +2357,7 @@ export type MutationResolvers>; updateIncomingPayment?: Resolver>; updatePeer?: Resolver>; + updateTenant?: Resolver>; updateWalletAddress?: Resolver>; voidLiquidityWithdrawal?: Resolver, ParentType, ContextType, RequireFields>; withdrawEventLiquidity?: Resolver, ParentType, ContextType, RequireFields>; @@ -2325,10 +2470,13 @@ export type QueryResolvers>; quote?: Resolver, ParentType, ContextType, RequireFields>; receiver?: Resolver, ParentType, ContextType, RequireFields>; + tenant?: Resolver, ParentType, ContextType, RequireFields>; + tenants?: Resolver>; walletAddress?: Resolver, ParentType, ContextType, RequireFields>; walletAddressByUrl?: Resolver, ParentType, ContextType, RequireFields>; walletAddresses?: Resolver>; webhookEvents?: Resolver>; + whoami?: Resolver; }; export type QuoteResolvers = { @@ -2383,6 +2531,35 @@ export type SetFeeResponseResolvers; }; +export type TenantResolvers = { + apiSecret?: Resolver; + createdAt?: Resolver; + deletedAt?: Resolver, ParentType, ContextType>; + email?: Resolver; + id?: Resolver; + idpConsentUrl?: Resolver; + idpSecret?: Resolver; + publicName?: Resolver, ParentType, ContextType>; + __isTypeOf?: IsTypeOfResolverFn; +}; + +export type TenantEdgeResolvers = { + cursor?: Resolver; + node?: Resolver; + __isTypeOf?: IsTypeOfResolverFn; +}; + +export type TenantMutationResponseResolvers = { + tenant?: Resolver; + __isTypeOf?: IsTypeOfResolverFn; +}; + +export type TenantsConnectionResolvers = { + edges?: Resolver, ParentType, ContextType>; + pageInfo?: Resolver; + __isTypeOf?: IsTypeOfResolverFn; +}; + export type TriggerWalletAddressEventsMutationResponseResolvers = { count?: Resolver, ParentType, ContextType>; __isTypeOf?: IsTypeOfResolverFn; @@ -2487,6 +2664,12 @@ export type WebhookEventsEdgeResolvers; }; +export type WhoamiResponseResolvers = { + id?: Resolver; + isOperator?: Resolver; + __isTypeOf?: IsTypeOfResolverFn; +}; + export type Resolvers = { AccountingTransfer?: AccountingTransferResolvers; AccountingTransferConnection?: AccountingTransferConnectionResolvers; @@ -2506,6 +2689,7 @@ export type Resolvers = { CreateWalletAddressMutationResponse?: CreateWalletAddressMutationResponseResolvers; DeleteAssetMutationResponse?: DeleteAssetMutationResponseResolvers; DeletePeerMutationResponse?: DeletePeerMutationResponseResolvers; + DeleteTenantMutationResponse?: DeleteTenantMutationResponseResolvers; Fee?: FeeResolvers; FeeEdge?: FeeEdgeResolvers; FeesConnection?: FeesConnectionResolvers; @@ -2539,6 +2723,10 @@ export type Resolvers = { Receiver?: ReceiverResolvers; RevokeWalletAddressKeyMutationResponse?: RevokeWalletAddressKeyMutationResponseResolvers; SetFeeResponse?: SetFeeResponseResolvers; + Tenant?: TenantResolvers; + TenantEdge?: TenantEdgeResolvers; + TenantMutationResponse?: TenantMutationResponseResolvers; + TenantsConnection?: TenantsConnectionResolvers; TriggerWalletAddressEventsMutationResponse?: TriggerWalletAddressEventsMutationResponseResolvers; UInt8?: GraphQLScalarType; UInt64?: GraphQLScalarType; @@ -2555,5 +2743,6 @@ export type Resolvers = { WebhookEvent?: WebhookEventResolvers; WebhookEventsConnection?: WebhookEventsConnectionResolvers; WebhookEventsEdge?: WebhookEventsEdgeResolvers; + WhoamiResponse?: WhoamiResponseResolvers; }; diff --git a/packages/backend/src/graphql/resolvers/index.ts b/packages/backend/src/graphql/resolvers/index.ts index 2c191b30e8..343c07a475 100644 --- a/packages/backend/src/graphql/resolvers/index.ts +++ b/packages/backend/src/graphql/resolvers/index.ts @@ -77,6 +77,14 @@ import { GraphQLJSONObject } from 'graphql-scalars' import { getCombinedPayments } from './combined_payments' import { createOrUpdatePeerByUrl } from './auto-peering' import { getAccountingTransfers } from './accounting_transfer' +import { + whoami, + createTenant, + updateTenant, + deleteTenant, + getTenant, + getTenants +} from './tenant' export const resolvers: Resolvers = { UInt8: GraphQLUInt8, @@ -92,6 +100,7 @@ export const resolvers: Resolvers = { liquidity: getPeerLiquidity }, Query: { + whoami, walletAddress: getWalletAddress, walletAddressByUrl: getWalletAddressByUrl, walletAddresses: getWalletAddresses, @@ -108,7 +117,9 @@ export const resolvers: Resolvers = { webhookEvents: getWebhookEvents, payments: getCombinedPayments, accountingTransfers: getAccountingTransfers, - receiver: getReceiver + receiver: getReceiver, + tenant: getTenant, + tenants: getTenants }, WalletAddress: { liquidity: getWalletAddressLiquidity, @@ -161,6 +172,9 @@ export const resolvers: Resolvers = { createIncomingPaymentWithdrawal, createOutgoingPaymentWithdrawal, setFee, - updateIncomingPayment + updateIncomingPayment, + createTenant, + updateTenant, + deleteTenant } } diff --git a/packages/backend/src/graphql/resolvers/tenant.test.ts b/packages/backend/src/graphql/resolvers/tenant.test.ts new file mode 100644 index 0000000000..4882d8b7bd --- /dev/null +++ b/packages/backend/src/graphql/resolvers/tenant.test.ts @@ -0,0 +1,508 @@ +import { IocContract } from '@adonisjs/fold' +import { AppServices } from '../../app' +import { createTestApp, TestContainer } from '../../tests/app' +import { + DeleteTenantMutationResponse, + Tenant, + TenantMutationResponse, + TenantsConnection, + WhoamiResponse +} from '../generated/graphql' +import { initIocContainer } from '../..' +import { Config, IAppConfig } from '../../config/app' +import { createTenant, generateTenantInput } from '../../tests/tenant' +import { ApolloError, gql, NormalizedCacheObject } from '@apollo/client' +import { getPageTests } from './page.test' +import { truncateTables } from '../../tests/tableManager' +import nock from 'nock' +import { + createHttpLink, + ApolloLink, + ApolloClient, + InMemoryCache +} from '@apollo/client' +import { setContext } from '@apollo/client/link/context' +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 + let config: IAppConfig + + beforeAll(async (): Promise => { + deps = await initIocContainer(Config) + appContainer = await createTestApp(deps) + config = await deps.use('config') + }) + + afterEach(async (): Promise => { + await truncateTables(appContainer.knex) + }) + afterAll(async (): Promise => { + await appContainer.apolloClient.stop() + await appContainer.shutdown() + }) + + describe('whoami', (): void => { + test.each` + isOperator | description + ${true} | ${'operator'} + ${false} | ${'tenant'} + `('whoami query as $description', async ({ isOperator }): Promise => { + const tenant = await createTenant(deps) + const client = isOperator + ? appContainer.apolloClient + : createTenantedApolloClient(appContainer, tenant.id) + + const result = await client + .query({ + query: gql` + query Whoami { + whoami { + id + isOperator + } + } + ` + }) + .then((query): WhoamiResponse => query.data?.whoami) + + expect(result).toEqual({ + id: isOperator ? config.operatorTenantId : tenant.id, + isOperator, + __typename: 'WhoamiResponse' + }) + }) + }) + + describe('Query.tenant', (): void => { + describe('page tests', (): void => { + getPageTests({ + getClient: () => appContainer.apolloClient, + createModel: () => createTenant(deps), + pagedQuery: 'tenants' + }) + + test('Cannot get page as non-operator', async (): Promise => { + const tenant = await createTenant(deps) + const apolloClient = createTenantedApolloClient(appContainer, tenant.id) + try { + await apolloClient + .query({ + query: gql` + query GetTenants { + tenants { + edges { + node { + id + } + } + } + } + ` + }) + .then((query): TenantsConnection => query.data?.tenants) + } catch (error) { + expect(error).toBeInstanceOf(ApolloError) + expect((error as ApolloError).graphQLErrors).toContainEqual( + expect.objectContaining({ + message: 'cannot get tenants page', + extensions: expect.objectContaining({ + code: GraphQLErrorCode.Forbidden + }) + }) + ) + } + }) + }) + + test('can get tenant as operator', async (): Promise => { + const tenant = await createTenant(deps) + + const query = await appContainer.apolloClient + .query({ + query: gql` + query Tenant($id: String!) { + tenant(id: $id) { + id + email + } + } + `, + variables: { + id: tenant.id + } + }) + .then((query): Tenant => query.data?.tenant) + + expect(query).toEqual({ + id: tenant.id, + email: tenant.email, + __typename: 'Tenant' + }) + }) + + test('can get own tenant', async (): Promise => { + const tenant = await createTenant(deps) + const apolloClient = createTenantedApolloClient(appContainer, tenant.id) + + const query = await apolloClient + .query({ + query: gql` + query Tenant($id: String!) { + tenant(id: $id) { + id + email + } + } + `, + variables: { + id: tenant.id + } + }) + .then((query): Tenant => query.data?.tenant) + + expect(query).toEqual({ + id: tenant.id, + email: tenant.email, + __typename: 'Tenant' + }) + }) + + test('cannot get other tenant as non-operator', async (): Promise => { + const firstTenant = await createTenant(deps) + const secondTenant = await createTenant(deps) + + const apolloClient = createTenantedApolloClient( + appContainer, + firstTenant.id + ) + + try { + await apolloClient + .query({ + query: gql` + query Tenant($id: String!) { + tenant(id: $id) { + id + email + } + } + `, + variables: { + id: secondTenant.id + } + }) + .then((query): Tenant => query.data?.tenant) + } catch (error) { + expect(error).toBeInstanceOf(ApolloError) + expect((error as ApolloError).graphQLErrors).toContainEqual( + expect.objectContaining({ + message: 'tenant does not exist', + extensions: expect.objectContaining({ + code: GraphQLErrorCode.NotFound + }) + }) + ) + } + }) + }) + + describe('Mutations', (): void => { + describe('Create', (): void => { + test('can create a tenant', async (): Promise => { + const input = generateTenantInput() + const scope = nock(config.authAdminApiUrl) + .post('') + .reply(200, { data: { createTenant: { id: 1234 } } }) + + const mutation = await appContainer.apolloClient + .mutate({ + mutation: gql` + mutation CreateTenant($input: CreateTenantInput!) { + createTenant(input: $input) { + tenant { + id + email + apiSecret + idpConsentUrl + idpSecret + publicName + } + } + } + `, + variables: { + input + } + }) + .then((query): TenantMutationResponse => query.data?.createTenant) + + expect(mutation.tenant).toEqual({ + ...input, + id: expect.any(String), + __typename: 'Tenant' + }) + scope.done() + }) + + test('cannot create tenant as non-operator', async (): Promise => { + const input = generateTenantInput() + const tenant = await createTenant(deps) + const apolloClient = createTenantedApolloClient(appContainer, tenant.id) + + try { + await apolloClient + .mutate({ + mutation: gql` + mutation CreateTenant($input: CreateTenantInput!) { + createTenant(input: $input) { + tenant { + id + email + apiSecret + idpConsentUrl + idpSecret + publicName + } + } + } + `, + variables: { + input + } + }) + .then((query): TenantMutationResponse => query.data?.createTenant) + } catch (error) { + expect(error).toBeInstanceOf(ApolloError) + expect((error as ApolloError).graphQLErrors).toContainEqual( + expect.objectContaining({ + message: 'permission denied', + extensions: expect.objectContaining({ + code: GraphQLErrorCode.Forbidden + }) + }) + ) + } + }) + }) + describe('Update', (): void => { + let tenantedApolloClient: ApolloClient + let tenant: TenantModel + beforeEach(async (): Promise => { + tenant = await createTenant(deps) + tenantedApolloClient = createTenantedApolloClient( + appContainer, + tenant.id + ) + }) + + afterEach(async (): Promise => { + await truncateTables(appContainer.knex) + }) + + test.each` + isOperator | description + ${true} | ${'operator'} + ${false} | ${'tenant'} + `( + 'can update a tenant as $description', + async ({ isOperator }): Promise => { + const client = isOperator + ? appContainer.apolloClient + : tenantedApolloClient + const updateInput = { + ...generateTenantInput(), + id: tenant.id + } + + const scope = nock(config.authAdminApiUrl) + .post('') + .reply(200, { data: { updateTenant: { id: tenant.id } } }) + const mutation = await client + .mutate({ + mutation: gql` + mutation UpdateTenant($input: UpdateTenantInput!) { + updateTenant(input: $input) { + tenant { + id + email + apiSecret + idpConsentUrl + idpSecret + publicName + } + } + } + `, + variables: { + input: updateInput + } + }) + .then((query): TenantMutationResponse => query.data?.updateTenant) + + scope.done() + expect(mutation.tenant).toEqual({ + ...updateInput, + __typename: 'Tenant' + }) + } + ) + test('Cannot update other tenant as non-operator', async (): Promise => { + const firstTenant = await createTenant(deps) + const secondTenant = await createTenant(deps) + + const updateInput = { + ...generateTenantInput(), + id: secondTenant.id + } + const client = createTenantedApolloClient(appContainer, firstTenant.id) + try { + await client + .mutate({ + mutation: gql` + mutation UpdateTenant($input: UpdateTenantInput!) { + updateTenant(input: $input) { + tenant { + id + email + apiSecret + idpConsentUrl + idpSecret + publicName + } + } + } + `, + variables: { + input: updateInput + } + }) + .then((query): TenantMutationResponse => query.data?.updateTenant) + } catch (error) { + expect(error).toBeInstanceOf(ApolloError) + expect((error as ApolloError).graphQLErrors).toContainEqual( + expect.objectContaining({ + message: 'tenant does not exist', + extensions: expect.objectContaining({ + code: GraphQLErrorCode.NotFound + }) + }) + ) + } + }) + }) + + describe('Delete', (): void => { + test.each` + isOperator | description + ${true} | ${'operator'} + ${false} | ${'tenant'} + `( + 'Can delete a tenant as $description', + async ({ isOperator }): Promise => { + const tenant = await createTenant(deps) + + const client = isOperator + ? appContainer.apolloClient + : createTenantedApolloClient(appContainer, tenant.id) + const scope = nock(config.authAdminApiUrl) + .post('') + .reply(200, { data: { deleteTenant: { id: tenant.id } } }) + const mutation = await client + .mutate({ + mutation: gql` + mutation DeleteTenant($id: String!) { + deleteTenant(id: $id) { + success + } + } + `, + variables: { + id: tenant.id + } + }) + .then( + (query): DeleteTenantMutationResponse => query.data?.deleteTenant + ) + + scope.done() + expect(mutation.success).toBe(true) + } + ) + + test('Cannot delete other tenant as non-operator', async (): Promise => { + const firstTenant = await createTenant(deps) + const secondTenant = await createTenant(deps) + + const client = createTenantedApolloClient(appContainer, secondTenant.id) + + try { + await client + .mutate({ + mutation: gql` + mutation DeleteTenant($id: String!) { + deleteTenant(id: $id) { + success + } + } + `, + variables: { + id: firstTenant.id + } + }) + .then( + (query): DeleteTenantMutationResponse => query.data?.deleteTenant + ) + } catch (error) { + expect(error).toBeInstanceOf(ApolloError) + expect((error as ApolloError).graphQLErrors).toContainEqual( + expect.objectContaining({ + message: 'tenant does not exist', + extensions: expect.objectContaining({ + code: GraphQLErrorCode.NotFound + }) + }) + ) + } + }) + }) + }) +}) diff --git a/packages/backend/src/graphql/resolvers/tenant.ts b/packages/backend/src/graphql/resolvers/tenant.ts new file mode 100644 index 0000000000..6d27940a4e --- /dev/null +++ b/packages/backend/src/graphql/resolvers/tenant.ts @@ -0,0 +1,179 @@ +import { GraphQLError } from 'graphql' +import { TenantedApolloContext } from '../../app' +import { + MutationResolvers, + QueryResolvers, + ResolversTypes, + Tenant as SchemaTenant +} from '../generated/graphql' +import { GraphQLErrorCode } from '../errors' +import { Tenant } from '../../tenants/model' +import { Pagination, SortOrder } from '../../shared/baseModel' +import { getPageInfo } from '../../shared/pagination' + +export const whoami: QueryResolvers['whoami'] = async ( + parent, + args, + ctx +): Promise => { + const { tenant, isOperator } = ctx + + return { + id: tenant.id, + isOperator + } +} + +export const getTenant: QueryResolvers['tenant'] = + async (parent, args, ctx): Promise => { + const { tenant: contextTenant, isOperator } = ctx + + // TODO: make this a util + // If the tenant that was authorized in the request is not the tenant being requested, + // or the requester is not the operator, return not found + if (args.id !== contextTenant.id && !isOperator) { + throw new GraphQLError('tenant does not exist', { + extensions: { + code: GraphQLErrorCode.NotFound + } + }) + } + + const tenantService = await ctx.container.use('tenantService') + const tenant = await tenantService.get(args.id) + if (!tenant) { + throw new GraphQLError('tenant does not exist', { + extensions: { + code: GraphQLErrorCode.NotFound + } + }) + } + + return tenantToGraphQl(tenant) + } + +export const getTenants: QueryResolvers['tenants'] = + async (parent, args, ctx): Promise => { + const { isOperator } = ctx + if (!isOperator) { + throw new GraphQLError('cannot get tenants page', { + extensions: { + code: GraphQLErrorCode.Forbidden + } + }) + } + + const { sortOrder, ...pagination } = args + const order = sortOrder === 'ASC' ? SortOrder.Asc : SortOrder.Desc + const tenantService = await ctx.container.use('tenantService') + + const tenants = await tenantService.getPage(pagination, order) + + const pageInfo = await getPageInfo({ + getPage: (pagination: Pagination, sortOrder?: SortOrder) => + tenantService.getPage(pagination, sortOrder), + page: tenants, + sortOrder: order + }) + return { + pageInfo, + edges: tenants.map((tenant: Tenant) => ({ + cursor: tenant.id, + node: tenantToGraphQl(tenant) + })) + } + } + +export const createTenant: MutationResolvers['createTenant'] = + async ( + parent, + args, + ctx + ): Promise => { + // createTenant is an operator-only resolver + const { isOperator } = ctx + if (!isOperator) { + throw new GraphQLError('permission denied', { + extensions: { + code: GraphQLErrorCode.Forbidden + } + }) + } + + const tenantService = await ctx.container.use('tenantService') + const tenant = await tenantService.create(args.input) + + return { tenant: tenantToGraphQl(tenant) } + } + +export const updateTenant: MutationResolvers['updateTenant'] = + async ( + parent, + args, + ctx + ): Promise => { + const { tenant: contextTenant, isOperator } = ctx + // TODO: make this a util + if (args.input.id !== contextTenant.id && !isOperator) { + throw new GraphQLError('tenant does not exist', { + extensions: { + code: GraphQLErrorCode.NotFound + } + }) + } + + const tenantService = await ctx.container.use('tenantService') + try { + const updatedTenant = await tenantService.update(args.input) + return { tenant: tenantToGraphQl(updatedTenant) } + } catch (err) { + throw new GraphQLError('failed to update tenant', { + extensions: { + code: GraphQLErrorCode.NotFound + } + }) + } + } + +export const deleteTenant: MutationResolvers['deleteTenant'] = + async ( + parent, + args, + ctx + ): Promise => { + const { tenant: contextTenant, isOperator } = ctx + if (args.id !== contextTenant.id && !isOperator) { + throw new GraphQLError('tenant does not exist', { + extensions: { + code: GraphQLErrorCode.NotFound + } + }) + } + + const tenantService = await ctx.container.use('tenantService') + try { + await tenantService.delete(args.id) + return { success: true } + } catch (err) { + throw new GraphQLError('failed to delete tenant', { + extensions: { + code: GraphQLErrorCode.NotFound + } + }) + } + } + +export function tenantToGraphQl(tenant: Tenant): SchemaTenant { + return { + id: tenant.id, + email: tenant.email, + apiSecret: tenant.apiSecret, + idpConsentUrl: tenant.idpConsentUrl, + idpSecret: tenant.idpSecret, + publicName: tenant.publicName, + createdAt: new Date(+tenant.createdAt).toISOString(), + deletedAt: tenant.deletedAt + ? new Date(+tenant.deletedAt).toISOString() + : null + } +} diff --git a/packages/backend/src/graphql/schema.graphql b/packages/backend/src/graphql/schema.graphql index f8286e14d4..958c9e9626 100644 --- a/packages/backend/src/graphql/schema.graphql +++ b/packages/backend/src/graphql/schema.graphql @@ -148,6 +148,26 @@ type Query { "Unique identifier of the receiver (incoming payment URL)." id: String! ): Receiver + + "Retrieve a tenant of the instance." + tenant("Unique identifier of the tenant." id: String!): Tenant + + "Fetch a paginated list of tenants on the instance." + tenants( + "Forward pagination: Cursor (tenant ID) to start retrieving tenants after this point." + after: String + "Backward pagination: Cursor (tenant ID) to start retrieving tenants before this point." + before: String + "Forward pagination: Limit the result to the first **n** tenants after the `after` cursor." + first: Int + "Backward pagination: Limit the result to the last **n** tenants before the `before` cursor." + last: Int + "Specify the sort order of tenants based on their creation date, either ascending or descending." + sortOrder: SortOrder + ): TenantsConnection! + + "Determine if the requester has operator permissions" + whoami: WhoamiResponse! } type Mutation { @@ -306,6 +326,15 @@ type Mutation { cancelIncomingPayment( input: CancelIncomingPaymentInput! ): CancelIncomingPaymentResponse! + + "Create a tenant." + createTenant(input: CreateTenantInput!): TenantMutationResponse! + + "Update a tenant." + updateTenant(input: UpdateTenantInput!): TenantMutationResponse! + + "Delete a tenant." + deleteTenant(id: String!): DeleteTenantMutationResponse! } type PageInfo { @@ -319,6 +348,11 @@ type PageInfo { startCursor: String } +type WhoamiResponse { + id: String! + isOperator: Boolean! +} + type AssetsConnection { "Information to aid in pagination." pageInfo: PageInfo! @@ -1493,6 +1527,75 @@ type CancelIncomingPaymentResponse { payment: IncomingPayment } +type Tenant implements Model { + "Unique identifier of the tenant." + id: ID! + "Contact email of the tenant owner." + email: String! + "Secret used to secure requests made for this tenant." + apiSecret: String! + "URL of the tenant's identity provider's consent screen." + idpConsentUrl: String! + "Secret used to secure requests from the tenant's identity provider." + idpSecret: String! + "Public name for the tenant." + publicName: String + "The date and time that this tenant was created." + createdAt: String! + "The date and time that this tenant was deleted." + deletedAt: String +} + +type TenantsConnection { + "Information to aid in pagination." + pageInfo: PageInfo! + "A list of edges representing tenants and cursors for pagination." + edges: [TenantEdge!]! +} + +type TenantEdge { + "A tenant node in the list." + node: Tenant! + "A cursor for paginating through the tenants." + cursor: String! +} + +input CreateTenantInput { + "Contact email of the tenant owner." + email: String! + "Secret used to secure requests made for this tenant." + apiSecret: String! + "URL of the tenant's identity provider's consent screen." + idpConsentUrl: String! + "Secret used to secure requests from the tenant's identity provider." + idpSecret: String! + "Public name for the tenant." + publicName: String +} + +input UpdateTenantInput { + "Unique identifier of the tenant." + id: ID! + "Contact email of the tenant owner." + email: String + "Secret used to secure requests made for this tenant." + apiSecret: String + "URL of the tenant's identity provider's consent screen." + idpConsentUrl: String + "Secret used to secure requests from the tenant's identity provider." + idpSecret: String + "Public name for the tenant." + publicName: String +} + +type TenantMutationResponse { + tenant: Tenant! +} + +type DeleteTenantMutationResponse { + success: Boolean! +} + """ The `UInt8` scalar type represents unsigned 8-bit whole numeric values, ranging from 0 to 255. """ diff --git a/packages/backend/src/tests/app.ts b/packages/backend/src/tests/app.ts index cbe82b4704..a407b2c182 100644 --- a/packages/backend/src/tests/app.ts +++ b/packages/backend/src/tests/app.ts @@ -14,6 +14,7 @@ import { start, gracefulShutdown } from '..' import { onError } from '@apollo/client/link/error' import { App, AppServices } from '../app' +import { Config } from '../config/app' export const testAccessToken = 'test-app-access' @@ -79,7 +80,8 @@ export const createTestApp = async ( const authLink = setContext((_, { headers }) => { return { headers: { - ...headers + ...headers, + 'tenant-id': Config.operatorTenantId } } }) diff --git a/packages/backend/src/tests/tenant.ts b/packages/backend/src/tests/tenant.ts index f174a58f2f..35521ff53a 100644 --- a/packages/backend/src/tests/tenant.ts +++ b/packages/backend/src/tests/tenant.ts @@ -12,6 +12,16 @@ interface CreateOptions { idpSecret: string } +export function generateTenantInput() { + return { + email: faker.internet.email(), + apiSecret: faker.string.alphanumeric(8), + idpConsentUrl: faker.internet.url(), + idpSecret: faker.string.alphanumeric(8), + publicName: faker.company.name() + } +} + export async function createTenant( deps: IocContract, options?: CreateOptions diff --git a/packages/frontend/app/generated/graphql.ts b/packages/frontend/app/generated/graphql.ts index 1116595860..d9a9050ed1 100644 --- a/packages/frontend/app/generated/graphql.ts +++ b/packages/frontend/app/generated/graphql.ts @@ -364,6 +364,19 @@ export type CreateReceiverResponse = { receiver?: Maybe; }; +export type CreateTenantInput = { + /** Secret used to secure requests made for this tenant. */ + apiSecret: Scalars['String']['input']; + /** Contact email of the tenant owner. */ + email: Scalars['String']['input']; + /** URL of the tenant's identity provider's consent screen. */ + idpConsentUrl: Scalars['String']['input']; + /** Secret used to secure requests from the tenant's identity provider. */ + idpSecret: Scalars['String']['input']; + /** Public name for the tenant. */ + publicName?: InputMaybe; +}; + export type CreateWalletAddressInput = { /** Additional properties associated with the wallet address. */ additionalProperties?: InputMaybe>; @@ -440,6 +453,11 @@ export type DeletePeerMutationResponse = { success: Scalars['Boolean']['output']; }; +export type DeleteTenantMutationResponse = { + __typename?: 'DeleteTenantMutationResponse'; + success: Scalars['Boolean']['output']; +}; + export type DepositAssetLiquidityInput = { /** Amount of liquidity to deposit. */ amount: Scalars['UInt64']['input']; @@ -721,6 +739,8 @@ export type Mutation = { createQuote: QuoteResponse; /** Create an internal or external Open Payments incoming payment. The receiver has a wallet address on either this or another Open Payments resource server. */ createReceiver: CreateReceiverResponse; + /** Create a tenant. */ + createTenant: TenantMutationResponse; /** Create a new wallet address. */ createWalletAddress: CreateWalletAddressMutationResponse; /** Add a public key to a wallet address that is used to verify Open Payments requests. */ @@ -731,6 +751,8 @@ export type Mutation = { deleteAsset: DeleteAssetMutationResponse; /** Delete a peer. */ deletePeer: DeletePeerMutationResponse; + /** Delete a tenant. */ + deleteTenant: DeleteTenantMutationResponse; /** Deposit asset liquidity. */ depositAssetLiquidity?: Maybe; /** @@ -756,6 +778,8 @@ export type Mutation = { updateIncomingPayment: IncomingPaymentResponse; /** Update an existing peer. */ updatePeer: UpdatePeerMutationResponse; + /** Update a tenant. */ + updateTenant: TenantMutationResponse; /** Update an existing wallet address. */ updateWalletAddress: UpdateWalletAddressMutationResponse; /** Void liquidity withdrawal. Withdrawals are two-phase commits and are rolled back via this mutation. */ @@ -843,6 +867,11 @@ export type MutationCreateReceiverArgs = { }; +export type MutationCreateTenantArgs = { + input: CreateTenantInput; +}; + + export type MutationCreateWalletAddressArgs = { input: CreateWalletAddressInput; }; @@ -868,6 +897,11 @@ export type MutationDeletePeerArgs = { }; +export type MutationDeleteTenantArgs = { + id: Scalars['String']['input']; +}; + + export type MutationDepositAssetLiquidityArgs = { input: DepositAssetLiquidityInput; }; @@ -923,6 +957,11 @@ export type MutationUpdatePeerArgs = { }; +export type MutationUpdateTenantArgs = { + input: UpdateTenantInput; +}; + + export type MutationUpdateWalletAddressArgs = { input: UpdateWalletAddressInput; }; @@ -1150,6 +1189,10 @@ export type Query = { quote?: Maybe; /** Retrieve an Open Payments incoming payment by receiver ID. The receiver's wallet address can be hosted on this server or a remote Open Payments resource server. */ receiver?: Maybe; + /** Retrieve a tenant of the instance. */ + tenant?: Maybe; + /** Fetch a paginated list of tenants on the instance. */ + tenants: TenantsConnection; /** Fetch a wallet address by its ID. */ walletAddress?: Maybe; /** Get a wallet address by its url if it exists */ @@ -1158,6 +1201,8 @@ export type Query = { walletAddresses: WalletAddressesConnection; /** Fetch a paginated list of webhook events. */ webhookEvents: WebhookEventsConnection; + /** Determine if the requester has operator permissions */ + whoami: WhoamiResponse; }; @@ -1247,6 +1292,20 @@ export type QueryReceiverArgs = { }; +export type QueryTenantArgs = { + id: Scalars['String']['input']; +}; + + +export type QueryTenantsArgs = { + after?: InputMaybe; + before?: InputMaybe; + first?: InputMaybe; + last?: InputMaybe; + sortOrder?: InputMaybe; +}; + + export type QueryWalletAddressArgs = { id: Scalars['String']['input']; }; @@ -1376,6 +1435,47 @@ export enum SortOrder { Desc = 'DESC' } +export type Tenant = Model & { + __typename?: 'Tenant'; + /** Secret used to secure requests made for this tenant. */ + apiSecret: Scalars['String']['output']; + /** The date and time that this tenant was created. */ + createdAt: Scalars['String']['output']; + /** The date and time that this tenant was deleted. */ + deletedAt?: Maybe; + /** Contact email of the tenant owner. */ + email: Scalars['String']['output']; + /** Unique identifier of the tenant. */ + id: Scalars['ID']['output']; + /** URL of the tenant's identity provider's consent screen. */ + idpConsentUrl: Scalars['String']['output']; + /** Secret used to secure requests from the tenant's identity provider. */ + idpSecret: Scalars['String']['output']; + /** Public name for the tenant. */ + publicName?: Maybe; +}; + +export type TenantEdge = { + __typename?: 'TenantEdge'; + /** A cursor for paginating through the tenants. */ + cursor: Scalars['String']['output']; + /** A tenant node in the list. */ + node: Tenant; +}; + +export type TenantMutationResponse = { + __typename?: 'TenantMutationResponse'; + tenant: Tenant; +}; + +export type TenantsConnection = { + __typename?: 'TenantsConnection'; + /** A list of edges representing tenants and cursors for pagination. */ + edges: Array; + /** Information to aid in pagination. */ + pageInfo: PageInfo; +}; + export enum TransferState { /** The accounting transfer is pending */ Pending = 'PENDING', @@ -1448,6 +1548,21 @@ export type UpdatePeerMutationResponse = { peer?: Maybe; }; +export type UpdateTenantInput = { + /** Secret used to secure requests made for this tenant. */ + apiSecret?: InputMaybe; + /** Contact email of the tenant owner. */ + email?: InputMaybe; + /** Unique identifier of the tenant. */ + id: Scalars['ID']['input']; + /** URL of the tenant's identity provider's consent screen. */ + idpConsentUrl?: InputMaybe; + /** Secret used to secure requests from the tenant's identity provider. */ + idpSecret?: InputMaybe; + /** Public name for the tenant. */ + publicName?: InputMaybe; +}; + export type UpdateWalletAddressInput = { /** Additional properties associated with this wallet address. */ additionalProperties?: InputMaybe>; @@ -1640,6 +1755,12 @@ export type WebhookEventsEdge = { node: WebhookEvent; }; +export type WhoamiResponse = { + __typename?: 'WhoamiResponse'; + id: Scalars['String']['output']; + isOperator: Scalars['Boolean']['output']; +}; + export type WithdrawEventLiquidityInput = { /** Unique identifier of the event to withdraw liquidity from. */ eventId: Scalars['String']['input']; @@ -1718,7 +1839,7 @@ export type DirectiveResolverFn> = { BasePayment: ( Partial ) | ( Partial ) | ( Partial ); - Model: ( Partial ) | ( Partial ) | ( Partial ) | ( Partial ) | ( Partial ) | ( Partial ) | ( Partial ) | ( Partial ) | ( Partial ) | ( Partial ); + Model: ( Partial ) | ( Partial ) | ( Partial ) | ( Partial ) | ( Partial ) | ( Partial ) | ( Partial ) | ( Partial ) | ( Partial ) | ( Partial ) | ( Partial ); }; /** Mapping between all available schema types and the resolvers types */ @@ -1756,6 +1877,7 @@ export type ResolversTypes = { CreateQuoteInput: ResolverTypeWrapper>; CreateReceiverInput: ResolverTypeWrapper>; CreateReceiverResponse: ResolverTypeWrapper>; + CreateTenantInput: ResolverTypeWrapper>; CreateWalletAddressInput: ResolverTypeWrapper>; CreateWalletAddressKeyInput: ResolverTypeWrapper>; CreateWalletAddressKeyMutationResponse: ResolverTypeWrapper>; @@ -1766,6 +1888,7 @@ export type ResolversTypes = { DeleteAssetMutationResponse: ResolverTypeWrapper>; DeletePeerInput: ResolverTypeWrapper>; DeletePeerMutationResponse: ResolverTypeWrapper>; + DeleteTenantMutationResponse: ResolverTypeWrapper>; DepositAssetLiquidityInput: ResolverTypeWrapper>; DepositEventLiquidityInput: ResolverTypeWrapper>; DepositOutgoingPaymentLiquidityInput: ResolverTypeWrapper>; @@ -1825,6 +1948,10 @@ export type ResolversTypes = { SetFeeResponse: ResolverTypeWrapper>; SortOrder: ResolverTypeWrapper>; String: ResolverTypeWrapper>; + Tenant: ResolverTypeWrapper>; + TenantEdge: ResolverTypeWrapper>; + TenantMutationResponse: ResolverTypeWrapper>; + TenantsConnection: ResolverTypeWrapper>; TransferState: ResolverTypeWrapper>; TransferType: ResolverTypeWrapper>; TriggerWalletAddressEventsInput: ResolverTypeWrapper>; @@ -1835,6 +1962,7 @@ export type ResolversTypes = { UpdateIncomingPaymentInput: ResolverTypeWrapper>; UpdatePeerInput: ResolverTypeWrapper>; UpdatePeerMutationResponse: ResolverTypeWrapper>; + UpdateTenantInput: ResolverTypeWrapper>; UpdateWalletAddressInput: ResolverTypeWrapper>; UpdateWalletAddressMutationResponse: ResolverTypeWrapper>; VoidLiquidityWithdrawalInput: ResolverTypeWrapper>; @@ -1851,6 +1979,7 @@ export type ResolversTypes = { WebhookEventFilter: ResolverTypeWrapper>; WebhookEventsConnection: ResolverTypeWrapper>; WebhookEventsEdge: ResolverTypeWrapper>; + WhoamiResponse: ResolverTypeWrapper>; WithdrawEventLiquidityInput: ResolverTypeWrapper>; }; @@ -1888,6 +2017,7 @@ export type ResolversParentTypes = { CreateQuoteInput: Partial; CreateReceiverInput: Partial; CreateReceiverResponse: Partial; + CreateTenantInput: Partial; CreateWalletAddressInput: Partial; CreateWalletAddressKeyInput: Partial; CreateWalletAddressKeyMutationResponse: Partial; @@ -1897,6 +2027,7 @@ export type ResolversParentTypes = { DeleteAssetMutationResponse: Partial; DeletePeerInput: Partial; DeletePeerMutationResponse: Partial; + DeleteTenantMutationResponse: Partial; DepositAssetLiquidityInput: Partial; DepositEventLiquidityInput: Partial; DepositOutgoingPaymentLiquidityInput: Partial; @@ -1949,6 +2080,10 @@ export type ResolversParentTypes = { SetFeeInput: Partial; SetFeeResponse: Partial; String: Partial; + Tenant: Partial; + TenantEdge: Partial; + TenantMutationResponse: Partial; + TenantsConnection: Partial; TriggerWalletAddressEventsInput: Partial; TriggerWalletAddressEventsMutationResponse: Partial; UInt8: Partial; @@ -1957,6 +2092,7 @@ export type ResolversParentTypes = { UpdateIncomingPaymentInput: Partial; UpdatePeerInput: Partial; UpdatePeerMutationResponse: Partial; + UpdateTenantInput: Partial; UpdateWalletAddressInput: Partial; UpdateWalletAddressMutationResponse: Partial; VoidLiquidityWithdrawalInput: Partial; @@ -1972,6 +2108,7 @@ export type ResolversParentTypes = { WebhookEventFilter: Partial; WebhookEventsConnection: Partial; WebhookEventsEdge: Partial; + WhoamiResponse: Partial; WithdrawEventLiquidityInput: Partial; }; @@ -2093,6 +2230,11 @@ export type DeletePeerMutationResponseResolvers; }; +export type DeleteTenantMutationResponseResolvers = { + success?: Resolver; + __isTypeOf?: IsTypeOfResolverFn; +}; + export type FeeResolvers = { assetId?: Resolver; basisPoints?: Resolver; @@ -2176,7 +2318,7 @@ export type LiquidityMutationResponseResolvers = { - __resolveType: TypeResolveFn<'AccountingTransfer' | 'Asset' | 'Fee' | 'IncomingPayment' | 'OutgoingPayment' | 'Payment' | 'Peer' | 'WalletAddress' | 'WalletAddressKey' | 'WebhookEvent', ParentType, ContextType>; + __resolveType: TypeResolveFn<'AccountingTransfer' | 'Asset' | 'Fee' | 'IncomingPayment' | 'OutgoingPayment' | 'Payment' | 'Peer' | 'Tenant' | 'WalletAddress' | 'WalletAddressKey' | 'WebhookEvent', ParentType, ContextType>; createdAt?: Resolver; id?: Resolver; }; @@ -2197,11 +2339,13 @@ export type MutationResolvers, ParentType, ContextType, RequireFields>; createQuote?: Resolver>; createReceiver?: Resolver>; + createTenant?: Resolver>; createWalletAddress?: Resolver>; createWalletAddressKey?: Resolver, ParentType, ContextType, RequireFields>; createWalletAddressWithdrawal?: Resolver, ParentType, ContextType, RequireFields>; deleteAsset?: Resolver>; deletePeer?: Resolver>; + deleteTenant?: Resolver>; depositAssetLiquidity?: Resolver, ParentType, ContextType, RequireFields>; depositEventLiquidity?: Resolver, ParentType, ContextType, RequireFields>; depositOutgoingPaymentLiquidity?: Resolver, ParentType, ContextType, RequireFields>; @@ -2213,6 +2357,7 @@ export type MutationResolvers>; updateIncomingPayment?: Resolver>; updatePeer?: Resolver>; + updateTenant?: Resolver>; updateWalletAddress?: Resolver>; voidLiquidityWithdrawal?: Resolver, ParentType, ContextType, RequireFields>; withdrawEventLiquidity?: Resolver, ParentType, ContextType, RequireFields>; @@ -2325,10 +2470,13 @@ export type QueryResolvers>; quote?: Resolver, ParentType, ContextType, RequireFields>; receiver?: Resolver, ParentType, ContextType, RequireFields>; + tenant?: Resolver, ParentType, ContextType, RequireFields>; + tenants?: Resolver>; walletAddress?: Resolver, ParentType, ContextType, RequireFields>; walletAddressByUrl?: Resolver, ParentType, ContextType, RequireFields>; walletAddresses?: Resolver>; webhookEvents?: Resolver>; + whoami?: Resolver; }; export type QuoteResolvers = { @@ -2383,6 +2531,35 @@ export type SetFeeResponseResolvers; }; +export type TenantResolvers = { + apiSecret?: Resolver; + createdAt?: Resolver; + deletedAt?: Resolver, ParentType, ContextType>; + email?: Resolver; + id?: Resolver; + idpConsentUrl?: Resolver; + idpSecret?: Resolver; + publicName?: Resolver, ParentType, ContextType>; + __isTypeOf?: IsTypeOfResolverFn; +}; + +export type TenantEdgeResolvers = { + cursor?: Resolver; + node?: Resolver; + __isTypeOf?: IsTypeOfResolverFn; +}; + +export type TenantMutationResponseResolvers = { + tenant?: Resolver; + __isTypeOf?: IsTypeOfResolverFn; +}; + +export type TenantsConnectionResolvers = { + edges?: Resolver, ParentType, ContextType>; + pageInfo?: Resolver; + __isTypeOf?: IsTypeOfResolverFn; +}; + export type TriggerWalletAddressEventsMutationResponseResolvers = { count?: Resolver, ParentType, ContextType>; __isTypeOf?: IsTypeOfResolverFn; @@ -2487,6 +2664,12 @@ export type WebhookEventsEdgeResolvers; }; +export type WhoamiResponseResolvers = { + id?: Resolver; + isOperator?: Resolver; + __isTypeOf?: IsTypeOfResolverFn; +}; + export type Resolvers = { AccountingTransfer?: AccountingTransferResolvers; AccountingTransferConnection?: AccountingTransferConnectionResolvers; @@ -2506,6 +2689,7 @@ export type Resolvers = { CreateWalletAddressMutationResponse?: CreateWalletAddressMutationResponseResolvers; DeleteAssetMutationResponse?: DeleteAssetMutationResponseResolvers; DeletePeerMutationResponse?: DeletePeerMutationResponseResolvers; + DeleteTenantMutationResponse?: DeleteTenantMutationResponseResolvers; Fee?: FeeResolvers; FeeEdge?: FeeEdgeResolvers; FeesConnection?: FeesConnectionResolvers; @@ -2539,6 +2723,10 @@ export type Resolvers = { Receiver?: ReceiverResolvers; RevokeWalletAddressKeyMutationResponse?: RevokeWalletAddressKeyMutationResponseResolvers; SetFeeResponse?: SetFeeResponseResolvers; + Tenant?: TenantResolvers; + TenantEdge?: TenantEdgeResolvers; + TenantMutationResponse?: TenantMutationResponseResolvers; + TenantsConnection?: TenantsConnectionResolvers; TriggerWalletAddressEventsMutationResponse?: TriggerWalletAddressEventsMutationResponseResolvers; UInt8?: GraphQLScalarType; UInt64?: GraphQLScalarType; @@ -2555,6 +2743,7 @@ export type Resolvers = { WebhookEvent?: WebhookEventResolvers; WebhookEventsConnection?: WebhookEventsConnectionResolvers; WebhookEventsEdge?: WebhookEventsEdgeResolvers; + WhoamiResponse?: WhoamiResponseResolvers; }; diff --git a/packages/mock-account-service-lib/src/generated/graphql.ts b/packages/mock-account-service-lib/src/generated/graphql.ts index 46dad2dedb..0d37f19a34 100644 --- a/packages/mock-account-service-lib/src/generated/graphql.ts +++ b/packages/mock-account-service-lib/src/generated/graphql.ts @@ -364,6 +364,19 @@ export type CreateReceiverResponse = { receiver?: Maybe; }; +export type CreateTenantInput = { + /** Secret used to secure requests made for this tenant. */ + apiSecret: Scalars['String']['input']; + /** Contact email of the tenant owner. */ + email: Scalars['String']['input']; + /** URL of the tenant's identity provider's consent screen. */ + idpConsentUrl: Scalars['String']['input']; + /** Secret used to secure requests from the tenant's identity provider. */ + idpSecret: Scalars['String']['input']; + /** Public name for the tenant. */ + publicName?: InputMaybe; +}; + export type CreateWalletAddressInput = { /** Additional properties associated with the wallet address. */ additionalProperties?: InputMaybe>; @@ -440,6 +453,11 @@ export type DeletePeerMutationResponse = { success: Scalars['Boolean']['output']; }; +export type DeleteTenantMutationResponse = { + __typename?: 'DeleteTenantMutationResponse'; + success: Scalars['Boolean']['output']; +}; + export type DepositAssetLiquidityInput = { /** Amount of liquidity to deposit. */ amount: Scalars['UInt64']['input']; @@ -721,6 +739,8 @@ export type Mutation = { createQuote: QuoteResponse; /** Create an internal or external Open Payments incoming payment. The receiver has a wallet address on either this or another Open Payments resource server. */ createReceiver: CreateReceiverResponse; + /** Create a tenant. */ + createTenant: TenantMutationResponse; /** Create a new wallet address. */ createWalletAddress: CreateWalletAddressMutationResponse; /** Add a public key to a wallet address that is used to verify Open Payments requests. */ @@ -731,6 +751,8 @@ export type Mutation = { deleteAsset: DeleteAssetMutationResponse; /** Delete a peer. */ deletePeer: DeletePeerMutationResponse; + /** Delete a tenant. */ + deleteTenant: DeleteTenantMutationResponse; /** Deposit asset liquidity. */ depositAssetLiquidity?: Maybe; /** @@ -756,6 +778,8 @@ export type Mutation = { updateIncomingPayment: IncomingPaymentResponse; /** Update an existing peer. */ updatePeer: UpdatePeerMutationResponse; + /** Update a tenant. */ + updateTenant: TenantMutationResponse; /** Update an existing wallet address. */ updateWalletAddress: UpdateWalletAddressMutationResponse; /** Void liquidity withdrawal. Withdrawals are two-phase commits and are rolled back via this mutation. */ @@ -843,6 +867,11 @@ export type MutationCreateReceiverArgs = { }; +export type MutationCreateTenantArgs = { + input: CreateTenantInput; +}; + + export type MutationCreateWalletAddressArgs = { input: CreateWalletAddressInput; }; @@ -868,6 +897,11 @@ export type MutationDeletePeerArgs = { }; +export type MutationDeleteTenantArgs = { + id: Scalars['String']['input']; +}; + + export type MutationDepositAssetLiquidityArgs = { input: DepositAssetLiquidityInput; }; @@ -923,6 +957,11 @@ export type MutationUpdatePeerArgs = { }; +export type MutationUpdateTenantArgs = { + input: UpdateTenantInput; +}; + + export type MutationUpdateWalletAddressArgs = { input: UpdateWalletAddressInput; }; @@ -1150,6 +1189,10 @@ export type Query = { quote?: Maybe; /** Retrieve an Open Payments incoming payment by receiver ID. The receiver's wallet address can be hosted on this server or a remote Open Payments resource server. */ receiver?: Maybe; + /** Retrieve a tenant of the instance. */ + tenant?: Maybe; + /** Fetch a paginated list of tenants on the instance. */ + tenants: TenantsConnection; /** Fetch a wallet address by its ID. */ walletAddress?: Maybe; /** Get a wallet address by its url if it exists */ @@ -1158,6 +1201,8 @@ export type Query = { walletAddresses: WalletAddressesConnection; /** Fetch a paginated list of webhook events. */ webhookEvents: WebhookEventsConnection; + /** Determine if the requester has operator permissions */ + whoami: WhoamiResponse; }; @@ -1247,6 +1292,20 @@ export type QueryReceiverArgs = { }; +export type QueryTenantArgs = { + id: Scalars['String']['input']; +}; + + +export type QueryTenantsArgs = { + after?: InputMaybe; + before?: InputMaybe; + first?: InputMaybe; + last?: InputMaybe; + sortOrder?: InputMaybe; +}; + + export type QueryWalletAddressArgs = { id: Scalars['String']['input']; }; @@ -1376,6 +1435,47 @@ export enum SortOrder { Desc = 'DESC' } +export type Tenant = Model & { + __typename?: 'Tenant'; + /** Secret used to secure requests made for this tenant. */ + apiSecret: Scalars['String']['output']; + /** The date and time that this tenant was created. */ + createdAt: Scalars['String']['output']; + /** The date and time that this tenant was deleted. */ + deletedAt?: Maybe; + /** Contact email of the tenant owner. */ + email: Scalars['String']['output']; + /** Unique identifier of the tenant. */ + id: Scalars['ID']['output']; + /** URL of the tenant's identity provider's consent screen. */ + idpConsentUrl: Scalars['String']['output']; + /** Secret used to secure requests from the tenant's identity provider. */ + idpSecret: Scalars['String']['output']; + /** Public name for the tenant. */ + publicName?: Maybe; +}; + +export type TenantEdge = { + __typename?: 'TenantEdge'; + /** A cursor for paginating through the tenants. */ + cursor: Scalars['String']['output']; + /** A tenant node in the list. */ + node: Tenant; +}; + +export type TenantMutationResponse = { + __typename?: 'TenantMutationResponse'; + tenant: Tenant; +}; + +export type TenantsConnection = { + __typename?: 'TenantsConnection'; + /** A list of edges representing tenants and cursors for pagination. */ + edges: Array; + /** Information to aid in pagination. */ + pageInfo: PageInfo; +}; + export enum TransferState { /** The accounting transfer is pending */ Pending = 'PENDING', @@ -1448,6 +1548,21 @@ export type UpdatePeerMutationResponse = { peer?: Maybe; }; +export type UpdateTenantInput = { + /** Secret used to secure requests made for this tenant. */ + apiSecret?: InputMaybe; + /** Contact email of the tenant owner. */ + email?: InputMaybe; + /** Unique identifier of the tenant. */ + id: Scalars['ID']['input']; + /** URL of the tenant's identity provider's consent screen. */ + idpConsentUrl?: InputMaybe; + /** Secret used to secure requests from the tenant's identity provider. */ + idpSecret?: InputMaybe; + /** Public name for the tenant. */ + publicName?: InputMaybe; +}; + export type UpdateWalletAddressInput = { /** Additional properties associated with this wallet address. */ additionalProperties?: InputMaybe>; @@ -1640,6 +1755,12 @@ export type WebhookEventsEdge = { node: WebhookEvent; }; +export type WhoamiResponse = { + __typename?: 'WhoamiResponse'; + id: Scalars['String']['output']; + isOperator: Scalars['Boolean']['output']; +}; + export type WithdrawEventLiquidityInput = { /** Unique identifier of the event to withdraw liquidity from. */ eventId: Scalars['String']['input']; @@ -1718,7 +1839,7 @@ export type DirectiveResolverFn> = { BasePayment: ( Partial ) | ( Partial ) | ( Partial ); - Model: ( Partial ) | ( Partial ) | ( Partial ) | ( Partial ) | ( Partial ) | ( Partial ) | ( Partial ) | ( Partial ) | ( Partial ) | ( Partial ); + Model: ( Partial ) | ( Partial ) | ( Partial ) | ( Partial ) | ( Partial ) | ( Partial ) | ( Partial ) | ( Partial ) | ( Partial ) | ( Partial ) | ( Partial ); }; /** Mapping between all available schema types and the resolvers types */ @@ -1756,6 +1877,7 @@ export type ResolversTypes = { CreateQuoteInput: ResolverTypeWrapper>; CreateReceiverInput: ResolverTypeWrapper>; CreateReceiverResponse: ResolverTypeWrapper>; + CreateTenantInput: ResolverTypeWrapper>; CreateWalletAddressInput: ResolverTypeWrapper>; CreateWalletAddressKeyInput: ResolverTypeWrapper>; CreateWalletAddressKeyMutationResponse: ResolverTypeWrapper>; @@ -1766,6 +1888,7 @@ export type ResolversTypes = { DeleteAssetMutationResponse: ResolverTypeWrapper>; DeletePeerInput: ResolverTypeWrapper>; DeletePeerMutationResponse: ResolverTypeWrapper>; + DeleteTenantMutationResponse: ResolverTypeWrapper>; DepositAssetLiquidityInput: ResolverTypeWrapper>; DepositEventLiquidityInput: ResolverTypeWrapper>; DepositOutgoingPaymentLiquidityInput: ResolverTypeWrapper>; @@ -1825,6 +1948,10 @@ export type ResolversTypes = { SetFeeResponse: ResolverTypeWrapper>; SortOrder: ResolverTypeWrapper>; String: ResolverTypeWrapper>; + Tenant: ResolverTypeWrapper>; + TenantEdge: ResolverTypeWrapper>; + TenantMutationResponse: ResolverTypeWrapper>; + TenantsConnection: ResolverTypeWrapper>; TransferState: ResolverTypeWrapper>; TransferType: ResolverTypeWrapper>; TriggerWalletAddressEventsInput: ResolverTypeWrapper>; @@ -1835,6 +1962,7 @@ export type ResolversTypes = { UpdateIncomingPaymentInput: ResolverTypeWrapper>; UpdatePeerInput: ResolverTypeWrapper>; UpdatePeerMutationResponse: ResolverTypeWrapper>; + UpdateTenantInput: ResolverTypeWrapper>; UpdateWalletAddressInput: ResolverTypeWrapper>; UpdateWalletAddressMutationResponse: ResolverTypeWrapper>; VoidLiquidityWithdrawalInput: ResolverTypeWrapper>; @@ -1851,6 +1979,7 @@ export type ResolversTypes = { WebhookEventFilter: ResolverTypeWrapper>; WebhookEventsConnection: ResolverTypeWrapper>; WebhookEventsEdge: ResolverTypeWrapper>; + WhoamiResponse: ResolverTypeWrapper>; WithdrawEventLiquidityInput: ResolverTypeWrapper>; }; @@ -1888,6 +2017,7 @@ export type ResolversParentTypes = { CreateQuoteInput: Partial; CreateReceiverInput: Partial; CreateReceiverResponse: Partial; + CreateTenantInput: Partial; CreateWalletAddressInput: Partial; CreateWalletAddressKeyInput: Partial; CreateWalletAddressKeyMutationResponse: Partial; @@ -1897,6 +2027,7 @@ export type ResolversParentTypes = { DeleteAssetMutationResponse: Partial; DeletePeerInput: Partial; DeletePeerMutationResponse: Partial; + DeleteTenantMutationResponse: Partial; DepositAssetLiquidityInput: Partial; DepositEventLiquidityInput: Partial; DepositOutgoingPaymentLiquidityInput: Partial; @@ -1949,6 +2080,10 @@ export type ResolversParentTypes = { SetFeeInput: Partial; SetFeeResponse: Partial; String: Partial; + Tenant: Partial; + TenantEdge: Partial; + TenantMutationResponse: Partial; + TenantsConnection: Partial; TriggerWalletAddressEventsInput: Partial; TriggerWalletAddressEventsMutationResponse: Partial; UInt8: Partial; @@ -1957,6 +2092,7 @@ export type ResolversParentTypes = { UpdateIncomingPaymentInput: Partial; UpdatePeerInput: Partial; UpdatePeerMutationResponse: Partial; + UpdateTenantInput: Partial; UpdateWalletAddressInput: Partial; UpdateWalletAddressMutationResponse: Partial; VoidLiquidityWithdrawalInput: Partial; @@ -1972,6 +2108,7 @@ export type ResolversParentTypes = { WebhookEventFilter: Partial; WebhookEventsConnection: Partial; WebhookEventsEdge: Partial; + WhoamiResponse: Partial; WithdrawEventLiquidityInput: Partial; }; @@ -2093,6 +2230,11 @@ export type DeletePeerMutationResponseResolvers; }; +export type DeleteTenantMutationResponseResolvers = { + success?: Resolver; + __isTypeOf?: IsTypeOfResolverFn; +}; + export type FeeResolvers = { assetId?: Resolver; basisPoints?: Resolver; @@ -2176,7 +2318,7 @@ export type LiquidityMutationResponseResolvers = { - __resolveType: TypeResolveFn<'AccountingTransfer' | 'Asset' | 'Fee' | 'IncomingPayment' | 'OutgoingPayment' | 'Payment' | 'Peer' | 'WalletAddress' | 'WalletAddressKey' | 'WebhookEvent', ParentType, ContextType>; + __resolveType: TypeResolveFn<'AccountingTransfer' | 'Asset' | 'Fee' | 'IncomingPayment' | 'OutgoingPayment' | 'Payment' | 'Peer' | 'Tenant' | 'WalletAddress' | 'WalletAddressKey' | 'WebhookEvent', ParentType, ContextType>; createdAt?: Resolver; id?: Resolver; }; @@ -2197,11 +2339,13 @@ export type MutationResolvers, ParentType, ContextType, RequireFields>; createQuote?: Resolver>; createReceiver?: Resolver>; + createTenant?: Resolver>; createWalletAddress?: Resolver>; createWalletAddressKey?: Resolver, ParentType, ContextType, RequireFields>; createWalletAddressWithdrawal?: Resolver, ParentType, ContextType, RequireFields>; deleteAsset?: Resolver>; deletePeer?: Resolver>; + deleteTenant?: Resolver>; depositAssetLiquidity?: Resolver, ParentType, ContextType, RequireFields>; depositEventLiquidity?: Resolver, ParentType, ContextType, RequireFields>; depositOutgoingPaymentLiquidity?: Resolver, ParentType, ContextType, RequireFields>; @@ -2213,6 +2357,7 @@ export type MutationResolvers>; updateIncomingPayment?: Resolver>; updatePeer?: Resolver>; + updateTenant?: Resolver>; updateWalletAddress?: Resolver>; voidLiquidityWithdrawal?: Resolver, ParentType, ContextType, RequireFields>; withdrawEventLiquidity?: Resolver, ParentType, ContextType, RequireFields>; @@ -2325,10 +2470,13 @@ export type QueryResolvers>; quote?: Resolver, ParentType, ContextType, RequireFields>; receiver?: Resolver, ParentType, ContextType, RequireFields>; + tenant?: Resolver, ParentType, ContextType, RequireFields>; + tenants?: Resolver>; walletAddress?: Resolver, ParentType, ContextType, RequireFields>; walletAddressByUrl?: Resolver, ParentType, ContextType, RequireFields>; walletAddresses?: Resolver>; webhookEvents?: Resolver>; + whoami?: Resolver; }; export type QuoteResolvers = { @@ -2383,6 +2531,35 @@ export type SetFeeResponseResolvers; }; +export type TenantResolvers = { + apiSecret?: Resolver; + createdAt?: Resolver; + deletedAt?: Resolver, ParentType, ContextType>; + email?: Resolver; + id?: Resolver; + idpConsentUrl?: Resolver; + idpSecret?: Resolver; + publicName?: Resolver, ParentType, ContextType>; + __isTypeOf?: IsTypeOfResolverFn; +}; + +export type TenantEdgeResolvers = { + cursor?: Resolver; + node?: Resolver; + __isTypeOf?: IsTypeOfResolverFn; +}; + +export type TenantMutationResponseResolvers = { + tenant?: Resolver; + __isTypeOf?: IsTypeOfResolverFn; +}; + +export type TenantsConnectionResolvers = { + edges?: Resolver, ParentType, ContextType>; + pageInfo?: Resolver; + __isTypeOf?: IsTypeOfResolverFn; +}; + export type TriggerWalletAddressEventsMutationResponseResolvers = { count?: Resolver, ParentType, ContextType>; __isTypeOf?: IsTypeOfResolverFn; @@ -2487,6 +2664,12 @@ export type WebhookEventsEdgeResolvers; }; +export type WhoamiResponseResolvers = { + id?: Resolver; + isOperator?: Resolver; + __isTypeOf?: IsTypeOfResolverFn; +}; + export type Resolvers = { AccountingTransfer?: AccountingTransferResolvers; AccountingTransferConnection?: AccountingTransferConnectionResolvers; @@ -2506,6 +2689,7 @@ export type Resolvers = { CreateWalletAddressMutationResponse?: CreateWalletAddressMutationResponseResolvers; DeleteAssetMutationResponse?: DeleteAssetMutationResponseResolvers; DeletePeerMutationResponse?: DeletePeerMutationResponseResolvers; + DeleteTenantMutationResponse?: DeleteTenantMutationResponseResolvers; Fee?: FeeResolvers; FeeEdge?: FeeEdgeResolvers; FeesConnection?: FeesConnectionResolvers; @@ -2539,6 +2723,10 @@ export type Resolvers = { Receiver?: ReceiverResolvers; RevokeWalletAddressKeyMutationResponse?: RevokeWalletAddressKeyMutationResponseResolvers; SetFeeResponse?: SetFeeResponseResolvers; + Tenant?: TenantResolvers; + TenantEdge?: TenantEdgeResolvers; + TenantMutationResponse?: TenantMutationResponseResolvers; + TenantsConnection?: TenantsConnectionResolvers; TriggerWalletAddressEventsMutationResponse?: TriggerWalletAddressEventsMutationResponseResolvers; UInt8?: GraphQLScalarType; UInt64?: GraphQLScalarType; @@ -2555,5 +2743,6 @@ export type Resolvers = { WebhookEvent?: WebhookEventResolvers; WebhookEventsConnection?: WebhookEventsConnectionResolvers; WebhookEventsEdge?: WebhookEventsEdgeResolvers; + WhoamiResponse?: WhoamiResponseResolvers; }; diff --git a/test/integration/lib/generated/graphql.ts b/test/integration/lib/generated/graphql.ts index 46dad2dedb..0d37f19a34 100644 --- a/test/integration/lib/generated/graphql.ts +++ b/test/integration/lib/generated/graphql.ts @@ -364,6 +364,19 @@ export type CreateReceiverResponse = { receiver?: Maybe; }; +export type CreateTenantInput = { + /** Secret used to secure requests made for this tenant. */ + apiSecret: Scalars['String']['input']; + /** Contact email of the tenant owner. */ + email: Scalars['String']['input']; + /** URL of the tenant's identity provider's consent screen. */ + idpConsentUrl: Scalars['String']['input']; + /** Secret used to secure requests from the tenant's identity provider. */ + idpSecret: Scalars['String']['input']; + /** Public name for the tenant. */ + publicName?: InputMaybe; +}; + export type CreateWalletAddressInput = { /** Additional properties associated with the wallet address. */ additionalProperties?: InputMaybe>; @@ -440,6 +453,11 @@ export type DeletePeerMutationResponse = { success: Scalars['Boolean']['output']; }; +export type DeleteTenantMutationResponse = { + __typename?: 'DeleteTenantMutationResponse'; + success: Scalars['Boolean']['output']; +}; + export type DepositAssetLiquidityInput = { /** Amount of liquidity to deposit. */ amount: Scalars['UInt64']['input']; @@ -721,6 +739,8 @@ export type Mutation = { createQuote: QuoteResponse; /** Create an internal or external Open Payments incoming payment. The receiver has a wallet address on either this or another Open Payments resource server. */ createReceiver: CreateReceiverResponse; + /** Create a tenant. */ + createTenant: TenantMutationResponse; /** Create a new wallet address. */ createWalletAddress: CreateWalletAddressMutationResponse; /** Add a public key to a wallet address that is used to verify Open Payments requests. */ @@ -731,6 +751,8 @@ export type Mutation = { deleteAsset: DeleteAssetMutationResponse; /** Delete a peer. */ deletePeer: DeletePeerMutationResponse; + /** Delete a tenant. */ + deleteTenant: DeleteTenantMutationResponse; /** Deposit asset liquidity. */ depositAssetLiquidity?: Maybe; /** @@ -756,6 +778,8 @@ export type Mutation = { updateIncomingPayment: IncomingPaymentResponse; /** Update an existing peer. */ updatePeer: UpdatePeerMutationResponse; + /** Update a tenant. */ + updateTenant: TenantMutationResponse; /** Update an existing wallet address. */ updateWalletAddress: UpdateWalletAddressMutationResponse; /** Void liquidity withdrawal. Withdrawals are two-phase commits and are rolled back via this mutation. */ @@ -843,6 +867,11 @@ export type MutationCreateReceiverArgs = { }; +export type MutationCreateTenantArgs = { + input: CreateTenantInput; +}; + + export type MutationCreateWalletAddressArgs = { input: CreateWalletAddressInput; }; @@ -868,6 +897,11 @@ export type MutationDeletePeerArgs = { }; +export type MutationDeleteTenantArgs = { + id: Scalars['String']['input']; +}; + + export type MutationDepositAssetLiquidityArgs = { input: DepositAssetLiquidityInput; }; @@ -923,6 +957,11 @@ export type MutationUpdatePeerArgs = { }; +export type MutationUpdateTenantArgs = { + input: UpdateTenantInput; +}; + + export type MutationUpdateWalletAddressArgs = { input: UpdateWalletAddressInput; }; @@ -1150,6 +1189,10 @@ export type Query = { quote?: Maybe; /** Retrieve an Open Payments incoming payment by receiver ID. The receiver's wallet address can be hosted on this server or a remote Open Payments resource server. */ receiver?: Maybe; + /** Retrieve a tenant of the instance. */ + tenant?: Maybe; + /** Fetch a paginated list of tenants on the instance. */ + tenants: TenantsConnection; /** Fetch a wallet address by its ID. */ walletAddress?: Maybe; /** Get a wallet address by its url if it exists */ @@ -1158,6 +1201,8 @@ export type Query = { walletAddresses: WalletAddressesConnection; /** Fetch a paginated list of webhook events. */ webhookEvents: WebhookEventsConnection; + /** Determine if the requester has operator permissions */ + whoami: WhoamiResponse; }; @@ -1247,6 +1292,20 @@ export type QueryReceiverArgs = { }; +export type QueryTenantArgs = { + id: Scalars['String']['input']; +}; + + +export type QueryTenantsArgs = { + after?: InputMaybe; + before?: InputMaybe; + first?: InputMaybe; + last?: InputMaybe; + sortOrder?: InputMaybe; +}; + + export type QueryWalletAddressArgs = { id: Scalars['String']['input']; }; @@ -1376,6 +1435,47 @@ export enum SortOrder { Desc = 'DESC' } +export type Tenant = Model & { + __typename?: 'Tenant'; + /** Secret used to secure requests made for this tenant. */ + apiSecret: Scalars['String']['output']; + /** The date and time that this tenant was created. */ + createdAt: Scalars['String']['output']; + /** The date and time that this tenant was deleted. */ + deletedAt?: Maybe; + /** Contact email of the tenant owner. */ + email: Scalars['String']['output']; + /** Unique identifier of the tenant. */ + id: Scalars['ID']['output']; + /** URL of the tenant's identity provider's consent screen. */ + idpConsentUrl: Scalars['String']['output']; + /** Secret used to secure requests from the tenant's identity provider. */ + idpSecret: Scalars['String']['output']; + /** Public name for the tenant. */ + publicName?: Maybe; +}; + +export type TenantEdge = { + __typename?: 'TenantEdge'; + /** A cursor for paginating through the tenants. */ + cursor: Scalars['String']['output']; + /** A tenant node in the list. */ + node: Tenant; +}; + +export type TenantMutationResponse = { + __typename?: 'TenantMutationResponse'; + tenant: Tenant; +}; + +export type TenantsConnection = { + __typename?: 'TenantsConnection'; + /** A list of edges representing tenants and cursors for pagination. */ + edges: Array; + /** Information to aid in pagination. */ + pageInfo: PageInfo; +}; + export enum TransferState { /** The accounting transfer is pending */ Pending = 'PENDING', @@ -1448,6 +1548,21 @@ export type UpdatePeerMutationResponse = { peer?: Maybe; }; +export type UpdateTenantInput = { + /** Secret used to secure requests made for this tenant. */ + apiSecret?: InputMaybe; + /** Contact email of the tenant owner. */ + email?: InputMaybe; + /** Unique identifier of the tenant. */ + id: Scalars['ID']['input']; + /** URL of the tenant's identity provider's consent screen. */ + idpConsentUrl?: InputMaybe; + /** Secret used to secure requests from the tenant's identity provider. */ + idpSecret?: InputMaybe; + /** Public name for the tenant. */ + publicName?: InputMaybe; +}; + export type UpdateWalletAddressInput = { /** Additional properties associated with this wallet address. */ additionalProperties?: InputMaybe>; @@ -1640,6 +1755,12 @@ export type WebhookEventsEdge = { node: WebhookEvent; }; +export type WhoamiResponse = { + __typename?: 'WhoamiResponse'; + id: Scalars['String']['output']; + isOperator: Scalars['Boolean']['output']; +}; + export type WithdrawEventLiquidityInput = { /** Unique identifier of the event to withdraw liquidity from. */ eventId: Scalars['String']['input']; @@ -1718,7 +1839,7 @@ export type DirectiveResolverFn> = { BasePayment: ( Partial ) | ( Partial ) | ( Partial ); - Model: ( Partial ) | ( Partial ) | ( Partial ) | ( Partial ) | ( Partial ) | ( Partial ) | ( Partial ) | ( Partial ) | ( Partial ) | ( Partial ); + Model: ( Partial ) | ( Partial ) | ( Partial ) | ( Partial ) | ( Partial ) | ( Partial ) | ( Partial ) | ( Partial ) | ( Partial ) | ( Partial ) | ( Partial ); }; /** Mapping between all available schema types and the resolvers types */ @@ -1756,6 +1877,7 @@ export type ResolversTypes = { CreateQuoteInput: ResolverTypeWrapper>; CreateReceiverInput: ResolverTypeWrapper>; CreateReceiverResponse: ResolverTypeWrapper>; + CreateTenantInput: ResolverTypeWrapper>; CreateWalletAddressInput: ResolverTypeWrapper>; CreateWalletAddressKeyInput: ResolverTypeWrapper>; CreateWalletAddressKeyMutationResponse: ResolverTypeWrapper>; @@ -1766,6 +1888,7 @@ export type ResolversTypes = { DeleteAssetMutationResponse: ResolverTypeWrapper>; DeletePeerInput: ResolverTypeWrapper>; DeletePeerMutationResponse: ResolverTypeWrapper>; + DeleteTenantMutationResponse: ResolverTypeWrapper>; DepositAssetLiquidityInput: ResolverTypeWrapper>; DepositEventLiquidityInput: ResolverTypeWrapper>; DepositOutgoingPaymentLiquidityInput: ResolverTypeWrapper>; @@ -1825,6 +1948,10 @@ export type ResolversTypes = { SetFeeResponse: ResolverTypeWrapper>; SortOrder: ResolverTypeWrapper>; String: ResolverTypeWrapper>; + Tenant: ResolverTypeWrapper>; + TenantEdge: ResolverTypeWrapper>; + TenantMutationResponse: ResolverTypeWrapper>; + TenantsConnection: ResolverTypeWrapper>; TransferState: ResolverTypeWrapper>; TransferType: ResolverTypeWrapper>; TriggerWalletAddressEventsInput: ResolverTypeWrapper>; @@ -1835,6 +1962,7 @@ export type ResolversTypes = { UpdateIncomingPaymentInput: ResolverTypeWrapper>; UpdatePeerInput: ResolverTypeWrapper>; UpdatePeerMutationResponse: ResolverTypeWrapper>; + UpdateTenantInput: ResolverTypeWrapper>; UpdateWalletAddressInput: ResolverTypeWrapper>; UpdateWalletAddressMutationResponse: ResolverTypeWrapper>; VoidLiquidityWithdrawalInput: ResolverTypeWrapper>; @@ -1851,6 +1979,7 @@ export type ResolversTypes = { WebhookEventFilter: ResolverTypeWrapper>; WebhookEventsConnection: ResolverTypeWrapper>; WebhookEventsEdge: ResolverTypeWrapper>; + WhoamiResponse: ResolverTypeWrapper>; WithdrawEventLiquidityInput: ResolverTypeWrapper>; }; @@ -1888,6 +2017,7 @@ export type ResolversParentTypes = { CreateQuoteInput: Partial; CreateReceiverInput: Partial; CreateReceiverResponse: Partial; + CreateTenantInput: Partial; CreateWalletAddressInput: Partial; CreateWalletAddressKeyInput: Partial; CreateWalletAddressKeyMutationResponse: Partial; @@ -1897,6 +2027,7 @@ export type ResolversParentTypes = { DeleteAssetMutationResponse: Partial; DeletePeerInput: Partial; DeletePeerMutationResponse: Partial; + DeleteTenantMutationResponse: Partial; DepositAssetLiquidityInput: Partial; DepositEventLiquidityInput: Partial; DepositOutgoingPaymentLiquidityInput: Partial; @@ -1949,6 +2080,10 @@ export type ResolversParentTypes = { SetFeeInput: Partial; SetFeeResponse: Partial; String: Partial; + Tenant: Partial; + TenantEdge: Partial; + TenantMutationResponse: Partial; + TenantsConnection: Partial; TriggerWalletAddressEventsInput: Partial; TriggerWalletAddressEventsMutationResponse: Partial; UInt8: Partial; @@ -1957,6 +2092,7 @@ export type ResolversParentTypes = { UpdateIncomingPaymentInput: Partial; UpdatePeerInput: Partial; UpdatePeerMutationResponse: Partial; + UpdateTenantInput: Partial; UpdateWalletAddressInput: Partial; UpdateWalletAddressMutationResponse: Partial; VoidLiquidityWithdrawalInput: Partial; @@ -1972,6 +2108,7 @@ export type ResolversParentTypes = { WebhookEventFilter: Partial; WebhookEventsConnection: Partial; WebhookEventsEdge: Partial; + WhoamiResponse: Partial; WithdrawEventLiquidityInput: Partial; }; @@ -2093,6 +2230,11 @@ export type DeletePeerMutationResponseResolvers; }; +export type DeleteTenantMutationResponseResolvers = { + success?: Resolver; + __isTypeOf?: IsTypeOfResolverFn; +}; + export type FeeResolvers = { assetId?: Resolver; basisPoints?: Resolver; @@ -2176,7 +2318,7 @@ export type LiquidityMutationResponseResolvers = { - __resolveType: TypeResolveFn<'AccountingTransfer' | 'Asset' | 'Fee' | 'IncomingPayment' | 'OutgoingPayment' | 'Payment' | 'Peer' | 'WalletAddress' | 'WalletAddressKey' | 'WebhookEvent', ParentType, ContextType>; + __resolveType: TypeResolveFn<'AccountingTransfer' | 'Asset' | 'Fee' | 'IncomingPayment' | 'OutgoingPayment' | 'Payment' | 'Peer' | 'Tenant' | 'WalletAddress' | 'WalletAddressKey' | 'WebhookEvent', ParentType, ContextType>; createdAt?: Resolver; id?: Resolver; }; @@ -2197,11 +2339,13 @@ export type MutationResolvers, ParentType, ContextType, RequireFields>; createQuote?: Resolver>; createReceiver?: Resolver>; + createTenant?: Resolver>; createWalletAddress?: Resolver>; createWalletAddressKey?: Resolver, ParentType, ContextType, RequireFields>; createWalletAddressWithdrawal?: Resolver, ParentType, ContextType, RequireFields>; deleteAsset?: Resolver>; deletePeer?: Resolver>; + deleteTenant?: Resolver>; depositAssetLiquidity?: Resolver, ParentType, ContextType, RequireFields>; depositEventLiquidity?: Resolver, ParentType, ContextType, RequireFields>; depositOutgoingPaymentLiquidity?: Resolver, ParentType, ContextType, RequireFields>; @@ -2213,6 +2357,7 @@ export type MutationResolvers>; updateIncomingPayment?: Resolver>; updatePeer?: Resolver>; + updateTenant?: Resolver>; updateWalletAddress?: Resolver>; voidLiquidityWithdrawal?: Resolver, ParentType, ContextType, RequireFields>; withdrawEventLiquidity?: Resolver, ParentType, ContextType, RequireFields>; @@ -2325,10 +2470,13 @@ export type QueryResolvers>; quote?: Resolver, ParentType, ContextType, RequireFields>; receiver?: Resolver, ParentType, ContextType, RequireFields>; + tenant?: Resolver, ParentType, ContextType, RequireFields>; + tenants?: Resolver>; walletAddress?: Resolver, ParentType, ContextType, RequireFields>; walletAddressByUrl?: Resolver, ParentType, ContextType, RequireFields>; walletAddresses?: Resolver>; webhookEvents?: Resolver>; + whoami?: Resolver; }; export type QuoteResolvers = { @@ -2383,6 +2531,35 @@ export type SetFeeResponseResolvers; }; +export type TenantResolvers = { + apiSecret?: Resolver; + createdAt?: Resolver; + deletedAt?: Resolver, ParentType, ContextType>; + email?: Resolver; + id?: Resolver; + idpConsentUrl?: Resolver; + idpSecret?: Resolver; + publicName?: Resolver, ParentType, ContextType>; + __isTypeOf?: IsTypeOfResolverFn; +}; + +export type TenantEdgeResolvers = { + cursor?: Resolver; + node?: Resolver; + __isTypeOf?: IsTypeOfResolverFn; +}; + +export type TenantMutationResponseResolvers = { + tenant?: Resolver; + __isTypeOf?: IsTypeOfResolverFn; +}; + +export type TenantsConnectionResolvers = { + edges?: Resolver, ParentType, ContextType>; + pageInfo?: Resolver; + __isTypeOf?: IsTypeOfResolverFn; +}; + export type TriggerWalletAddressEventsMutationResponseResolvers = { count?: Resolver, ParentType, ContextType>; __isTypeOf?: IsTypeOfResolverFn; @@ -2487,6 +2664,12 @@ export type WebhookEventsEdgeResolvers; }; +export type WhoamiResponseResolvers = { + id?: Resolver; + isOperator?: Resolver; + __isTypeOf?: IsTypeOfResolverFn; +}; + export type Resolvers = { AccountingTransfer?: AccountingTransferResolvers; AccountingTransferConnection?: AccountingTransferConnectionResolvers; @@ -2506,6 +2689,7 @@ export type Resolvers = { CreateWalletAddressMutationResponse?: CreateWalletAddressMutationResponseResolvers; DeleteAssetMutationResponse?: DeleteAssetMutationResponseResolvers; DeletePeerMutationResponse?: DeletePeerMutationResponseResolvers; + DeleteTenantMutationResponse?: DeleteTenantMutationResponseResolvers; Fee?: FeeResolvers; FeeEdge?: FeeEdgeResolvers; FeesConnection?: FeesConnectionResolvers; @@ -2539,6 +2723,10 @@ export type Resolvers = { Receiver?: ReceiverResolvers; RevokeWalletAddressKeyMutationResponse?: RevokeWalletAddressKeyMutationResponseResolvers; SetFeeResponse?: SetFeeResponseResolvers; + Tenant?: TenantResolvers; + TenantEdge?: TenantEdgeResolvers; + TenantMutationResponse?: TenantMutationResponseResolvers; + TenantsConnection?: TenantsConnectionResolvers; TriggerWalletAddressEventsMutationResponse?: TriggerWalletAddressEventsMutationResponseResolvers; UInt8?: GraphQLScalarType; UInt64?: GraphQLScalarType; @@ -2555,5 +2743,6 @@ export type Resolvers = { WebhookEvent?: WebhookEventResolvers; WebhookEventsConnection?: WebhookEventsConnectionResolvers; WebhookEventsEdge?: WebhookEventsEdgeResolvers; + WhoamiResponse?: WhoamiResponseResolvers; };