From 5ae5f3f6451cfba97451276ef92f43a73e10e5c0 Mon Sep 17 00:00:00 2001 From: Max Kurapov Date: Fri, 4 Nov 2022 11:53:04 +0100 Subject: [PATCH 01/16] feat(open-payments): adding AS types --- .../open-payments/scripts/generate-types.ts | 32 +- .../src/client/ilp-stream-connection.test.ts | 2 +- .../src/client/ilp-stream-connection.ts | 4 +- .../src/client/incoming-payment.test.ts | 2 +- .../src/client/incoming-payment.ts | 4 +- packages/open-payments/src/client/index.ts | 6 +- .../src/client/payment-pointer.test.ts | 2 +- .../src/client/payment-pointer.ts | 4 +- packages/open-payments/src/config.ts | 6 +- .../generated/authorization-server-types.ts | 378 ++++++++++++++++++ .../{types.ts => resource-server-types.ts} | 58 +-- packages/open-payments/src/types.ts | 17 +- packages/open-payments/tsconfig.json | 2 +- 13 files changed, 461 insertions(+), 56 deletions(-) create mode 100644 packages/open-payments/src/generated/authorization-server-types.ts rename packages/open-payments/src/generated/{types.ts => resource-server-types.ts} (91%) diff --git a/packages/open-payments/scripts/generate-types.ts b/packages/open-payments/scripts/generate-types.ts index 27c978a180..632eade117 100644 --- a/packages/open-payments/scripts/generate-types.ts +++ b/packages/open-payments/scripts/generate-types.ts @@ -1,16 +1,32 @@ import fs from 'fs' import openapiTS from 'openapi-typescript' import config from '../src/config' + +const generateTypesFromOpenApi = async ( + specUrl: string, + outputFileName: string +) => { + const generatedTypesOutput = await openapiTS(specUrl) + + fs.writeFile(outputFileName, generatedTypesOutput, (error) => { + if (error) { + console.log(`Error when writing types to ${outputFileName}`, { error }) + } + }) +} + ;(async () => { - try { - const output = await openapiTS(config.OPEN_PAYMENTS_OPEN_API_URL) - const fileName = 'src/generated/types.ts' + const rootFolder = `src/generated` - fs.writeFile(fileName, output, (error) => { - if (error) { - console.log(`Error when writing types to ${fileName}`, { error }) - } - }) + try { + await generateTypesFromOpenApi( + config.OPEN_PAYMENTS_RS_OPEN_API_URL, + `${rootFolder}/resource-server-types.ts` + ) + await generateTypesFromOpenApi( + config.OPEN_PAYMENTS_AS_OPEN_API_URL, + `${rootFolder}/authorization-server-types.ts` + ) } catch (error) { console.log('Error when generating types', { error diff --git a/packages/open-payments/src/client/ilp-stream-connection.test.ts b/packages/open-payments/src/client/ilp-stream-connection.test.ts index 01ae2302bb..699a1799db 100644 --- a/packages/open-payments/src/client/ilp-stream-connection.test.ts +++ b/packages/open-payments/src/client/ilp-stream-connection.test.ts @@ -7,7 +7,7 @@ describe('ilp-stream-connection', (): void => { let openApi: OpenAPI beforeAll(async () => { - openApi = await createOpenAPI(config.OPEN_PAYMENTS_OPEN_API_URL) + openApi = await createOpenAPI(config.OPEN_PAYMENTS_RS_OPEN_API_URL) }) const axiosInstance = defaultAxiosInstance diff --git a/packages/open-payments/src/client/ilp-stream-connection.ts b/packages/open-payments/src/client/ilp-stream-connection.ts index d03fad0011..b12e7e84e2 100644 --- a/packages/open-payments/src/client/ilp-stream-connection.ts +++ b/packages/open-payments/src/client/ilp-stream-connection.ts @@ -1,6 +1,6 @@ import { HttpMethod } from 'openapi' import { ClientDeps } from '.' -import { getPath, ILPStreamConnection } from '../types' +import { getRSPath, ILPStreamConnection } from '../types' import { get } from './requests' interface GetArgs { @@ -18,7 +18,7 @@ export const createILPStreamConnectionRoutes = ( const getILPStreamConnectionValidator = openApi.createResponseValidator({ - path: getPath('/connections/{id}'), + path: getRSPath('/connections/{id}'), method: HttpMethod.GET }) diff --git a/packages/open-payments/src/client/incoming-payment.test.ts b/packages/open-payments/src/client/incoming-payment.test.ts index dca95e2ede..80ee4779d5 100644 --- a/packages/open-payments/src/client/incoming-payment.test.ts +++ b/packages/open-payments/src/client/incoming-payment.test.ts @@ -18,7 +18,7 @@ describe('incoming-payment', (): void => { let openApi: OpenAPI beforeAll(async () => { - openApi = await createOpenAPI(config.OPEN_PAYMENTS_OPEN_API_URL) + openApi = await createOpenAPI(config.OPEN_PAYMENTS_RS_OPEN_API_URL) }) const axiosInstance = defaultAxiosInstance diff --git a/packages/open-payments/src/client/incoming-payment.ts b/packages/open-payments/src/client/incoming-payment.ts index 852cadac35..f0a75564d9 100644 --- a/packages/open-payments/src/client/incoming-payment.ts +++ b/packages/open-payments/src/client/incoming-payment.ts @@ -1,6 +1,6 @@ import { HttpMethod, ResponseValidator } from 'openapi' import { ClientDeps } from '.' -import { IncomingPayment, getPath } from '../types' +import { IncomingPayment, getRSPath } from '../types' import { get } from './requests' interface GetArgs { @@ -19,7 +19,7 @@ export const createIncomingPaymentRoutes = ( const getIncomingPaymentOpenApiValidator = openApi.createResponseValidator({ - path: getPath('/incoming-payments/{id}'), + path: getRSPath('/incoming-payments/{id}'), method: HttpMethod.GET }) diff --git a/packages/open-payments/src/client/index.ts b/packages/open-payments/src/client/index.ts index 3a51c063c1..5ef0bc044a 100644 --- a/packages/open-payments/src/client/index.ts +++ b/packages/open-payments/src/client/index.ts @@ -40,9 +40,11 @@ export const createClient = async ( requestTimeoutMs: args?.requestTimeoutMs ?? config.DEFAULT_REQUEST_TIMEOUT_MS }) - const openApi = await createOpenAPI(config.OPEN_PAYMENTS_OPEN_API_URL) + const resourceServerOpenApi = await createOpenAPI( + config.OPEN_PAYMENTS_RS_OPEN_API_URL + ) const logger = args?.logger ?? createLogger() - const deps = { axiosInstance, openApi, logger } + const deps = { axiosInstance, openApi: resourceServerOpenApi, logger } return { incomingPayment: createIncomingPaymentRoutes(deps), diff --git a/packages/open-payments/src/client/payment-pointer.test.ts b/packages/open-payments/src/client/payment-pointer.test.ts index 2915f8bab8..f003f69da4 100644 --- a/packages/open-payments/src/client/payment-pointer.test.ts +++ b/packages/open-payments/src/client/payment-pointer.test.ts @@ -7,7 +7,7 @@ describe('payment-pointer', (): void => { let openApi: OpenAPI beforeAll(async () => { - openApi = await createOpenAPI(config.OPEN_PAYMENTS_OPEN_API_URL) + openApi = await createOpenAPI(config.OPEN_PAYMENTS_RS_OPEN_API_URL) }) const axiosInstance = defaultAxiosInstance diff --git a/packages/open-payments/src/client/payment-pointer.ts b/packages/open-payments/src/client/payment-pointer.ts index d436cf9574..d7ddd20346 100644 --- a/packages/open-payments/src/client/payment-pointer.ts +++ b/packages/open-payments/src/client/payment-pointer.ts @@ -1,6 +1,6 @@ import { HttpMethod } from 'openapi' import { ClientDeps } from '.' -import { PaymentPointer, getPath } from '../types' +import { PaymentPointer, getRSPath } from '../types' import { get } from './requests' interface GetArgs { @@ -18,7 +18,7 @@ export const createPaymentPointerRoutes = ( const getPaymentPaymentValidator = openApi.createResponseValidator({ - path: getPath('/'), + path: getRSPath('/'), method: HttpMethod.GET }) diff --git a/packages/open-payments/src/config.ts b/packages/open-payments/src/config.ts index d3c58e27b9..0a3931c035 100644 --- a/packages/open-payments/src/config.ts +++ b/packages/open-payments/src/config.ts @@ -1,5 +1,7 @@ export default { - OPEN_PAYMENTS_OPEN_API_URL: - 'https://raw.githubusercontent.com/interledger/open-payments/7bb2e6a03d7dfe7ecb0553afb6c70741317bb489/openapi/RS/openapi.yaml', + OPEN_PAYMENTS_RS_OPEN_API_URL: + 'https://raw.githubusercontent.com/interledger/open-payments/1e3e118d8b22c5d2942f972e28ebf0f0114d04f5/openapi/resource-server.yaml', + OPEN_PAYMENTS_AS_OPEN_API_URL: + 'https://raw.githubusercontent.com/interledger/open-payments/1e3e118d8b22c5d2942f972e28ebf0f0114d04f5/openapi/auth-server.yaml', DEFAULT_REQUEST_TIMEOUT_MS: 5_000 } diff --git a/packages/open-payments/src/generated/authorization-server-types.ts b/packages/open-payments/src/generated/authorization-server-types.ts new file mode 100644 index 0000000000..7f258e56c8 --- /dev/null +++ b/packages/open-payments/src/generated/authorization-server-types.ts @@ -0,0 +1,378 @@ +/** + * This file was auto-generated by openapi-typescript. + * Do not make direct changes to the file. + */ + +export interface paths { + "/": { + /** Make a new grant request */ + post: operations["post-request"]; + parameters: {}; + }; + "/continue/{id}": { + /** Continue a grant request during or after user interaction. */ + post: operations["post-continue"]; + /** Cancel a grant request or delete a grant client side. */ + delete: operations["delete-continue"]; + parameters: { + path: { + id: string; + }; + }; + }; + "/token/{id}": { + /** Management endpoint to rotate access token. */ + post: operations["post-token"]; + /** Management endpoint to revoke access token. */ + delete: operations["delete-token"]; + parameters: { + path: { + id: string; + }; + }; + }; +} + +export interface components { + schemas: { + /** + * client + * @description Describes the client instance that is making this request, including the key that the client instance will use to protect this request and any continuation requests at the AS and any user-facing information about the client instance used in interactions. + * + * When sending a non-continuation request to the AS, the client instance MUST identify itself by including the client field of the request and by signing the request. + */ + client: { + /** @description An object containing additional information that the AS MAY display to the RO during interaction, authorization, and management. */ + display?: { + name?: string; + /** Format: uri */ + uri?: string; + }; + /** @description An identifier string that the AS can use to identify the client software comprising this client instance. */ + client_id?: string; + /** @description The public key of the client instance to be used in this request or a reference to a key. */ + key?: + | external["https://raw.githubusercontent.com/interledger/open-payments/1e3e118d8b22c5d2942f972e28ebf0f0114d04f5/openapi/schemas.yaml"]["components"]["schemas"]["key"] + | string; + }; + /** + * interact + * @description The client instance declares the parameters for interaction methods that it can support using the interact field. + */ + "interact-request": { + /** @description Indicates how the client instance can start an interaction. */ + start: "redirect"[]; + /** @description Indicates how the client instance can receive an indication that interaction has finished at the AS. */ + finish?: { + /** @description The callback method that the AS will use to contact the client instance. */ + method: "redirect"; + /** + * Format: uri + * @description Indicates the URI that the AS will either send the RO to after interaction or send an HTTP POST request. + */ + uri: string; + /** @description Unique value to be used in the calculation of the "hash" query parameter sent to the callback URI, must be sufficiently random to be unguessable by an attacker. MUST be generated by the client instance as a unique value for this request. */ + nonce: string; + }; + }; + /** interact-response */ + "interact-response": { + /** + * Format: uri + * @description The URI to direct the end user to. + */ + redirect: string; + /** @description Unique key to secure the callback. */ + finish: string; + }; + /** + * continue + * @description If the AS determines that the request can be continued with additional requests, it responds with the continue field. + */ + continue: { + /** @description A unique access token for continuing the request, called the "continuation access token". */ + access_token: { + value: string; + }; + /** + * Format: uri + * @description The URI at which the client instance can make continuation requests. + */ + uri: string; + /** @description The amount of time in integer seconds the client instance MUST wait after receiving this request continuation response and calling the continuation URI. */ + wait?: number; + }; + /** + * access_token + * @description A single access token or set of access tokens that the client instance can use to call the RS on behalf of the RO. + */ + access_token: { + /** @description The value of the access token as a string. The value is opaque to the client instance. The value SHOULD be limited to ASCII characters to facilitate transmission over HTTP headers within other protocols without requiring additional encoding. */ + value: string; + /** + * Format: uri + * @description The management URI for this access token. This URI MUST NOT include the access token value and SHOULD be different for each access token issued in a request. + */ + manage: string; + /** @description The number of seconds in which the access will expire. The client instance MUST NOT use the access token past this time. An RS MUST NOT accept an access token past this time. */ + expires_in?: number; + access: external["https://raw.githubusercontent.com/interledger/open-payments/1e3e118d8b22c5d2942f972e28ebf0f0114d04f5/openapi/schemas.yaml"]["components"]["schemas"]["access"]; + }; + }; +} + +export interface operations { + /** Make a new grant request */ + "post-request": { + parameters: {}; + responses: { + /** OK */ + 200: { + content: { + "application/json": + | { + interact: components["schemas"]["interact-response"]; + continue: components["schemas"]["continue"]; + } + | { + access_token: components["schemas"]["access_token"]; + continue: components["schemas"]["continue"]; + }; + }; + }; + /** Bad Request */ + 400: unknown; + }; + requestBody: { + content: { + "application/json": { + access_token: { + access: external["https://raw.githubusercontent.com/interledger/open-payments/1e3e118d8b22c5d2942f972e28ebf0f0114d04f5/openapi/schemas.yaml"]["components"]["schemas"]["access"]; + }; + client: components["schemas"]["client"]; + interact?: components["schemas"]["interact-request"]; + }; + }; + }; + }; + /** Continue a grant request during or after user interaction. */ + "post-continue": { + parameters: { + path: { + id: string; + }; + }; + responses: { + /** Success */ + 200: { + content: { + "application/json": { + access_token?: components["schemas"]["access_token"]; + continue: components["schemas"]["continue"]; + }; + }; + }; + /** Unauthorized */ + 401: unknown; + /** Not Found */ + 404: unknown; + }; + requestBody: { + content: { + "application/json": { + /** + * @description The interaction reference generated for this + * interaction by the AS. + */ + interact_ref: string; + }; + }; + }; + }; + /** Cancel a grant request or delete a grant client side. */ + "delete-continue": { + parameters: { + path: { + id: string; + }; + }; + responses: { + /** Accepted */ + 202: unknown; + /** Unauthorized */ + 401: unknown; + /** Not Found */ + 404: unknown; + }; + }; + /** Management endpoint to rotate access token. */ + "post-token": { + parameters: { + path: { + id: string; + }; + }; + responses: { + /** OK */ + 200: { + content: { + "application/json": { + access_token: components["schemas"]["access_token"]; + }; + }; + }; + /** Unauthorized */ + 401: unknown; + /** Not Found */ + 404: unknown; + }; + }; + /** Management endpoint to revoke access token. */ + "delete-token": { + parameters: { + path: { + id: string; + }; + }; + responses: { + /** No Content */ + 204: never; + /** Unauthorized */ + 401: unknown; + }; + }; +} + +export interface external { + "https://raw.githubusercontent.com/interledger/open-payments/1e3e118d8b22c5d2942f972e28ebf0f0114d04f5/openapi/schemas.yaml": { + paths: {}; + components: { + schemas: { + /** @description A description of the rights associated with this access token. */ + access: external["https://raw.githubusercontent.com/interledger/open-payments/1e3e118d8b22c5d2942f972e28ebf0f0114d04f5/openapi/schemas.yaml"]["components"]["schemas"]["access-item"][]; + /** @description The access associated with the access token is described using objects that each contain multiple dimensions of access. */ + "access-item": + | external["https://raw.githubusercontent.com/interledger/open-payments/1e3e118d8b22c5d2942f972e28ebf0f0114d04f5/openapi/schemas.yaml"]["components"]["schemas"]["access-incoming"] + | external["https://raw.githubusercontent.com/interledger/open-payments/1e3e118d8b22c5d2942f972e28ebf0f0114d04f5/openapi/schemas.yaml"]["components"]["schemas"]["access-outgoing"] + | external["https://raw.githubusercontent.com/interledger/open-payments/1e3e118d8b22c5d2942f972e28ebf0f0114d04f5/openapi/schemas.yaml"]["components"]["schemas"]["access-quote"]; + /** access-incoming */ + "access-incoming": { + /** @description The type of resource request as a string. This field defines which other fields are allowed in the request object. */ + type: "incoming-payment"; + /** @description The types of actions the client instance will take at the RS as an array of strings. */ + actions: ( + | "create" + | "complete" + | "read" + | "read-all" + | "list" + | "list-all" + )[]; + /** + * Format: uri + * @description A string identifier indicating a specific resource at the RS. + */ + identifier?: string; + }; + /** access-outgoing */ + "access-outgoing": { + /** @description The type of resource request as a string. This field defines which other fields are allowed in the request object. */ + type: "outgoing-payment"; + /** @description The types of actions the client instance will take at the RS as an array of strings. */ + actions: ("create" | "read" | "read-all" | "list" | "list-all")[]; + /** + * Format: uri + * @description A string identifier indicating a specific resource at the RS. + */ + identifier: string; + limits?: external["https://raw.githubusercontent.com/interledger/open-payments/1e3e118d8b22c5d2942f972e28ebf0f0114d04f5/openapi/schemas.yaml"]["components"]["schemas"]["limits-outgoing"]; + }; + /** access-quote */ + "access-quote": { + /** @description The type of resource request as a string. This field defines which other fields are allowed in the request object. */ + type: "quote"; + /** @description The types of actions the client instance will take at the RS as an array of strings. */ + actions: ("create" | "read" | "read-all")[]; + }; + /** + * amount + * @description All amounts are maxima, i.e. multiple payments can be created under a grant as long as the total amounts of these payments do not exceed the maximum amount per interval as specified in the grant. + */ + amount: { + /** + * Format: uint64 + * @description The value is an unsigned 64-bit integer amount, represented as a string. + */ + value: string; + assetCode: external["https://raw.githubusercontent.com/interledger/open-payments/1e3e118d8b22c5d2942f972e28ebf0f0114d04f5/openapi/schemas.yaml"]["components"]["schemas"]["assetCode"]; + assetScale: external["https://raw.githubusercontent.com/interledger/open-payments/1e3e118d8b22c5d2942f972e28ebf0f0114d04f5/openapi/schemas.yaml"]["components"]["schemas"]["assetScale"]; + }; + /** + * Asset code + * @description The assetCode is a code that indicates the underlying asset. This SHOULD be an ISO4217 currency code. + */ + assetCode: string; + /** + * Asset scale + * @description The scale of amounts denoted in the corresponding asset code. + */ + assetScale: number; + /** + * Interval + * @description [ISO8601 repeating interval](https://en.wikipedia.org/wiki/ISO_8601#Repeating_intervals) + */ + interval: string; + /** + * key + * @description A key presented by value MUST be a public key. + */ + key: { + /** @description The form of proof that the client instance will use when presenting the key. */ + proof: "httpsig"; + /** @description The public key and its properties represented as a JSON Web Key [[RFC7517](https://datatracker.ietf.org/doc/html/rfc7517)]. */ + jwk: { + /** @description The cryptographic algorithm family used with the key. The only allowed value is `EdDSA`. */ + alg: "EdDSA"; + /** @description A Key ID can be used to match a specific key. */ + kid: string; + /** @description The Key Type. The only allowed value is `OKP`. */ + kty: "OKP"; + /** @description The intended use of the key. */ + use?: "sig"; + /** @description The cryptographic curve used with the key. The only allowed value is `Ed25519`. */ + crv: "Ed25519"; + /** @description Public key encoded using the `base64url` encoding. */ + x: string; + /** @description Array of allowed operations this key may be used for. */ + key_ops?: ("sign" | "verify")[]; + /** @description UNIX timestamp indicating the earliest this key may be used. */ + nbf?: number; + /** @description UNIX timestamp indicating the latest this key may be used. */ + exp?: number; + /** @description The revocation status of the key. */ + revoked?: boolean; + }; + }; + /** + * limits-outgoing + * @description Open Payments specific property that defines the limits under which outgoing payments can be created. + */ + "limits-outgoing": Partial & { + receiver?: external["https://raw.githubusercontent.com/interledger/open-payments/1e3e118d8b22c5d2942f972e28ebf0f0114d04f5/openapi/schemas.yaml"]["components"]["schemas"]["receiver"]; + sendAmount?: external["https://raw.githubusercontent.com/interledger/open-payments/1e3e118d8b22c5d2942f972e28ebf0f0114d04f5/openapi/schemas.yaml"]["components"]["schemas"]["amount"]; + receiveAmount?: external["https://raw.githubusercontent.com/interledger/open-payments/1e3e118d8b22c5d2942f972e28ebf0f0114d04f5/openapi/schemas.yaml"]["components"]["schemas"]["amount"]; + interval?: external["https://raw.githubusercontent.com/interledger/open-payments/1e3e118d8b22c5d2942f972e28ebf0f0114d04f5/openapi/schemas.yaml"]["components"]["schemas"]["interval"]; + }; + "list-actions": "list" | "list-all"; + "read-actions": "read" | "read-all"; + /** + * Receiver + * Format: uri + * @description The URL of the incoming payment or ILP STREAM connection that is being paid. + */ + receiver: string; + }; + }; + operations: {}; + }; +} diff --git a/packages/open-payments/src/generated/types.ts b/packages/open-payments/src/generated/resource-server-types.ts similarity index 91% rename from packages/open-payments/src/generated/types.ts rename to packages/open-payments/src/generated/resource-server-types.ts index d3398c3d86..ab1a4f3c7e 100644 --- a/packages/open-payments/src/generated/types.ts +++ b/packages/open-payments/src/generated/resource-server-types.ts @@ -118,8 +118,8 @@ export interface components { id: string; /** @description A public name for the account. This should be set by the account holder with their provider to provide a hint to counterparties as to the identity of the account holder. */ publicName?: string; - assetCode: external["https://raw.githubusercontent.com/interledger/open-payments/7bb2e6a03d7dfe7ecb0553afb6c70741317bb489/openapi/shared/schemas.yaml"]["components"]["schemas"]["assetCode"]; - assetScale: external["https://raw.githubusercontent.com/interledger/open-payments/7bb2e6a03d7dfe7ecb0553afb6c70741317bb489/openapi/shared/schemas.yaml"]["components"]["schemas"]["assetScale"]; + assetCode: external["https://raw.githubusercontent.com/interledger/open-payments/1e3e118d8b22c5d2942f972e28ebf0f0114d04f5/openapi/schemas.yaml"]["components"]["schemas"]["assetCode"]; + assetScale: external["https://raw.githubusercontent.com/interledger/open-payments/1e3e118d8b22c5d2942f972e28ebf0f0114d04f5/openapi/schemas.yaml"]["components"]["schemas"]["assetScale"]; /** * Format: uri * @description The URL of the authorization server endpoint for getting grants and access tokens for this payment pointer. @@ -147,8 +147,8 @@ export interface components { ilpAddress: string; /** @description The base64 url-encoded shared secret to use when establishing a STREAM connection. */ sharedSecret: string; - assetCode: external["https://raw.githubusercontent.com/interledger/open-payments/7bb2e6a03d7dfe7ecb0553afb6c70741317bb489/openapi/shared/schemas.yaml"]["components"]["schemas"]["assetCode"]; - assetScale: external["https://raw.githubusercontent.com/interledger/open-payments/7bb2e6a03d7dfe7ecb0553afb6c70741317bb489/openapi/shared/schemas.yaml"]["components"]["schemas"]["assetScale"]; + assetCode: external["https://raw.githubusercontent.com/interledger/open-payments/1e3e118d8b22c5d2942f972e28ebf0f0114d04f5/openapi/schemas.yaml"]["components"]["schemas"]["assetCode"]; + assetScale: external["https://raw.githubusercontent.com/interledger/open-payments/1e3e118d8b22c5d2942f972e28ebf0f0114d04f5/openapi/schemas.yaml"]["components"]["schemas"]["assetScale"]; }; /** * Incoming Payment @@ -168,9 +168,9 @@ export interface components { /** @description Describes whether the incoming payment has completed receiving fund. */ completed: boolean; /** @description The maximum amount that should be paid into the payment pointer under this incoming payment. */ - incomingAmount?: external["https://raw.githubusercontent.com/interledger/open-payments/7bb2e6a03d7dfe7ecb0553afb6c70741317bb489/openapi/shared/schemas.yaml"]["components"]["schemas"]["amount"]; + incomingAmount?: external["https://raw.githubusercontent.com/interledger/open-payments/1e3e118d8b22c5d2942f972e28ebf0f0114d04f5/openapi/schemas.yaml"]["components"]["schemas"]["amount"]; /** @description The total amount that has been paid into the payment pointer under this incoming payment. */ - receivedAmount: external["https://raw.githubusercontent.com/interledger/open-payments/7bb2e6a03d7dfe7ecb0553afb6c70741317bb489/openapi/shared/schemas.yaml"]["components"]["schemas"]["amount"]; + receivedAmount: external["https://raw.githubusercontent.com/interledger/open-payments/1e3e118d8b22c5d2942f972e28ebf0f0114d04f5/openapi/schemas.yaml"]["components"]["schemas"]["amount"]; /** * Format: date-time * @description The date and time when payments under this incoming payment will no longer be accepted. @@ -232,13 +232,13 @@ export interface components { /** @description Describes whether the payment failed to send its full amount. */ failed?: boolean; /** @description The URL of the incoming payment or ILP STREAM Connection that is being paid. */ - receiver: external["https://raw.githubusercontent.com/interledger/open-payments/7bb2e6a03d7dfe7ecb0553afb6c70741317bb489/openapi/shared/schemas.yaml"]["components"]["schemas"]["receiver"]; + receiver: external["https://raw.githubusercontent.com/interledger/open-payments/1e3e118d8b22c5d2942f972e28ebf0f0114d04f5/openapi/schemas.yaml"]["components"]["schemas"]["receiver"]; /** @description The total amount that should be received by the receiver when this outgoing payment has been paid. */ - receiveAmount: external["https://raw.githubusercontent.com/interledger/open-payments/7bb2e6a03d7dfe7ecb0553afb6c70741317bb489/openapi/shared/schemas.yaml"]["components"]["schemas"]["amount"]; + receiveAmount: external["https://raw.githubusercontent.com/interledger/open-payments/1e3e118d8b22c5d2942f972e28ebf0f0114d04f5/openapi/schemas.yaml"]["components"]["schemas"]["amount"]; /** @description The total amount that should be sent when this outgoing payment has been paid. */ - sendAmount: external["https://raw.githubusercontent.com/interledger/open-payments/7bb2e6a03d7dfe7ecb0553afb6c70741317bb489/openapi/shared/schemas.yaml"]["components"]["schemas"]["amount"]; + sendAmount: external["https://raw.githubusercontent.com/interledger/open-payments/1e3e118d8b22c5d2942f972e28ebf0f0114d04f5/openapi/schemas.yaml"]["components"]["schemas"]["amount"]; /** @description The total amount that has been sent under this outgoing payment. */ - sentAmount: external["https://raw.githubusercontent.com/interledger/open-payments/7bb2e6a03d7dfe7ecb0553afb6c70741317bb489/openapi/shared/schemas.yaml"]["components"]["schemas"]["amount"]; + sentAmount: external["https://raw.githubusercontent.com/interledger/open-payments/1e3e118d8b22c5d2942f972e28ebf0f0114d04f5/openapi/schemas.yaml"]["components"]["schemas"]["amount"]; /** @description Human readable description of the outgoing payment that will be visible to the account holder and shared with the receiver. */ description?: string; /** @description A reference that can be used by external systems to reconcile this payment with their systems. E.g. An invoice number. (Optional) */ @@ -269,9 +269,9 @@ export interface components { * @description The URL of the payment pointer from which this quote's payment would be sent. */ paymentPointer: string; - receiver: external["https://raw.githubusercontent.com/interledger/open-payments/7bb2e6a03d7dfe7ecb0553afb6c70741317bb489/openapi/shared/schemas.yaml"]["components"]["schemas"]["receiver"]; - receiveAmount: external["https://raw.githubusercontent.com/interledger/open-payments/7bb2e6a03d7dfe7ecb0553afb6c70741317bb489/openapi/shared/schemas.yaml"]["components"]["schemas"]["amount"]; - sendAmount: external["https://raw.githubusercontent.com/interledger/open-payments/7bb2e6a03d7dfe7ecb0553afb6c70741317bb489/openapi/shared/schemas.yaml"]["components"]["schemas"]["amount"]; + receiver: external["https://raw.githubusercontent.com/interledger/open-payments/1e3e118d8b22c5d2942f972e28ebf0f0114d04f5/openapi/schemas.yaml"]["components"]["schemas"]["receiver"]; + receiveAmount: external["https://raw.githubusercontent.com/interledger/open-payments/1e3e118d8b22c5d2942f972e28ebf0f0114d04f5/openapi/schemas.yaml"]["components"]["schemas"]["amount"]; + sendAmount: external["https://raw.githubusercontent.com/interledger/open-payments/1e3e118d8b22c5d2942f972e28ebf0f0114d04f5/openapi/schemas.yaml"]["components"]["schemas"]["amount"]; /** @description The date and time when the calculated `sendAmount` is no longer valid. */ expiresAt?: string; /** @@ -458,7 +458,7 @@ export interface operations { content: { "application/json": { /** @description The maximum amount that should be paid into the payment pointer under this incoming payment. */ - incomingAmount?: external["https://raw.githubusercontent.com/interledger/open-payments/7bb2e6a03d7dfe7ecb0553afb6c70741317bb489/openapi/shared/schemas.yaml"]["components"]["schemas"]["amount"]; + incomingAmount?: external["https://raw.githubusercontent.com/interledger/open-payments/1e3e118d8b22c5d2942f972e28ebf0f0114d04f5/openapi/schemas.yaml"]["components"]["schemas"]["amount"]; /** * Format: date-time * @description The date and time when payments into the incoming payment must no longer be accepted. @@ -571,9 +571,9 @@ export interface operations { requestBody: { content: { "application/json": { - receiver: external["https://raw.githubusercontent.com/interledger/open-payments/7bb2e6a03d7dfe7ecb0553afb6c70741317bb489/openapi/shared/schemas.yaml"]["components"]["schemas"]["receiver"]; - receiveAmount?: external["https://raw.githubusercontent.com/interledger/open-payments/7bb2e6a03d7dfe7ecb0553afb6c70741317bb489/openapi/shared/schemas.yaml"]["components"]["schemas"]["amount"]; - sendAmount?: external["https://raw.githubusercontent.com/interledger/open-payments/7bb2e6a03d7dfe7ecb0553afb6c70741317bb489/openapi/shared/schemas.yaml"]["components"]["schemas"]["amount"]; + receiver: external["https://raw.githubusercontent.com/interledger/open-payments/1e3e118d8b22c5d2942f972e28ebf0f0114d04f5/openapi/schemas.yaml"]["components"]["schemas"]["receiver"]; + receiveAmount?: external["https://raw.githubusercontent.com/interledger/open-payments/1e3e118d8b22c5d2942f972e28ebf0f0114d04f5/openapi/schemas.yaml"]["components"]["schemas"]["amount"]; + sendAmount?: external["https://raw.githubusercontent.com/interledger/open-payments/1e3e118d8b22c5d2942f972e28ebf0f0114d04f5/openapi/schemas.yaml"]["components"]["schemas"]["amount"]; }; }; }; @@ -693,17 +693,17 @@ export interface operations { } export interface external { - "https://raw.githubusercontent.com/interledger/open-payments/7bb2e6a03d7dfe7ecb0553afb6c70741317bb489/openapi/shared/schemas.yaml": { + "https://raw.githubusercontent.com/interledger/open-payments/1e3e118d8b22c5d2942f972e28ebf0f0114d04f5/openapi/schemas.yaml": { paths: {}; components: { schemas: { /** @description A description of the rights associated with this access token. */ - access: external["https://raw.githubusercontent.com/interledger/open-payments/7bb2e6a03d7dfe7ecb0553afb6c70741317bb489/openapi/shared/schemas.yaml"]["components"]["schemas"]["access-item"][]; + access: external["https://raw.githubusercontent.com/interledger/open-payments/1e3e118d8b22c5d2942f972e28ebf0f0114d04f5/openapi/schemas.yaml"]["components"]["schemas"]["access-item"][]; /** @description The access associated with the access token is described using objects that each contain multiple dimensions of access. */ "access-item": - | external["https://raw.githubusercontent.com/interledger/open-payments/7bb2e6a03d7dfe7ecb0553afb6c70741317bb489/openapi/shared/schemas.yaml"]["components"]["schemas"]["access-incoming"] - | external["https://raw.githubusercontent.com/interledger/open-payments/7bb2e6a03d7dfe7ecb0553afb6c70741317bb489/openapi/shared/schemas.yaml"]["components"]["schemas"]["access-outgoing"] - | external["https://raw.githubusercontent.com/interledger/open-payments/7bb2e6a03d7dfe7ecb0553afb6c70741317bb489/openapi/shared/schemas.yaml"]["components"]["schemas"]["access-quote"]; + | external["https://raw.githubusercontent.com/interledger/open-payments/1e3e118d8b22c5d2942f972e28ebf0f0114d04f5/openapi/schemas.yaml"]["components"]["schemas"]["access-incoming"] + | external["https://raw.githubusercontent.com/interledger/open-payments/1e3e118d8b22c5d2942f972e28ebf0f0114d04f5/openapi/schemas.yaml"]["components"]["schemas"]["access-outgoing"] + | external["https://raw.githubusercontent.com/interledger/open-payments/1e3e118d8b22c5d2942f972e28ebf0f0114d04f5/openapi/schemas.yaml"]["components"]["schemas"]["access-quote"]; /** access-incoming */ "access-incoming": { /** @description The type of resource request as a string. This field defines which other fields are allowed in the request object. */ @@ -734,7 +734,7 @@ export interface external { * @description A string identifier indicating a specific resource at the RS. */ identifier: string; - limits?: external["https://raw.githubusercontent.com/interledger/open-payments/7bb2e6a03d7dfe7ecb0553afb6c70741317bb489/openapi/shared/schemas.yaml"]["components"]["schemas"]["limits-outgoing"]; + limits?: external["https://raw.githubusercontent.com/interledger/open-payments/1e3e118d8b22c5d2942f972e28ebf0f0114d04f5/openapi/schemas.yaml"]["components"]["schemas"]["limits-outgoing"]; }; /** access-quote */ "access-quote": { @@ -753,8 +753,8 @@ export interface external { * @description The value is an unsigned 64-bit integer amount, represented as a string. */ value: string; - assetCode: external["https://raw.githubusercontent.com/interledger/open-payments/7bb2e6a03d7dfe7ecb0553afb6c70741317bb489/openapi/shared/schemas.yaml"]["components"]["schemas"]["assetCode"]; - assetScale: external["https://raw.githubusercontent.com/interledger/open-payments/7bb2e6a03d7dfe7ecb0553afb6c70741317bb489/openapi/shared/schemas.yaml"]["components"]["schemas"]["assetScale"]; + assetCode: external["https://raw.githubusercontent.com/interledger/open-payments/1e3e118d8b22c5d2942f972e28ebf0f0114d04f5/openapi/schemas.yaml"]["components"]["schemas"]["assetCode"]; + assetScale: external["https://raw.githubusercontent.com/interledger/open-payments/1e3e118d8b22c5d2942f972e28ebf0f0114d04f5/openapi/schemas.yaml"]["components"]["schemas"]["assetScale"]; }; /** * Asset code @@ -807,10 +807,10 @@ export interface external { * @description Open Payments specific property that defines the limits under which outgoing payments can be created. */ "limits-outgoing": Partial & { - receiver?: external["https://raw.githubusercontent.com/interledger/open-payments/7bb2e6a03d7dfe7ecb0553afb6c70741317bb489/openapi/shared/schemas.yaml"]["components"]["schemas"]["receiver"]; - sendAmount?: external["https://raw.githubusercontent.com/interledger/open-payments/7bb2e6a03d7dfe7ecb0553afb6c70741317bb489/openapi/shared/schemas.yaml"]["components"]["schemas"]["amount"]; - receiveAmount?: external["https://raw.githubusercontent.com/interledger/open-payments/7bb2e6a03d7dfe7ecb0553afb6c70741317bb489/openapi/shared/schemas.yaml"]["components"]["schemas"]["amount"]; - interval?: external["https://raw.githubusercontent.com/interledger/open-payments/7bb2e6a03d7dfe7ecb0553afb6c70741317bb489/openapi/shared/schemas.yaml"]["components"]["schemas"]["interval"]; + receiver?: external["https://raw.githubusercontent.com/interledger/open-payments/1e3e118d8b22c5d2942f972e28ebf0f0114d04f5/openapi/schemas.yaml"]["components"]["schemas"]["receiver"]; + sendAmount?: external["https://raw.githubusercontent.com/interledger/open-payments/1e3e118d8b22c5d2942f972e28ebf0f0114d04f5/openapi/schemas.yaml"]["components"]["schemas"]["amount"]; + receiveAmount?: external["https://raw.githubusercontent.com/interledger/open-payments/1e3e118d8b22c5d2942f972e28ebf0f0114d04f5/openapi/schemas.yaml"]["components"]["schemas"]["amount"]; + interval?: external["https://raw.githubusercontent.com/interledger/open-payments/1e3e118d8b22c5d2942f972e28ebf0f0114d04f5/openapi/schemas.yaml"]["components"]["schemas"]["interval"]; }; "list-actions": "list" | "list-all"; "read-actions": "read" | "read-all"; diff --git a/packages/open-payments/src/types.ts b/packages/open-payments/src/types.ts index 96198ee03b..41fccefc32 100644 --- a/packages/open-payments/src/types.ts +++ b/packages/open-payments/src/types.ts @@ -1,9 +1,16 @@ -import { components, paths as Paths } from './generated/types' +import { + components as rsComponents, + paths as RSPaths +} from './generated/resource-server-types' +import { paths as ASPaths } from './generated/authorization-server-types' -export const getPath =

(path: P): string => +export const getRSPath =

(path: P): string => + path as string +export const getASPath =

(path: P): string => path as string export type IncomingPayment = - components['schemas']['incoming-payment-with-connection'] -export type ILPStreamConnection = components['schemas']['ilp-stream-connection'] -export type PaymentPointer = components['schemas']['payment-pointer'] + rsComponents['schemas']['incoming-payment-with-connection'] +export type ILPStreamConnection = + rsComponents['schemas']['ilp-stream-connection'] +export type PaymentPointer = rsComponents['schemas']['payment-pointer'] diff --git a/packages/open-payments/tsconfig.json b/packages/open-payments/tsconfig.json index 700f4d60dc..bc4252bfc2 100644 --- a/packages/open-payments/tsconfig.json +++ b/packages/open-payments/tsconfig.json @@ -7,5 +7,5 @@ "declaration": true }, "include": ["src/**/*"], - "exclude": ["**/*.test.ts", "src/scripts/*", "src/test/*"] + "exclude": ["**/*.test.ts", "src/test/*"] } From deb3081be74d5e80d73760a97247864f5089b7fd Mon Sep 17 00:00:00 2001 From: Max Kurapov Date: Fri, 4 Nov 2022 13:46:24 +0100 Subject: [PATCH 02/16] feat(open-payments): adding support for grant requests --- .../open-payments/src/client/grant.test.ts | 31 ++++++++ packages/open-payments/src/client/grant.ts | 58 ++++++++++++++ packages/open-payments/src/client/index.ts | 29 ++++++- .../open-payments/src/client/requests.test.ts | 79 ++++++++++++++++++- packages/open-payments/src/client/requests.ts | 52 ++++++++++++ packages/open-payments/src/index.ts | 7 +- packages/open-payments/src/types.ts | 36 +++++++-- 7 files changed, 278 insertions(+), 14 deletions(-) create mode 100644 packages/open-payments/src/client/grant.test.ts create mode 100644 packages/open-payments/src/client/grant.ts diff --git a/packages/open-payments/src/client/grant.test.ts b/packages/open-payments/src/client/grant.test.ts new file mode 100644 index 0000000000..7fcd22d128 --- /dev/null +++ b/packages/open-payments/src/client/grant.test.ts @@ -0,0 +1,31 @@ +import { createGrantRoutes } from './grant' +import { OpenAPI, HttpMethod, createOpenAPI } from 'openapi' +import config from '../config' +import { defaultAxiosInstance, silentLogger } from '../test/helpers' + +describe('grant', (): void => { + let openApi: OpenAPI + + beforeAll(async () => { + openApi = await createOpenAPI(config.OPEN_PAYMENTS_AS_OPEN_API_URL) + }) + + const axiosInstance = defaultAxiosInstance + const logger = silentLogger + + describe('createGrantRoutes', (): void => { + test('creates response validators for grant requests', async (): Promise => { + jest.spyOn(openApi, 'createResponseValidator') + + createGrantRoutes({ axiosInstance, openApi, logger }) + expect(openApi.createResponseValidator).toHaveBeenNthCalledWith(1, { + path: '/', + method: HttpMethod.POST + }) + expect(openApi.createResponseValidator).toHaveBeenNthCalledWith(2, { + path: '/', + method: HttpMethod.POST + }) + }) + }) +}) diff --git a/packages/open-payments/src/client/grant.ts b/packages/open-payments/src/client/grant.ts new file mode 100644 index 0000000000..72d49833d2 --- /dev/null +++ b/packages/open-payments/src/client/grant.ts @@ -0,0 +1,58 @@ +import { HttpMethod } from 'openapi' +import { ClientDeps } from '.' +import { + getASPath, + InteractiveGrant, + InteractiveGrantRequest, + NonInteractiveGrant, + NonInteractiveGrantRequest +} from '../types' +import { post } from './requests' + +interface RequestGrantArgs { + url: string + request: T +} + +export interface GrantRoutes { + requestInteractiveGrant( + args: RequestGrantArgs + ): Promise + requestNonInteractiveGrant( + args: RequestGrantArgs + ): Promise +} + +export const createGrantRoutes = (clientDeps: ClientDeps): GrantRoutes => { + const { axiosInstance, openApi, logger } = clientDeps + + const createInteractiveGrantValidator = + openApi.createResponseValidator({ + path: getASPath('/'), + method: HttpMethod.POST + }) + const createNonInteractiveGrantValidator = + openApi.createResponseValidator({ + path: getASPath('/'), + method: HttpMethod.POST + }) + + return { + requestInteractiveGrant: ( + args: RequestGrantArgs + ) => + post( + { axiosInstance, logger }, + { url: args.url, body: args.request }, + createInteractiveGrantValidator + ), + requestNonInteractiveGrant: ( + args: RequestGrantArgs + ) => + post( + { axiosInstance, logger }, + { url: args.url, body: args.request }, + createNonInteractiveGrantValidator + ) + } +} diff --git a/packages/open-payments/src/client/index.ts b/packages/open-payments/src/client/index.ts index 5ef0bc044a..4e3d42d1f0 100644 --- a/packages/open-payments/src/client/index.ts +++ b/packages/open-payments/src/client/index.ts @@ -15,6 +15,7 @@ import { } from './payment-pointer' import { createAxiosInstance } from './requests' import { AxiosInstance } from 'axios' +import { createGrantRoutes, GrantRoutes } from './grant' export interface CreateOpenPaymentClientArgs { requestTimeoutMs?: number @@ -31,6 +32,7 @@ export interface OpenPaymentsClient { incomingPayment: IncomingPaymentRoutes ilpStreamConnection: ILPStreamConnectionRoutes paymentPointer: PaymentPointerRoutes + grant: GrantRoutes } export const createClient = async ( @@ -43,12 +45,31 @@ export const createClient = async ( const resourceServerOpenApi = await createOpenAPI( config.OPEN_PAYMENTS_RS_OPEN_API_URL ) + const authorizationServerOpenApi = await createOpenAPI( + config.OPEN_PAYMENTS_AS_OPEN_API_URL + ) const logger = args?.logger ?? createLogger() - const deps = { axiosInstance, openApi: resourceServerOpenApi, logger } return { - incomingPayment: createIncomingPaymentRoutes(deps), - ilpStreamConnection: createILPStreamConnectionRoutes(deps), - paymentPointer: createPaymentPointerRoutes(deps) + incomingPayment: createIncomingPaymentRoutes({ + axiosInstance, + openApi: resourceServerOpenApi, + logger + }), + ilpStreamConnection: createILPStreamConnectionRoutes({ + axiosInstance, + openApi: resourceServerOpenApi, + logger + }), + paymentPointer: createPaymentPointerRoutes({ + axiosInstance, + openApi: resourceServerOpenApi, + logger + }), + grant: createGrantRoutes({ + axiosInstance, + openApi: authorizationServerOpenApi, + logger + }) } } diff --git a/packages/open-payments/src/client/requests.test.ts b/packages/open-payments/src/client/requests.test.ts index 60b4d62860..b8332490c9 100644 --- a/packages/open-payments/src/client/requests.test.ts +++ b/packages/open-payments/src/client/requests.test.ts @@ -1,5 +1,5 @@ /* eslint-disable @typescript-eslint/no-empty-function */ -import { createAxiosInstance, get } from './requests' +import { createAxiosInstance, get, post } from './requests' import nock from 'nock' import { mockOpenApiResponseValidators, silentLogger } from '../test/helpers' @@ -114,4 +114,81 @@ describe('requests', (): void => { ).rejects.toThrow(/Failed to validate OpenApi response/) }) }) + + describe('post', (): void => { + const axiosInstance = createAxiosInstance({ requestTimeoutMs: 0 }) + const baseUrl = 'http://localhost:1000' + const responseValidators = mockOpenApiResponseValidators() + + beforeAll(() => { + jest.spyOn(axiosInstance, 'post') + }) + + test('properly POSTs request', async (): Promise => { + const status = 200 + const body = { + id: 'id' + } + + nock(baseUrl).post('/grant', body).reply(status, body) + + await post( + { axiosInstance, logger }, + { + url: `${baseUrl}/grant`, + body + }, + responseValidators.successfulValidator + ) + + expect(axiosInstance.post).toHaveBeenCalledWith(`${baseUrl}/grant`, body) + }) + + test('calls validator function properly', async (): Promise => { + const status = 200 + const body = { + id: 'id' + } + + nock(baseUrl).post('/grant', body).reply(status, body) + + const responseValidatorSpy = jest.spyOn( + responseValidators, + 'successfulValidator' + ) + + await post( + { axiosInstance, logger }, + { + url: `${baseUrl}/grant`, + body + }, + responseValidators.successfulValidator + ) + + expect(responseValidatorSpy).toHaveBeenCalledWith({ + body, + status + }) + }) + + test('throws if response validator function fails', async (): Promise => { + const status = 200 + const body = { + id: 'id' + } + nock(baseUrl).post('/grant', body).reply(status, body) + + await expect( + post( + { axiosInstance, logger }, + { + url: `${baseUrl}/grant`, + body + }, + responseValidators.failedValidator + ) + ).rejects.toThrow(/Failed to validate OpenApi response/) + }) + }) }) diff --git a/packages/open-payments/src/client/requests.ts b/packages/open-payments/src/client/requests.ts index cc9608a057..0fdecd04c7 100644 --- a/packages/open-payments/src/client/requests.ts +++ b/packages/open-payments/src/client/requests.ts @@ -6,6 +6,10 @@ interface GetArgs { url: string accessToken?: string } +interface PostArgs { + url: string + body: T +} export const get = async ( clientDeps: Pick, @@ -63,6 +67,54 @@ export const get = async ( } } +export const post = async ( + clientDeps: Pick, + args: PostArgs, + openApiResponseValidator: ResponseValidator +): Promise => { + const { axiosInstance, logger } = clientDeps + const { body } = args + + const requestUrl = new URL(args.url) + if (process.env.NODE_ENV === 'development') { + requestUrl.protocol = 'http' + } + + const url = requestUrl.href + + try { + const { data, status } = await axiosInstance.post(url, body) + + try { + openApiResponseValidator({ + status, + body: data + }) + } catch (error) { + const errorMessage = 'Failed to validate OpenApi response' + logger.error( + { + data: JSON.stringify(data), + url, + validationError: error?.message + }, + errorMessage + ) + + throw new Error(errorMessage) + } + + return data + } catch (error) { + const errorMessage = `Error when making Open Payments POST request: ${ + error?.message ? error.message : 'Unknown error' + }` + logger.error({ url }, errorMessage) + + throw new Error(errorMessage) + } +} + export const createAxiosInstance = (args: { requestTimeoutMs: number }): AxiosInstance => { diff --git a/packages/open-payments/src/index.ts b/packages/open-payments/src/index.ts index 0cc9edc2a2..baa01d97eb 100644 --- a/packages/open-payments/src/index.ts +++ b/packages/open-payments/src/index.ts @@ -1,2 +1,7 @@ -export { IncomingPayment, ILPStreamConnection } from './types' +export { + IncomingPayment, + ILPStreamConnection, + InteractiveGrant, + NonInteractiveGrant +} from './types' export { createClient, OpenPaymentsClient } from './client' diff --git a/packages/open-payments/src/types.ts b/packages/open-payments/src/types.ts index 41fccefc32..f75969394e 100644 --- a/packages/open-payments/src/types.ts +++ b/packages/open-payments/src/types.ts @@ -1,16 +1,36 @@ import { - components as rsComponents, + components as RSComponents, paths as RSPaths } from './generated/resource-server-types' -import { paths as ASPaths } from './generated/authorization-server-types' +import { + components as ASComponents, + paths as ASPaths +} from './generated/authorization-server-types' export const getRSPath =

(path: P): string => path as string -export const getASPath =

(path: P): string => - path as string - export type IncomingPayment = - rsComponents['schemas']['incoming-payment-with-connection'] + RSComponents['schemas']['incoming-payment-with-connection'] export type ILPStreamConnection = - rsComponents['schemas']['ilp-stream-connection'] -export type PaymentPointer = rsComponents['schemas']['payment-pointer'] + RSComponents['schemas']['ilp-stream-connection'] +export type PaymentPointer = RSComponents['schemas']['payment-pointer'] + +export const getASPath =

(path: P): string => + path as string +export type NonInteractiveGrantRequest = { + accessToken: ASComponents['schemas']['access_token'] + client: ASComponents['schemas']['client'] +} +export type NonInteractiveGrant = { + accessToken: ASComponents['schemas']['access_token'] + continue: ASComponents['schemas']['continue'] +} +export type InteractiveGrantRequest = { + accessToken: ASComponents['schemas']['access_token'] + client: ASComponents['schemas']['client'] + interact: ASComponents['schemas']['interact-request'] +} +export type InteractiveGrant = { + interact: ASComponents['schemas']['interact-response'] + continue: ASComponents['schemas']['continue'] +} From 17af881f726322a05d890a101a97d370a3462410 Mon Sep 17 00:00:00 2001 From: Max Kurapov Date: Mon, 7 Nov 2022 16:47:32 +0100 Subject: [PATCH 03/16] chore(open-payments): update open-payments spec commit version --- packages/open-payments/src/config.ts | 4 +- .../generated/authorization-server-types.ts | 30 +++++----- .../src/generated/resource-server-types.ts | 58 +++++++++---------- 3 files changed, 46 insertions(+), 46 deletions(-) diff --git a/packages/open-payments/src/config.ts b/packages/open-payments/src/config.ts index 0a3931c035..a5932ff7b6 100644 --- a/packages/open-payments/src/config.ts +++ b/packages/open-payments/src/config.ts @@ -1,7 +1,7 @@ export default { OPEN_PAYMENTS_RS_OPEN_API_URL: - 'https://raw.githubusercontent.com/interledger/open-payments/1e3e118d8b22c5d2942f972e28ebf0f0114d04f5/openapi/resource-server.yaml', + 'https://raw.githubusercontent.com/interledger/open-payments/62c4b4a9875e3adaa21f89f597e88db43016fe0b/openapi/resource-server.yaml', OPEN_PAYMENTS_AS_OPEN_API_URL: - 'https://raw.githubusercontent.com/interledger/open-payments/1e3e118d8b22c5d2942f972e28ebf0f0114d04f5/openapi/auth-server.yaml', + 'https://raw.githubusercontent.com/interledger/open-payments/62c4b4a9875e3adaa21f89f597e88db43016fe0b/openapi/auth-server.yaml', DEFAULT_REQUEST_TIMEOUT_MS: 5_000 } diff --git a/packages/open-payments/src/generated/authorization-server-types.ts b/packages/open-payments/src/generated/authorization-server-types.ts index 7f258e56c8..4dc991fb30 100644 --- a/packages/open-payments/src/generated/authorization-server-types.ts +++ b/packages/open-payments/src/generated/authorization-server-types.ts @@ -52,7 +52,7 @@ export interface components { client_id?: string; /** @description The public key of the client instance to be used in this request or a reference to a key. */ key?: - | external["https://raw.githubusercontent.com/interledger/open-payments/1e3e118d8b22c5d2942f972e28ebf0f0114d04f5/openapi/schemas.yaml"]["components"]["schemas"]["key"] + | external["https://raw.githubusercontent.com/interledger/open-payments/62c4b4a9875e3adaa21f89f597e88db43016fe0b/openapi/schemas.yaml"]["components"]["schemas"]["key"] | string; }; /** @@ -116,7 +116,7 @@ export interface components { manage: string; /** @description The number of seconds in which the access will expire. The client instance MUST NOT use the access token past this time. An RS MUST NOT accept an access token past this time. */ expires_in?: number; - access: external["https://raw.githubusercontent.com/interledger/open-payments/1e3e118d8b22c5d2942f972e28ebf0f0114d04f5/openapi/schemas.yaml"]["components"]["schemas"]["access"]; + access: external["https://raw.githubusercontent.com/interledger/open-payments/62c4b4a9875e3adaa21f89f597e88db43016fe0b/openapi/schemas.yaml"]["components"]["schemas"]["access"]; }; }; } @@ -147,7 +147,7 @@ export interface operations { content: { "application/json": { access_token: { - access: external["https://raw.githubusercontent.com/interledger/open-payments/1e3e118d8b22c5d2942f972e28ebf0f0114d04f5/openapi/schemas.yaml"]["components"]["schemas"]["access"]; + access: external["https://raw.githubusercontent.com/interledger/open-payments/62c4b4a9875e3adaa21f89f597e88db43016fe0b/openapi/schemas.yaml"]["components"]["schemas"]["access"]; }; client: components["schemas"]["client"]; interact?: components["schemas"]["interact-request"]; @@ -244,17 +244,17 @@ export interface operations { } export interface external { - "https://raw.githubusercontent.com/interledger/open-payments/1e3e118d8b22c5d2942f972e28ebf0f0114d04f5/openapi/schemas.yaml": { + "https://raw.githubusercontent.com/interledger/open-payments/62c4b4a9875e3adaa21f89f597e88db43016fe0b/openapi/schemas.yaml": { paths: {}; components: { schemas: { /** @description A description of the rights associated with this access token. */ - access: external["https://raw.githubusercontent.com/interledger/open-payments/1e3e118d8b22c5d2942f972e28ebf0f0114d04f5/openapi/schemas.yaml"]["components"]["schemas"]["access-item"][]; + access: external["https://raw.githubusercontent.com/interledger/open-payments/62c4b4a9875e3adaa21f89f597e88db43016fe0b/openapi/schemas.yaml"]["components"]["schemas"]["access-item"][]; /** @description The access associated with the access token is described using objects that each contain multiple dimensions of access. */ "access-item": - | external["https://raw.githubusercontent.com/interledger/open-payments/1e3e118d8b22c5d2942f972e28ebf0f0114d04f5/openapi/schemas.yaml"]["components"]["schemas"]["access-incoming"] - | external["https://raw.githubusercontent.com/interledger/open-payments/1e3e118d8b22c5d2942f972e28ebf0f0114d04f5/openapi/schemas.yaml"]["components"]["schemas"]["access-outgoing"] - | external["https://raw.githubusercontent.com/interledger/open-payments/1e3e118d8b22c5d2942f972e28ebf0f0114d04f5/openapi/schemas.yaml"]["components"]["schemas"]["access-quote"]; + | external["https://raw.githubusercontent.com/interledger/open-payments/62c4b4a9875e3adaa21f89f597e88db43016fe0b/openapi/schemas.yaml"]["components"]["schemas"]["access-incoming"] + | external["https://raw.githubusercontent.com/interledger/open-payments/62c4b4a9875e3adaa21f89f597e88db43016fe0b/openapi/schemas.yaml"]["components"]["schemas"]["access-outgoing"] + | external["https://raw.githubusercontent.com/interledger/open-payments/62c4b4a9875e3adaa21f89f597e88db43016fe0b/openapi/schemas.yaml"]["components"]["schemas"]["access-quote"]; /** access-incoming */ "access-incoming": { /** @description The type of resource request as a string. This field defines which other fields are allowed in the request object. */ @@ -285,7 +285,7 @@ export interface external { * @description A string identifier indicating a specific resource at the RS. */ identifier: string; - limits?: external["https://raw.githubusercontent.com/interledger/open-payments/1e3e118d8b22c5d2942f972e28ebf0f0114d04f5/openapi/schemas.yaml"]["components"]["schemas"]["limits-outgoing"]; + limits?: external["https://raw.githubusercontent.com/interledger/open-payments/62c4b4a9875e3adaa21f89f597e88db43016fe0b/openapi/schemas.yaml"]["components"]["schemas"]["limits-outgoing"]; }; /** access-quote */ "access-quote": { @@ -304,8 +304,8 @@ export interface external { * @description The value is an unsigned 64-bit integer amount, represented as a string. */ value: string; - assetCode: external["https://raw.githubusercontent.com/interledger/open-payments/1e3e118d8b22c5d2942f972e28ebf0f0114d04f5/openapi/schemas.yaml"]["components"]["schemas"]["assetCode"]; - assetScale: external["https://raw.githubusercontent.com/interledger/open-payments/1e3e118d8b22c5d2942f972e28ebf0f0114d04f5/openapi/schemas.yaml"]["components"]["schemas"]["assetScale"]; + assetCode: external["https://raw.githubusercontent.com/interledger/open-payments/62c4b4a9875e3adaa21f89f597e88db43016fe0b/openapi/schemas.yaml"]["components"]["schemas"]["assetCode"]; + assetScale: external["https://raw.githubusercontent.com/interledger/open-payments/62c4b4a9875e3adaa21f89f597e88db43016fe0b/openapi/schemas.yaml"]["components"]["schemas"]["assetScale"]; }; /** * Asset code @@ -358,10 +358,10 @@ export interface external { * @description Open Payments specific property that defines the limits under which outgoing payments can be created. */ "limits-outgoing": Partial & { - receiver?: external["https://raw.githubusercontent.com/interledger/open-payments/1e3e118d8b22c5d2942f972e28ebf0f0114d04f5/openapi/schemas.yaml"]["components"]["schemas"]["receiver"]; - sendAmount?: external["https://raw.githubusercontent.com/interledger/open-payments/1e3e118d8b22c5d2942f972e28ebf0f0114d04f5/openapi/schemas.yaml"]["components"]["schemas"]["amount"]; - receiveAmount?: external["https://raw.githubusercontent.com/interledger/open-payments/1e3e118d8b22c5d2942f972e28ebf0f0114d04f5/openapi/schemas.yaml"]["components"]["schemas"]["amount"]; - interval?: external["https://raw.githubusercontent.com/interledger/open-payments/1e3e118d8b22c5d2942f972e28ebf0f0114d04f5/openapi/schemas.yaml"]["components"]["schemas"]["interval"]; + receiver?: external["https://raw.githubusercontent.com/interledger/open-payments/62c4b4a9875e3adaa21f89f597e88db43016fe0b/openapi/schemas.yaml"]["components"]["schemas"]["receiver"]; + sendAmount?: external["https://raw.githubusercontent.com/interledger/open-payments/62c4b4a9875e3adaa21f89f597e88db43016fe0b/openapi/schemas.yaml"]["components"]["schemas"]["amount"]; + receiveAmount?: external["https://raw.githubusercontent.com/interledger/open-payments/62c4b4a9875e3adaa21f89f597e88db43016fe0b/openapi/schemas.yaml"]["components"]["schemas"]["amount"]; + interval?: external["https://raw.githubusercontent.com/interledger/open-payments/62c4b4a9875e3adaa21f89f597e88db43016fe0b/openapi/schemas.yaml"]["components"]["schemas"]["interval"]; }; "list-actions": "list" | "list-all"; "read-actions": "read" | "read-all"; diff --git a/packages/open-payments/src/generated/resource-server-types.ts b/packages/open-payments/src/generated/resource-server-types.ts index ab1a4f3c7e..c7187d1bb5 100644 --- a/packages/open-payments/src/generated/resource-server-types.ts +++ b/packages/open-payments/src/generated/resource-server-types.ts @@ -118,8 +118,8 @@ export interface components { id: string; /** @description A public name for the account. This should be set by the account holder with their provider to provide a hint to counterparties as to the identity of the account holder. */ publicName?: string; - assetCode: external["https://raw.githubusercontent.com/interledger/open-payments/1e3e118d8b22c5d2942f972e28ebf0f0114d04f5/openapi/schemas.yaml"]["components"]["schemas"]["assetCode"]; - assetScale: external["https://raw.githubusercontent.com/interledger/open-payments/1e3e118d8b22c5d2942f972e28ebf0f0114d04f5/openapi/schemas.yaml"]["components"]["schemas"]["assetScale"]; + assetCode: external["https://raw.githubusercontent.com/interledger/open-payments/62c4b4a9875e3adaa21f89f597e88db43016fe0b/openapi/schemas.yaml"]["components"]["schemas"]["assetCode"]; + assetScale: external["https://raw.githubusercontent.com/interledger/open-payments/62c4b4a9875e3adaa21f89f597e88db43016fe0b/openapi/schemas.yaml"]["components"]["schemas"]["assetScale"]; /** * Format: uri * @description The URL of the authorization server endpoint for getting grants and access tokens for this payment pointer. @@ -147,8 +147,8 @@ export interface components { ilpAddress: string; /** @description The base64 url-encoded shared secret to use when establishing a STREAM connection. */ sharedSecret: string; - assetCode: external["https://raw.githubusercontent.com/interledger/open-payments/1e3e118d8b22c5d2942f972e28ebf0f0114d04f5/openapi/schemas.yaml"]["components"]["schemas"]["assetCode"]; - assetScale: external["https://raw.githubusercontent.com/interledger/open-payments/1e3e118d8b22c5d2942f972e28ebf0f0114d04f5/openapi/schemas.yaml"]["components"]["schemas"]["assetScale"]; + assetCode: external["https://raw.githubusercontent.com/interledger/open-payments/62c4b4a9875e3adaa21f89f597e88db43016fe0b/openapi/schemas.yaml"]["components"]["schemas"]["assetCode"]; + assetScale: external["https://raw.githubusercontent.com/interledger/open-payments/62c4b4a9875e3adaa21f89f597e88db43016fe0b/openapi/schemas.yaml"]["components"]["schemas"]["assetScale"]; }; /** * Incoming Payment @@ -168,9 +168,9 @@ export interface components { /** @description Describes whether the incoming payment has completed receiving fund. */ completed: boolean; /** @description The maximum amount that should be paid into the payment pointer under this incoming payment. */ - incomingAmount?: external["https://raw.githubusercontent.com/interledger/open-payments/1e3e118d8b22c5d2942f972e28ebf0f0114d04f5/openapi/schemas.yaml"]["components"]["schemas"]["amount"]; + incomingAmount?: external["https://raw.githubusercontent.com/interledger/open-payments/62c4b4a9875e3adaa21f89f597e88db43016fe0b/openapi/schemas.yaml"]["components"]["schemas"]["amount"]; /** @description The total amount that has been paid into the payment pointer under this incoming payment. */ - receivedAmount: external["https://raw.githubusercontent.com/interledger/open-payments/1e3e118d8b22c5d2942f972e28ebf0f0114d04f5/openapi/schemas.yaml"]["components"]["schemas"]["amount"]; + receivedAmount: external["https://raw.githubusercontent.com/interledger/open-payments/62c4b4a9875e3adaa21f89f597e88db43016fe0b/openapi/schemas.yaml"]["components"]["schemas"]["amount"]; /** * Format: date-time * @description The date and time when payments under this incoming payment will no longer be accepted. @@ -232,13 +232,13 @@ export interface components { /** @description Describes whether the payment failed to send its full amount. */ failed?: boolean; /** @description The URL of the incoming payment or ILP STREAM Connection that is being paid. */ - receiver: external["https://raw.githubusercontent.com/interledger/open-payments/1e3e118d8b22c5d2942f972e28ebf0f0114d04f5/openapi/schemas.yaml"]["components"]["schemas"]["receiver"]; + receiver: external["https://raw.githubusercontent.com/interledger/open-payments/62c4b4a9875e3adaa21f89f597e88db43016fe0b/openapi/schemas.yaml"]["components"]["schemas"]["receiver"]; /** @description The total amount that should be received by the receiver when this outgoing payment has been paid. */ - receiveAmount: external["https://raw.githubusercontent.com/interledger/open-payments/1e3e118d8b22c5d2942f972e28ebf0f0114d04f5/openapi/schemas.yaml"]["components"]["schemas"]["amount"]; + receiveAmount: external["https://raw.githubusercontent.com/interledger/open-payments/62c4b4a9875e3adaa21f89f597e88db43016fe0b/openapi/schemas.yaml"]["components"]["schemas"]["amount"]; /** @description The total amount that should be sent when this outgoing payment has been paid. */ - sendAmount: external["https://raw.githubusercontent.com/interledger/open-payments/1e3e118d8b22c5d2942f972e28ebf0f0114d04f5/openapi/schemas.yaml"]["components"]["schemas"]["amount"]; + sendAmount: external["https://raw.githubusercontent.com/interledger/open-payments/62c4b4a9875e3adaa21f89f597e88db43016fe0b/openapi/schemas.yaml"]["components"]["schemas"]["amount"]; /** @description The total amount that has been sent under this outgoing payment. */ - sentAmount: external["https://raw.githubusercontent.com/interledger/open-payments/1e3e118d8b22c5d2942f972e28ebf0f0114d04f5/openapi/schemas.yaml"]["components"]["schemas"]["amount"]; + sentAmount: external["https://raw.githubusercontent.com/interledger/open-payments/62c4b4a9875e3adaa21f89f597e88db43016fe0b/openapi/schemas.yaml"]["components"]["schemas"]["amount"]; /** @description Human readable description of the outgoing payment that will be visible to the account holder and shared with the receiver. */ description?: string; /** @description A reference that can be used by external systems to reconcile this payment with their systems. E.g. An invoice number. (Optional) */ @@ -269,9 +269,9 @@ export interface components { * @description The URL of the payment pointer from which this quote's payment would be sent. */ paymentPointer: string; - receiver: external["https://raw.githubusercontent.com/interledger/open-payments/1e3e118d8b22c5d2942f972e28ebf0f0114d04f5/openapi/schemas.yaml"]["components"]["schemas"]["receiver"]; - receiveAmount: external["https://raw.githubusercontent.com/interledger/open-payments/1e3e118d8b22c5d2942f972e28ebf0f0114d04f5/openapi/schemas.yaml"]["components"]["schemas"]["amount"]; - sendAmount: external["https://raw.githubusercontent.com/interledger/open-payments/1e3e118d8b22c5d2942f972e28ebf0f0114d04f5/openapi/schemas.yaml"]["components"]["schemas"]["amount"]; + receiver: external["https://raw.githubusercontent.com/interledger/open-payments/62c4b4a9875e3adaa21f89f597e88db43016fe0b/openapi/schemas.yaml"]["components"]["schemas"]["receiver"]; + receiveAmount: external["https://raw.githubusercontent.com/interledger/open-payments/62c4b4a9875e3adaa21f89f597e88db43016fe0b/openapi/schemas.yaml"]["components"]["schemas"]["amount"]; + sendAmount: external["https://raw.githubusercontent.com/interledger/open-payments/62c4b4a9875e3adaa21f89f597e88db43016fe0b/openapi/schemas.yaml"]["components"]["schemas"]["amount"]; /** @description The date and time when the calculated `sendAmount` is no longer valid. */ expiresAt?: string; /** @@ -458,7 +458,7 @@ export interface operations { content: { "application/json": { /** @description The maximum amount that should be paid into the payment pointer under this incoming payment. */ - incomingAmount?: external["https://raw.githubusercontent.com/interledger/open-payments/1e3e118d8b22c5d2942f972e28ebf0f0114d04f5/openapi/schemas.yaml"]["components"]["schemas"]["amount"]; + incomingAmount?: external["https://raw.githubusercontent.com/interledger/open-payments/62c4b4a9875e3adaa21f89f597e88db43016fe0b/openapi/schemas.yaml"]["components"]["schemas"]["amount"]; /** * Format: date-time * @description The date and time when payments into the incoming payment must no longer be accepted. @@ -571,9 +571,9 @@ export interface operations { requestBody: { content: { "application/json": { - receiver: external["https://raw.githubusercontent.com/interledger/open-payments/1e3e118d8b22c5d2942f972e28ebf0f0114d04f5/openapi/schemas.yaml"]["components"]["schemas"]["receiver"]; - receiveAmount?: external["https://raw.githubusercontent.com/interledger/open-payments/1e3e118d8b22c5d2942f972e28ebf0f0114d04f5/openapi/schemas.yaml"]["components"]["schemas"]["amount"]; - sendAmount?: external["https://raw.githubusercontent.com/interledger/open-payments/1e3e118d8b22c5d2942f972e28ebf0f0114d04f5/openapi/schemas.yaml"]["components"]["schemas"]["amount"]; + receiver: external["https://raw.githubusercontent.com/interledger/open-payments/62c4b4a9875e3adaa21f89f597e88db43016fe0b/openapi/schemas.yaml"]["components"]["schemas"]["receiver"]; + receiveAmount?: external["https://raw.githubusercontent.com/interledger/open-payments/62c4b4a9875e3adaa21f89f597e88db43016fe0b/openapi/schemas.yaml"]["components"]["schemas"]["amount"]; + sendAmount?: external["https://raw.githubusercontent.com/interledger/open-payments/62c4b4a9875e3adaa21f89f597e88db43016fe0b/openapi/schemas.yaml"]["components"]["schemas"]["amount"]; }; }; }; @@ -693,17 +693,17 @@ export interface operations { } export interface external { - "https://raw.githubusercontent.com/interledger/open-payments/1e3e118d8b22c5d2942f972e28ebf0f0114d04f5/openapi/schemas.yaml": { + "https://raw.githubusercontent.com/interledger/open-payments/62c4b4a9875e3adaa21f89f597e88db43016fe0b/openapi/schemas.yaml": { paths: {}; components: { schemas: { /** @description A description of the rights associated with this access token. */ - access: external["https://raw.githubusercontent.com/interledger/open-payments/1e3e118d8b22c5d2942f972e28ebf0f0114d04f5/openapi/schemas.yaml"]["components"]["schemas"]["access-item"][]; + access: external["https://raw.githubusercontent.com/interledger/open-payments/62c4b4a9875e3adaa21f89f597e88db43016fe0b/openapi/schemas.yaml"]["components"]["schemas"]["access-item"][]; /** @description The access associated with the access token is described using objects that each contain multiple dimensions of access. */ "access-item": - | external["https://raw.githubusercontent.com/interledger/open-payments/1e3e118d8b22c5d2942f972e28ebf0f0114d04f5/openapi/schemas.yaml"]["components"]["schemas"]["access-incoming"] - | external["https://raw.githubusercontent.com/interledger/open-payments/1e3e118d8b22c5d2942f972e28ebf0f0114d04f5/openapi/schemas.yaml"]["components"]["schemas"]["access-outgoing"] - | external["https://raw.githubusercontent.com/interledger/open-payments/1e3e118d8b22c5d2942f972e28ebf0f0114d04f5/openapi/schemas.yaml"]["components"]["schemas"]["access-quote"]; + | external["https://raw.githubusercontent.com/interledger/open-payments/62c4b4a9875e3adaa21f89f597e88db43016fe0b/openapi/schemas.yaml"]["components"]["schemas"]["access-incoming"] + | external["https://raw.githubusercontent.com/interledger/open-payments/62c4b4a9875e3adaa21f89f597e88db43016fe0b/openapi/schemas.yaml"]["components"]["schemas"]["access-outgoing"] + | external["https://raw.githubusercontent.com/interledger/open-payments/62c4b4a9875e3adaa21f89f597e88db43016fe0b/openapi/schemas.yaml"]["components"]["schemas"]["access-quote"]; /** access-incoming */ "access-incoming": { /** @description The type of resource request as a string. This field defines which other fields are allowed in the request object. */ @@ -734,7 +734,7 @@ export interface external { * @description A string identifier indicating a specific resource at the RS. */ identifier: string; - limits?: external["https://raw.githubusercontent.com/interledger/open-payments/1e3e118d8b22c5d2942f972e28ebf0f0114d04f5/openapi/schemas.yaml"]["components"]["schemas"]["limits-outgoing"]; + limits?: external["https://raw.githubusercontent.com/interledger/open-payments/62c4b4a9875e3adaa21f89f597e88db43016fe0b/openapi/schemas.yaml"]["components"]["schemas"]["limits-outgoing"]; }; /** access-quote */ "access-quote": { @@ -753,8 +753,8 @@ export interface external { * @description The value is an unsigned 64-bit integer amount, represented as a string. */ value: string; - assetCode: external["https://raw.githubusercontent.com/interledger/open-payments/1e3e118d8b22c5d2942f972e28ebf0f0114d04f5/openapi/schemas.yaml"]["components"]["schemas"]["assetCode"]; - assetScale: external["https://raw.githubusercontent.com/interledger/open-payments/1e3e118d8b22c5d2942f972e28ebf0f0114d04f5/openapi/schemas.yaml"]["components"]["schemas"]["assetScale"]; + assetCode: external["https://raw.githubusercontent.com/interledger/open-payments/62c4b4a9875e3adaa21f89f597e88db43016fe0b/openapi/schemas.yaml"]["components"]["schemas"]["assetCode"]; + assetScale: external["https://raw.githubusercontent.com/interledger/open-payments/62c4b4a9875e3adaa21f89f597e88db43016fe0b/openapi/schemas.yaml"]["components"]["schemas"]["assetScale"]; }; /** * Asset code @@ -807,10 +807,10 @@ export interface external { * @description Open Payments specific property that defines the limits under which outgoing payments can be created. */ "limits-outgoing": Partial & { - receiver?: external["https://raw.githubusercontent.com/interledger/open-payments/1e3e118d8b22c5d2942f972e28ebf0f0114d04f5/openapi/schemas.yaml"]["components"]["schemas"]["receiver"]; - sendAmount?: external["https://raw.githubusercontent.com/interledger/open-payments/1e3e118d8b22c5d2942f972e28ebf0f0114d04f5/openapi/schemas.yaml"]["components"]["schemas"]["amount"]; - receiveAmount?: external["https://raw.githubusercontent.com/interledger/open-payments/1e3e118d8b22c5d2942f972e28ebf0f0114d04f5/openapi/schemas.yaml"]["components"]["schemas"]["amount"]; - interval?: external["https://raw.githubusercontent.com/interledger/open-payments/1e3e118d8b22c5d2942f972e28ebf0f0114d04f5/openapi/schemas.yaml"]["components"]["schemas"]["interval"]; + receiver?: external["https://raw.githubusercontent.com/interledger/open-payments/62c4b4a9875e3adaa21f89f597e88db43016fe0b/openapi/schemas.yaml"]["components"]["schemas"]["receiver"]; + sendAmount?: external["https://raw.githubusercontent.com/interledger/open-payments/62c4b4a9875e3adaa21f89f597e88db43016fe0b/openapi/schemas.yaml"]["components"]["schemas"]["amount"]; + receiveAmount?: external["https://raw.githubusercontent.com/interledger/open-payments/62c4b4a9875e3adaa21f89f597e88db43016fe0b/openapi/schemas.yaml"]["components"]["schemas"]["amount"]; + interval?: external["https://raw.githubusercontent.com/interledger/open-payments/62c4b4a9875e3adaa21f89f597e88db43016fe0b/openapi/schemas.yaml"]["components"]["schemas"]["interval"]; }; "list-actions": "list" | "list-all"; "read-actions": "read" | "read-all"; From ac71e7cf8653ab79c157660c24703dfefba61c34 Mon Sep 17 00:00:00 2001 From: Max Kurapov Date: Mon, 7 Nov 2022 17:50:25 +0100 Subject: [PATCH 04/16] chore(open-payments): update AS request/response types --- packages/open-payments/src/types.ts | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/packages/open-payments/src/types.ts b/packages/open-payments/src/types.ts index f75969394e..741ccd6954 100644 --- a/packages/open-payments/src/types.ts +++ b/packages/open-payments/src/types.ts @@ -4,7 +4,8 @@ import { } from './generated/resource-server-types' import { components as ASComponents, - paths as ASPaths + paths as ASPaths, + operations as ASOperations } from './generated/authorization-server-types' export const getRSPath =

(path: P): string => @@ -18,17 +19,17 @@ export type PaymentPointer = RSComponents['schemas']['payment-pointer'] export const getASPath =

(path: P): string => path as string export type NonInteractiveGrantRequest = { - accessToken: ASComponents['schemas']['access_token'] - client: ASComponents['schemas']['client'] + access_token: ASOperations['post-request']['requestBody']['content']['application/json']['access_token'] + client: ASOperations['post-request']['requestBody']['content']['application/json']['client'] } export type NonInteractiveGrant = { - accessToken: ASComponents['schemas']['access_token'] + access_token: ASComponents['schemas']['access_token'] continue: ASComponents['schemas']['continue'] } export type InteractiveGrantRequest = { - accessToken: ASComponents['schemas']['access_token'] - client: ASComponents['schemas']['client'] - interact: ASComponents['schemas']['interact-request'] + access_token: ASOperations['post-request']['requestBody']['content']['application/json']['access_token'] + client: ASOperations['post-request']['requestBody']['content']['application/json']['client'] + interact: ASOperations['post-request']['requestBody']['content']['application/json']['interact'] } export type InteractiveGrant = { interact: ASComponents['schemas']['interact-response'] From f0febf013873e976d1298bd9d749ac630f83966c Mon Sep 17 00:00:00 2001 From: Max Kurapov Date: Mon, 7 Nov 2022 17:57:04 +0100 Subject: [PATCH 05/16] chore(open-payments): rename AS generated types file --- packages/open-payments/scripts/generate-types.ts | 2 +- .../{authorization-server-types.ts => auth-server-types.ts} | 0 packages/open-payments/src/types.ts | 2 +- 3 files changed, 2 insertions(+), 2 deletions(-) rename packages/open-payments/src/generated/{authorization-server-types.ts => auth-server-types.ts} (100%) diff --git a/packages/open-payments/scripts/generate-types.ts b/packages/open-payments/scripts/generate-types.ts index 632eade117..608f7d6916 100644 --- a/packages/open-payments/scripts/generate-types.ts +++ b/packages/open-payments/scripts/generate-types.ts @@ -25,7 +25,7 @@ const generateTypesFromOpenApi = async ( ) await generateTypesFromOpenApi( config.OPEN_PAYMENTS_AS_OPEN_API_URL, - `${rootFolder}/authorization-server-types.ts` + `${rootFolder}/auth-server-types.ts` ) } catch (error) { console.log('Error when generating types', { diff --git a/packages/open-payments/src/generated/authorization-server-types.ts b/packages/open-payments/src/generated/auth-server-types.ts similarity index 100% rename from packages/open-payments/src/generated/authorization-server-types.ts rename to packages/open-payments/src/generated/auth-server-types.ts diff --git a/packages/open-payments/src/types.ts b/packages/open-payments/src/types.ts index 741ccd6954..504119fb60 100644 --- a/packages/open-payments/src/types.ts +++ b/packages/open-payments/src/types.ts @@ -6,7 +6,7 @@ import { components as ASComponents, paths as ASPaths, operations as ASOperations -} from './generated/authorization-server-types' +} from './generated/auth-server-types' export const getRSPath =

(path: P): string => path as string From 5aa53702450e8d9f412a68fc7cb7eb517991e321 Mon Sep 17 00:00:00 2001 From: Max Kurapov Date: Thu, 17 Nov 2022 00:03:12 +0100 Subject: [PATCH 06/16] chore(open-payments): merge bw-httpsig --- packages/backend/src/app.ts | 4 +- packages/backend/src/config/app.ts | 1 + packages/backend/src/index.ts | 9 +- .../open_payments/receiver/service.test.ts | 4 +- .../src/open_payments/receiver/service.ts | 4 +- packages/open-payments/package.json | 1 + .../src/client/ilp-stream-connection.ts | 6 +- .../src/client/incoming-payment.ts | 10 +-- packages/open-payments/src/client/index.ts | 85 ++++++++++++++++--- .../src/client/payment-pointer.ts | 6 +- .../open-payments/src/client/requests.test.ts | 61 ++++++++++--- packages/open-payments/src/client/requests.ts | 31 ++++++- .../open-payments/src/client/signatures.ts | 52 ++++++++++++ packages/open-payments/src/index.ts | 8 +- packages/open-payments/src/test/helpers.ts | 9 +- pnpm-lock.yaml | 6 ++ 16 files changed, 248 insertions(+), 49 deletions(-) create mode 100644 packages/open-payments/src/client/signatures.ts diff --git a/packages/backend/src/app.ts b/packages/backend/src/app.ts index 1b2f8cb005..27865a6727 100644 --- a/packages/backend/src/app.ts +++ b/packages/backend/src/app.ts @@ -49,7 +49,7 @@ import { Session } from './session/util' import { createValidatorMiddleware, HttpMethod, isHttpMethod } from 'openapi' import { PaymentPointerKeyService } from './paymentPointerKey/service' import { GrantReferenceService } from './open_payments/grantReference/service' -import { OpenPaymentsClient } from 'open-payments' +import { AuthenticatedClient } from 'open-payments' export interface AppContextData { logger: Logger @@ -147,7 +147,7 @@ export interface AppServices { sessionService: Promise paymentPointerKeyService: Promise grantReferenceService: Promise - openPaymentsClient: Promise + openPaymentsClient: Promise } export type AppContainer = IocContract diff --git a/packages/backend/src/config/app.ts b/packages/backend/src/config/app.ts index 4eb4390490..cf67c2d904 100644 --- a/packages/backend/src/config/app.ts +++ b/packages/backend/src/config/app.ts @@ -112,6 +112,7 @@ export const Config = { 'AUTH_SERVER_SPEC', 'https://raw.githubusercontent.com/interledger/open-payments/77462cd0872be8d0fa487a4b233defe2897a7ee4/auth-server-open-api-spec.yaml' ), + keyId: envString('KEY_ID', 'rafiki'), privateKey: parseOrProvisionKey(envString('PRIVATE_KEY_FILE', undefined)), /** Frontend **/ diff --git a/packages/backend/src/index.ts b/packages/backend/src/index.ts index 920a5aab39..b27ca1820d 100644 --- a/packages/backend/src/index.ts +++ b/packages/backend/src/index.ts @@ -37,7 +37,7 @@ import { createConnectorService } from './connector' import { createSessionService } from './session/service' import { createApiKeyService } from './apiKey/service' import { createOpenAPI } from 'openapi' -import { createClient as createOpenPaymentsClient } from 'open-payments' +import { createAuthenticatedClient as createOpenPaymentsClient } from 'open-payments' import { createConnectionService } from './open_payments/connection/service' import { createConnectionRoutes } from './open_payments/connection/routes' import { createPaymentPointerKeyService } from './paymentPointerKey/service' @@ -115,8 +115,13 @@ export function initIocContainer( return await createOpenAPI(config.authServerSpec) }) container.singleton('openPaymentsClient', async (deps) => { + const config = await deps.use('config') const logger = await deps.use('logger') - return createOpenPaymentsClient({ logger }) + return createOpenPaymentsClient({ + logger, + keyId: config.keyId, + privateKey: config.privateKey + }) }) /** diff --git a/packages/backend/src/open_payments/receiver/service.test.ts b/packages/backend/src/open_payments/receiver/service.test.ts index 03781651b2..3cdb2922fa 100644 --- a/packages/backend/src/open_payments/receiver/service.test.ts +++ b/packages/backend/src/open_payments/receiver/service.test.ts @@ -12,14 +12,14 @@ import { createIncomingPayment } from '../../tests/incomingPayment' import { createPaymentPointer } from '../../tests/paymentPointer' import { truncateTables } from '../../tests/tableManager' import { ConnectionService } from '../connection/service' -import { OpenPaymentsClient } from 'open-payments' +import { AuthenticatedClient } from 'open-payments' import { PaymentPointerService } from '../payment_pointer/service' describe('Receiver Service', (): void => { let deps: IocContract let appContainer: TestContainer let receiverService: ReceiverService - let openPaymentsClient: OpenPaymentsClient + let openPaymentsClient: AuthenticatedClient let knex: Knex let connectionService: ConnectionService let paymentPointerService: PaymentPointerService diff --git a/packages/backend/src/open_payments/receiver/service.ts b/packages/backend/src/open_payments/receiver/service.ts index 491cb0a551..99fb9dfff4 100644 --- a/packages/backend/src/open_payments/receiver/service.ts +++ b/packages/backend/src/open_payments/receiver/service.ts @@ -1,5 +1,5 @@ import { - OpenPaymentsClient, + AuthenticatedClient, IncomingPayment as OpenPaymentsIncomingPayment, ILPStreamConnection as OpenPaymentsConnection } from 'open-payments' @@ -22,7 +22,7 @@ interface ServiceDependencies extends BaseService { incomingPaymentService: IncomingPaymentService openPaymentsUrl: string paymentPointerService: PaymentPointerService - openPaymentsClient: OpenPaymentsClient + openPaymentsClient: AuthenticatedClient } const CONNECTION_URL_REGEX = /\/connections\/(.){36}$/ diff --git a/packages/open-payments/package.json b/packages/open-payments/package.json index e67fe7d8e0..fdcfc77397 100644 --- a/packages/open-payments/package.json +++ b/packages/open-payments/package.json @@ -25,6 +25,7 @@ }, "dependencies": { "axios": "^1.1.2", + "http-message-signatures": "^0.1.2", "openapi": "workspace:../openapi", "pino": "^8.4.2" } diff --git a/packages/open-payments/src/client/ilp-stream-connection.ts b/packages/open-payments/src/client/ilp-stream-connection.ts index b12e7e84e2..6227022128 100644 --- a/packages/open-payments/src/client/ilp-stream-connection.ts +++ b/packages/open-payments/src/client/ilp-stream-connection.ts @@ -1,5 +1,5 @@ import { HttpMethod } from 'openapi' -import { ClientDeps } from '.' +import { RouteDeps } from '.' import { getRSPath, ILPStreamConnection } from '../types' import { get } from './requests' @@ -12,9 +12,9 @@ export interface ILPStreamConnectionRoutes { } export const createILPStreamConnectionRoutes = ( - clientDeps: ClientDeps + deps: RouteDeps ): ILPStreamConnectionRoutes => { - const { axiosInstance, openApi, logger } = clientDeps + const { axiosInstance, openApi, logger } = deps const getILPStreamConnectionValidator = openApi.createResponseValidator({ diff --git a/packages/open-payments/src/client/incoming-payment.ts b/packages/open-payments/src/client/incoming-payment.ts index f0a75564d9..4d42222b65 100644 --- a/packages/open-payments/src/client/incoming-payment.ts +++ b/packages/open-payments/src/client/incoming-payment.ts @@ -1,5 +1,5 @@ import { HttpMethod, ResponseValidator } from 'openapi' -import { ClientDeps } from '.' +import { RouteDeps } from '.' import { IncomingPayment, getRSPath } from '../types' import { get } from './requests' @@ -13,9 +13,9 @@ export interface IncomingPaymentRoutes { } export const createIncomingPaymentRoutes = ( - clientDeps: ClientDeps + deps: RouteDeps ): IncomingPaymentRoutes => { - const { axiosInstance, openApi, logger } = clientDeps + const { axiosInstance, openApi, logger } = deps const getIncomingPaymentOpenApiValidator = openApi.createResponseValidator({ @@ -34,11 +34,11 @@ export const createIncomingPaymentRoutes = ( } export const getIncomingPayment = async ( - clientDeps: Pick, + deps: Pick, args: GetArgs, validateOpenApiResponse: ResponseValidator ) => { - const { axiosInstance, logger } = clientDeps + const { axiosInstance, logger } = deps const { url } = args const incomingPayment = await get( diff --git a/packages/open-payments/src/client/index.ts b/packages/open-payments/src/client/index.ts index 4e3d42d1f0..64ba815dcb 100644 --- a/packages/open-payments/src/client/index.ts +++ b/packages/open-payments/src/client/index.ts @@ -1,3 +1,4 @@ +import { KeyLike } from 'crypto' import { createOpenAPI, OpenAPI } from 'openapi' import createLogger, { Logger } from 'pino' import config from '../config' @@ -17,28 +18,25 @@ import { createAxiosInstance } from './requests' import { AxiosInstance } from 'axios' import { createGrantRoutes, GrantRoutes } from './grant' -export interface CreateOpenPaymentClientArgs { - requestTimeoutMs?: number - logger?: Logger +interface ClientDeps { + axiosInstance: AxiosInstance + resourceServerOpenApi: OpenAPI + authorizationServerOpenApi: OpenAPI + logger: Logger } -export interface ClientDeps { +export interface RouteDeps { axiosInstance: AxiosInstance openApi: OpenAPI logger: Logger } -export interface OpenPaymentsClient { - incomingPayment: IncomingPaymentRoutes - ilpStreamConnection: ILPStreamConnectionRoutes - paymentPointer: PaymentPointerRoutes - grant: GrantRoutes -} - -export const createClient = async ( - args?: CreateOpenPaymentClientArgs -): Promise => { +const createDeps = async ( + args: Partial +): Promise => { const axiosInstance = createAxiosInstance({ + privateKey: args.privateKey, + keyId: args.keyId, requestTimeoutMs: args?.requestTimeoutMs ?? config.DEFAULT_REQUEST_TIMEOUT_MS }) @@ -49,6 +47,65 @@ export const createClient = async ( config.OPEN_PAYMENTS_AS_OPEN_API_URL ) const logger = args?.logger ?? createLogger() + return { + axiosInstance, + resourceServerOpenApi, + authorizationServerOpenApi, + logger + } +} + +export interface CreateUnauthenticatedClientArgs { + requestTimeoutMs?: number + logger?: Logger +} + +export interface UnauthenticatedClient { + ilpStreamConnection: ILPStreamConnectionRoutes + paymentPointer: PaymentPointerRoutes +} + +export const createUnauthenticatedClient = async ( + args: CreateUnauthenticatedClientArgs +): Promise => { + const { axiosInstance, resourceServerOpenApi, logger } = await createDeps( + args + ) + + return { + ilpStreamConnection: createILPStreamConnectionRoutes({ + axiosInstance, + openApi: resourceServerOpenApi, + logger + }), + paymentPointer: createPaymentPointerRoutes({ + axiosInstance, + openApi: resourceServerOpenApi, + logger + }) + } +} + +export interface CreateAuthenticatedClientArgs + extends CreateUnauthenticatedClientArgs { + privateKey: KeyLike + keyId: string +} + +export interface AuthenticatedClient extends UnauthenticatedClient { + incomingPayment: IncomingPaymentRoutes + grant: GrantRoutes +} + +export const createAuthenticatedClient = async ( + args: CreateAuthenticatedClientArgs +): Promise => { + const { + axiosInstance, + resourceServerOpenApi, + authorizationServerOpenApi, + logger + } = await createDeps(args) return { incomingPayment: createIncomingPaymentRoutes({ diff --git a/packages/open-payments/src/client/payment-pointer.ts b/packages/open-payments/src/client/payment-pointer.ts index d7ddd20346..b5b080055a 100644 --- a/packages/open-payments/src/client/payment-pointer.ts +++ b/packages/open-payments/src/client/payment-pointer.ts @@ -1,5 +1,5 @@ import { HttpMethod } from 'openapi' -import { ClientDeps } from '.' +import { RouteDeps } from '.' import { PaymentPointer, getRSPath } from '../types' import { get } from './requests' @@ -12,9 +12,9 @@ export interface PaymentPointerRoutes { } export const createPaymentPointerRoutes = ( - clientDeps: ClientDeps + deps: RouteDeps ): PaymentPointerRoutes => { - const { axiosInstance, openApi, logger } = clientDeps + const { axiosInstance, openApi, logger } = deps const getPaymentPaymentValidator = openApi.createResponseValidator({ diff --git a/packages/open-payments/src/client/requests.test.ts b/packages/open-payments/src/client/requests.test.ts index b8332490c9..e7f45e61ef 100644 --- a/packages/open-payments/src/client/requests.test.ts +++ b/packages/open-payments/src/client/requests.test.ts @@ -1,28 +1,35 @@ /* eslint-disable @typescript-eslint/no-empty-function */ import { createAxiosInstance, get, post } from './requests' +import { generateKeyPairSync } from 'crypto' import nock from 'nock' import { mockOpenApiResponseValidators, silentLogger } from '../test/helpers' describe('requests', (): void => { const logger = silentLogger + const privateKey = generateKeyPairSync('ed25519').privateKey + const keyId = 'myId' describe('createAxiosInstance', (): void => { test('sets timeout properly', async (): Promise => { expect( - createAxiosInstance({ requestTimeoutMs: 1000 }).defaults.timeout + createAxiosInstance({ requestTimeoutMs: 1000, privateKey, keyId }) + .defaults.timeout ).toBe(1000) }) test('sets Content-Type header properly', async (): Promise => { expect( - createAxiosInstance({ requestTimeoutMs: 0 }).defaults.headers.common[ - 'Content-Type' - ] + createAxiosInstance({ requestTimeoutMs: 0, privateKey, keyId }).defaults + .headers.common['Content-Type'] ).toBe('application/json') }) }) describe('get', (): void => { - const axiosInstance = createAxiosInstance({ requestTimeoutMs: 0 }) + const axiosInstance = createAxiosInstance({ + requestTimeoutMs: 0, + privateKey, + keyId + }) const baseUrl = 'http://localhost:1000' const responseValidators = mockOpenApiResponseValidators() @@ -30,8 +37,37 @@ describe('requests', (): void => { jest.spyOn(axiosInstance, 'get') }) + afterEach(() => { + jest.useRealTimers() + }) + test('sets headers properly if accessToken provided', async (): Promise => { - nock(baseUrl).get('/incoming-payment').reply(200) + // https://github.com/nock/nock/issues/2200#issuecomment-1280957462 + jest + .useFakeTimers({ + doNotFake: [ + 'nextTick', + 'setImmediate', + 'clearImmediate', + 'setInterval', + 'clearInterval', + 'setTimeout', + 'clearTimeout' + ] + }) + .setSystemTime(new Date()) + + const scope = nock(baseUrl) + .matchHeader('Signature', /sig1=:([a-zA-Z0-9+/]){86}==:/) + .matchHeader( + 'Signature-Input', + `sig1=("@method" "@target-uri" "authorization");created=${Math.floor( + Date.now() / 1000 + )};keyid="${keyId}";alg="ed25519"` + ) + .get('/incoming-payment') + // TODO: verify signature + .reply(200) await get( { axiosInstance, logger }, @@ -42,20 +78,24 @@ describe('requests', (): void => { responseValidators.successfulValidator ) + scope.done() + expect(axiosInstance.get).toHaveBeenCalledWith( `${baseUrl}/incoming-payment`, { headers: { - Authorization: 'GNAP accessToken', - Signature: 'TODO', - 'Signature-Input': 'TODO' + Authorization: 'GNAP accessToken' } } ) }) test('sets headers properly if accessToken is not provided', async (): Promise => { - nock(baseUrl).get('/incoming-payment').reply(200) + const scope = nock(baseUrl) + .matchHeader('Signature', (sig) => sig === undefined) + .matchHeader('Signature-Input', (sigInput) => sigInput === undefined) + .get('/incoming-payment') + .reply(200) await get( { axiosInstance, logger }, @@ -64,6 +104,7 @@ describe('requests', (): void => { }, responseValidators.successfulValidator ) + scope.done() expect(axiosInstance.get).toHaveBeenCalledWith( `${baseUrl}/incoming-payment`, diff --git a/packages/open-payments/src/client/requests.ts b/packages/open-payments/src/client/requests.ts index 0fdecd04c7..cc2291d799 100644 --- a/packages/open-payments/src/client/requests.ts +++ b/packages/open-payments/src/client/requests.ts @@ -1,6 +1,8 @@ import axios, { AxiosInstance } from 'axios' +import { KeyLike } from 'crypto' import { ResponseValidator } from 'openapi' import { ClientDeps } from '.' +import { createSignatureHeaders } from './signatures' interface GetArgs { url: string @@ -30,9 +32,7 @@ export const get = async ( const { data, status } = await axiosInstance.get(url, { headers: accessToken ? { - Authorization: `GNAP ${accessToken}`, - Signature: 'TODO', - 'Signature-Input': 'TODO' + Authorization: `GNAP ${accessToken}` } : {} }) @@ -117,12 +117,35 @@ export const post = async ( export const createAxiosInstance = (args: { requestTimeoutMs: number + privateKey: KeyLike + keyId: string }): AxiosInstance => { const axiosInstance = axios.create({ timeout: args.requestTimeoutMs }) - axiosInstance.defaults.headers.common['Content-Type'] = 'application/json' + axiosInstance.interceptors.request.use( + async (config) => { + const sigHeaders = await createSignatureHeaders({ + request: { + method: config.method.toUpperCase(), + url: config.url, + headers: config.headers, + body: config.data + }, + privateKey: args.privateKey, + keyId: args.keyId + }) + config.headers['Signature'] = sigHeaders['Signature'] + config.headers['Signature-Input'] = sigHeaders['Signature-Input'] + return config + }, + null, + { + runWhen: (config) => !!config.headers['Authorization'] + } + ) + return axiosInstance } diff --git a/packages/open-payments/src/client/signatures.ts b/packages/open-payments/src/client/signatures.ts new file mode 100644 index 0000000000..85fb0bad41 --- /dev/null +++ b/packages/open-payments/src/client/signatures.ts @@ -0,0 +1,52 @@ +import { sign, KeyLike } from 'crypto' +import { + httpis as httpsig, + Algorithm, + RequestLike, + Signer +} from 'http-message-signatures' + +interface SignOptions { + request: RequestLike + privateKey: KeyLike + keyId: string +} + +interface SignatureHeaders { + Signature: string + 'Signature-Input': string +} + +const createSigner = (privateKey: KeyLike): Signer => { + const signer = async (data: Buffer) => sign(null, data, privateKey) + signer.alg = 'ed25519' as Algorithm + return signer +} + +export const createSignatureHeaders = async ({ + request, + privateKey, + keyId +}: SignOptions): Promise => { + const components = ['@method', '@target-uri'] + if (request.headers['Authorization']) { + components.push('authorization') + } + if (request.body) { + // TODO: 'content-digest' + components.push('content-length', 'content-type') + } + const { headers } = await httpsig.sign(request, { + components, + parameters: { + created: Math.floor(Date.now() / 1000) + }, + keyId, + signer: createSigner(privateKey), + format: 'httpbis' + }) + return { + Signature: headers['Signature'] as string, + 'Signature-Input': headers['Signature-Input'] as string + } +} diff --git a/packages/open-payments/src/index.ts b/packages/open-payments/src/index.ts index baa01d97eb..4f7cb830f6 100644 --- a/packages/open-payments/src/index.ts +++ b/packages/open-payments/src/index.ts @@ -4,4 +4,10 @@ export { InteractiveGrant, NonInteractiveGrant } from './types' -export { createClient, OpenPaymentsClient } from './client' + +export { + createAuthenticatedClient, + createUnauthenticatedClient, + AuthenticatedClient, + UnauthenticatedClient +} from './client' diff --git a/packages/open-payments/src/test/helpers.ts b/packages/open-payments/src/test/helpers.ts index 8afadd20bb..113e3b1507 100644 --- a/packages/open-payments/src/test/helpers.ts +++ b/packages/open-payments/src/test/helpers.ts @@ -1,3 +1,4 @@ +import { generateKeyPairSync } from 'crypto' import createLogger from 'pino' import { createAxiosInstance } from '../client/requests' import { ILPStreamConnection, IncomingPayment } from '../types' @@ -9,7 +10,13 @@ export const silentLogger = createLogger({ level: 'silent' }) -export const defaultAxiosInstance = createAxiosInstance({ requestTimeoutMs: 0 }) +export const keyId = 'default-key-id' + +export const defaultAxiosInstance = createAxiosInstance({ + requestTimeoutMs: 0, + keyId, + privateKey: generateKeyPairSync('ed25519').privateKey +}) export const mockOpenApiResponseValidators = () => ({ successfulValidator: ((data: unknown): data is unknown => diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index d73da29fa0..b54a40bd74 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -312,6 +312,7 @@ importers: '@types/node': ^18.7.12 axios: ^1.1.2 base64url: ^3.0.1 + http-message-signatures: ^0.1.2 nock: ^13.2.9 openapi: workspace:../openapi openapi-typescript: ^4.5.0 @@ -321,6 +322,7 @@ importers: uuid: ^8.3.2 dependencies: axios: 1.1.2 + http-message-signatures: 0.1.2 openapi: link:../openapi pino: 8.4.2 devDependencies: @@ -8034,6 +8036,10 @@ packages: statuses: 2.0.1 toidentifier: 1.0.1 + /http-message-signatures/0.1.2: + resolution: {integrity: sha512-gjJYDgFBy+xnlAs2G0gIWpiorCv9Xi7pIlOnnd91zHAK7BtkLxonmm/JAtd5e6CakOuW03IwEuJzj2YMy8lfWQ==} + dev: false + /http-proxy-agent/5.0.0: resolution: {integrity: sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==} engines: {node: '>= 6'} From ca946c8b6e024e0b1d93082292ff19dcbfcbeaaf Mon Sep 17 00:00:00 2001 From: Max Kurapov Date: Thu, 17 Nov 2022 00:19:05 +0100 Subject: [PATCH 07/16] chore(open-payments): update types, fix tests --- packages/open-payments/src/client/grant.ts | 6 +++--- packages/open-payments/src/client/index.ts | 9 ++++++--- packages/open-payments/src/client/requests.test.ts | 6 +++++- packages/open-payments/src/client/requests.ts | 10 +++++----- 4 files changed, 19 insertions(+), 12 deletions(-) diff --git a/packages/open-payments/src/client/grant.ts b/packages/open-payments/src/client/grant.ts index 72d49833d2..0f09972d87 100644 --- a/packages/open-payments/src/client/grant.ts +++ b/packages/open-payments/src/client/grant.ts @@ -1,5 +1,5 @@ import { HttpMethod } from 'openapi' -import { ClientDeps } from '.' +import { RouteDeps } from '.' import { getASPath, InteractiveGrant, @@ -23,8 +23,8 @@ export interface GrantRoutes { ): Promise } -export const createGrantRoutes = (clientDeps: ClientDeps): GrantRoutes => { - const { axiosInstance, openApi, logger } = clientDeps +export const createGrantRoutes = (deps: RouteDeps): GrantRoutes => { + const { axiosInstance, openApi, logger } = deps const createInteractiveGrantValidator = openApi.createResponseValidator({ diff --git a/packages/open-payments/src/client/index.ts b/packages/open-payments/src/client/index.ts index 64ba815dcb..57fe1caac1 100644 --- a/packages/open-payments/src/client/index.ts +++ b/packages/open-payments/src/client/index.ts @@ -18,14 +18,17 @@ import { createAxiosInstance } from './requests' import { AxiosInstance } from 'axios' import { createGrantRoutes, GrantRoutes } from './grant' -interface ClientDeps { +export interface BaseDeps { axiosInstance: AxiosInstance + logger: Logger +} + +interface ClientDeps extends BaseDeps { resourceServerOpenApi: OpenAPI authorizationServerOpenApi: OpenAPI - logger: Logger } -export interface RouteDeps { +export interface RouteDeps extends BaseDeps { axiosInstance: AxiosInstance openApi: OpenAPI logger: Logger diff --git a/packages/open-payments/src/client/requests.test.ts b/packages/open-payments/src/client/requests.test.ts index e7f45e61ef..fffb97fda1 100644 --- a/packages/open-payments/src/client/requests.test.ts +++ b/packages/open-payments/src/client/requests.test.ts @@ -157,7 +157,11 @@ describe('requests', (): void => { }) describe('post', (): void => { - const axiosInstance = createAxiosInstance({ requestTimeoutMs: 0 }) + const axiosInstance = createAxiosInstance({ + requestTimeoutMs: 0, + privateKey, + keyId + }) const baseUrl = 'http://localhost:1000' const responseValidators = mockOpenApiResponseValidators() diff --git a/packages/open-payments/src/client/requests.ts b/packages/open-payments/src/client/requests.ts index cc2291d799..4247cc75b0 100644 --- a/packages/open-payments/src/client/requests.ts +++ b/packages/open-payments/src/client/requests.ts @@ -1,7 +1,7 @@ import axios, { AxiosInstance } from 'axios' import { KeyLike } from 'crypto' import { ResponseValidator } from 'openapi' -import { ClientDeps } from '.' +import { BaseDeps } from '.' import { createSignatureHeaders } from './signatures' interface GetArgs { @@ -14,11 +14,11 @@ interface PostArgs { } export const get = async ( - clientDeps: Pick, + deps: BaseDeps, args: GetArgs, openApiResponseValidator: ResponseValidator ): Promise => { - const { axiosInstance, logger } = clientDeps + const { axiosInstance, logger } = deps const { accessToken } = args const requestUrl = new URL(args.url) @@ -68,11 +68,11 @@ export const get = async ( } export const post = async ( - clientDeps: Pick, + deps: BaseDeps, args: PostArgs, openApiResponseValidator: ResponseValidator ): Promise => { - const { axiosInstance, logger } = clientDeps + const { axiosInstance, logger } = deps const { body } = args const requestUrl = new URL(args.url) From 0a453d5030ba1873a7aa67a8d6c5a933f87667c9 Mon Sep 17 00:00:00 2001 From: Max Kurapov Date: Thu, 17 Nov 2022 00:35:42 +0100 Subject: [PATCH 08/16] chore(open-payments): update open api specs --- packages/open-payments/src/config.ts | 4 +- .../src/generated/auth-server-types.ts | 30 +++---- .../src/generated/resource-server-types.ts | 78 +++++++++---------- 3 files changed, 56 insertions(+), 56 deletions(-) diff --git a/packages/open-payments/src/config.ts b/packages/open-payments/src/config.ts index a5932ff7b6..feb66e15ea 100644 --- a/packages/open-payments/src/config.ts +++ b/packages/open-payments/src/config.ts @@ -1,7 +1,7 @@ export default { OPEN_PAYMENTS_RS_OPEN_API_URL: - 'https://raw.githubusercontent.com/interledger/open-payments/62c4b4a9875e3adaa21f89f597e88db43016fe0b/openapi/resource-server.yaml', + 'https://raw.githubusercontent.com/interledger/open-payments/0523804eeb21d9b8cd2eb65e4119738a0512a095/openapi/resource-server.yaml', OPEN_PAYMENTS_AS_OPEN_API_URL: - 'https://raw.githubusercontent.com/interledger/open-payments/62c4b4a9875e3adaa21f89f597e88db43016fe0b/openapi/auth-server.yaml', + 'https://raw.githubusercontent.com/interledger/open-payments/0523804eeb21d9b8cd2eb65e4119738a0512a095/openapi/auth-server.yaml', DEFAULT_REQUEST_TIMEOUT_MS: 5_000 } diff --git a/packages/open-payments/src/generated/auth-server-types.ts b/packages/open-payments/src/generated/auth-server-types.ts index 4dc991fb30..3537d91c5b 100644 --- a/packages/open-payments/src/generated/auth-server-types.ts +++ b/packages/open-payments/src/generated/auth-server-types.ts @@ -52,7 +52,7 @@ export interface components { client_id?: string; /** @description The public key of the client instance to be used in this request or a reference to a key. */ key?: - | external["https://raw.githubusercontent.com/interledger/open-payments/62c4b4a9875e3adaa21f89f597e88db43016fe0b/openapi/schemas.yaml"]["components"]["schemas"]["key"] + | external["https://raw.githubusercontent.com/interledger/open-payments/0523804eeb21d9b8cd2eb65e4119738a0512a095/openapi/schemas.yaml"]["components"]["schemas"]["key"] | string; }; /** @@ -116,7 +116,7 @@ export interface components { manage: string; /** @description The number of seconds in which the access will expire. The client instance MUST NOT use the access token past this time. An RS MUST NOT accept an access token past this time. */ expires_in?: number; - access: external["https://raw.githubusercontent.com/interledger/open-payments/62c4b4a9875e3adaa21f89f597e88db43016fe0b/openapi/schemas.yaml"]["components"]["schemas"]["access"]; + access: external["https://raw.githubusercontent.com/interledger/open-payments/0523804eeb21d9b8cd2eb65e4119738a0512a095/openapi/schemas.yaml"]["components"]["schemas"]["access"]; }; }; } @@ -147,7 +147,7 @@ export interface operations { content: { "application/json": { access_token: { - access: external["https://raw.githubusercontent.com/interledger/open-payments/62c4b4a9875e3adaa21f89f597e88db43016fe0b/openapi/schemas.yaml"]["components"]["schemas"]["access"]; + access: external["https://raw.githubusercontent.com/interledger/open-payments/0523804eeb21d9b8cd2eb65e4119738a0512a095/openapi/schemas.yaml"]["components"]["schemas"]["access"]; }; client: components["schemas"]["client"]; interact?: components["schemas"]["interact-request"]; @@ -244,17 +244,17 @@ export interface operations { } export interface external { - "https://raw.githubusercontent.com/interledger/open-payments/62c4b4a9875e3adaa21f89f597e88db43016fe0b/openapi/schemas.yaml": { + "https://raw.githubusercontent.com/interledger/open-payments/0523804eeb21d9b8cd2eb65e4119738a0512a095/openapi/schemas.yaml": { paths: {}; components: { schemas: { /** @description A description of the rights associated with this access token. */ - access: external["https://raw.githubusercontent.com/interledger/open-payments/62c4b4a9875e3adaa21f89f597e88db43016fe0b/openapi/schemas.yaml"]["components"]["schemas"]["access-item"][]; + access: external["https://raw.githubusercontent.com/interledger/open-payments/0523804eeb21d9b8cd2eb65e4119738a0512a095/openapi/schemas.yaml"]["components"]["schemas"]["access-item"][]; /** @description The access associated with the access token is described using objects that each contain multiple dimensions of access. */ "access-item": - | external["https://raw.githubusercontent.com/interledger/open-payments/62c4b4a9875e3adaa21f89f597e88db43016fe0b/openapi/schemas.yaml"]["components"]["schemas"]["access-incoming"] - | external["https://raw.githubusercontent.com/interledger/open-payments/62c4b4a9875e3adaa21f89f597e88db43016fe0b/openapi/schemas.yaml"]["components"]["schemas"]["access-outgoing"] - | external["https://raw.githubusercontent.com/interledger/open-payments/62c4b4a9875e3adaa21f89f597e88db43016fe0b/openapi/schemas.yaml"]["components"]["schemas"]["access-quote"]; + | external["https://raw.githubusercontent.com/interledger/open-payments/0523804eeb21d9b8cd2eb65e4119738a0512a095/openapi/schemas.yaml"]["components"]["schemas"]["access-incoming"] + | external["https://raw.githubusercontent.com/interledger/open-payments/0523804eeb21d9b8cd2eb65e4119738a0512a095/openapi/schemas.yaml"]["components"]["schemas"]["access-outgoing"] + | external["https://raw.githubusercontent.com/interledger/open-payments/0523804eeb21d9b8cd2eb65e4119738a0512a095/openapi/schemas.yaml"]["components"]["schemas"]["access-quote"]; /** access-incoming */ "access-incoming": { /** @description The type of resource request as a string. This field defines which other fields are allowed in the request object. */ @@ -285,7 +285,7 @@ export interface external { * @description A string identifier indicating a specific resource at the RS. */ identifier: string; - limits?: external["https://raw.githubusercontent.com/interledger/open-payments/62c4b4a9875e3adaa21f89f597e88db43016fe0b/openapi/schemas.yaml"]["components"]["schemas"]["limits-outgoing"]; + limits?: external["https://raw.githubusercontent.com/interledger/open-payments/0523804eeb21d9b8cd2eb65e4119738a0512a095/openapi/schemas.yaml"]["components"]["schemas"]["limits-outgoing"]; }; /** access-quote */ "access-quote": { @@ -304,8 +304,8 @@ export interface external { * @description The value is an unsigned 64-bit integer amount, represented as a string. */ value: string; - assetCode: external["https://raw.githubusercontent.com/interledger/open-payments/62c4b4a9875e3adaa21f89f597e88db43016fe0b/openapi/schemas.yaml"]["components"]["schemas"]["assetCode"]; - assetScale: external["https://raw.githubusercontent.com/interledger/open-payments/62c4b4a9875e3adaa21f89f597e88db43016fe0b/openapi/schemas.yaml"]["components"]["schemas"]["assetScale"]; + assetCode: external["https://raw.githubusercontent.com/interledger/open-payments/0523804eeb21d9b8cd2eb65e4119738a0512a095/openapi/schemas.yaml"]["components"]["schemas"]["assetCode"]; + assetScale: external["https://raw.githubusercontent.com/interledger/open-payments/0523804eeb21d9b8cd2eb65e4119738a0512a095/openapi/schemas.yaml"]["components"]["schemas"]["assetScale"]; }; /** * Asset code @@ -358,10 +358,10 @@ export interface external { * @description Open Payments specific property that defines the limits under which outgoing payments can be created. */ "limits-outgoing": Partial & { - receiver?: external["https://raw.githubusercontent.com/interledger/open-payments/62c4b4a9875e3adaa21f89f597e88db43016fe0b/openapi/schemas.yaml"]["components"]["schemas"]["receiver"]; - sendAmount?: external["https://raw.githubusercontent.com/interledger/open-payments/62c4b4a9875e3adaa21f89f597e88db43016fe0b/openapi/schemas.yaml"]["components"]["schemas"]["amount"]; - receiveAmount?: external["https://raw.githubusercontent.com/interledger/open-payments/62c4b4a9875e3adaa21f89f597e88db43016fe0b/openapi/schemas.yaml"]["components"]["schemas"]["amount"]; - interval?: external["https://raw.githubusercontent.com/interledger/open-payments/62c4b4a9875e3adaa21f89f597e88db43016fe0b/openapi/schemas.yaml"]["components"]["schemas"]["interval"]; + receiver?: external["https://raw.githubusercontent.com/interledger/open-payments/0523804eeb21d9b8cd2eb65e4119738a0512a095/openapi/schemas.yaml"]["components"]["schemas"]["receiver"]; + sendAmount?: external["https://raw.githubusercontent.com/interledger/open-payments/0523804eeb21d9b8cd2eb65e4119738a0512a095/openapi/schemas.yaml"]["components"]["schemas"]["amount"]; + receiveAmount?: external["https://raw.githubusercontent.com/interledger/open-payments/0523804eeb21d9b8cd2eb65e4119738a0512a095/openapi/schemas.yaml"]["components"]["schemas"]["amount"]; + interval?: external["https://raw.githubusercontent.com/interledger/open-payments/0523804eeb21d9b8cd2eb65e4119738a0512a095/openapi/schemas.yaml"]["components"]["schemas"]["interval"]; }; "list-actions": "list" | "list-all"; "read-actions": "read" | "read-all"; diff --git a/packages/open-payments/src/generated/resource-server-types.ts b/packages/open-payments/src/generated/resource-server-types.ts index c7187d1bb5..9ae90da8f5 100644 --- a/packages/open-payments/src/generated/resource-server-types.ts +++ b/packages/open-payments/src/generated/resource-server-types.ts @@ -118,8 +118,8 @@ export interface components { id: string; /** @description A public name for the account. This should be set by the account holder with their provider to provide a hint to counterparties as to the identity of the account holder. */ publicName?: string; - assetCode: external["https://raw.githubusercontent.com/interledger/open-payments/62c4b4a9875e3adaa21f89f597e88db43016fe0b/openapi/schemas.yaml"]["components"]["schemas"]["assetCode"]; - assetScale: external["https://raw.githubusercontent.com/interledger/open-payments/62c4b4a9875e3adaa21f89f597e88db43016fe0b/openapi/schemas.yaml"]["components"]["schemas"]["assetScale"]; + assetCode: external["https://raw.githubusercontent.com/interledger/open-payments/0523804eeb21d9b8cd2eb65e4119738a0512a095/openapi/schemas.yaml"]["components"]["schemas"]["assetCode"]; + assetScale: external["https://raw.githubusercontent.com/interledger/open-payments/0523804eeb21d9b8cd2eb65e4119738a0512a095/openapi/schemas.yaml"]["components"]["schemas"]["assetScale"]; /** * Format: uri * @description The URL of the authorization server endpoint for getting grants and access tokens for this payment pointer. @@ -147,8 +147,8 @@ export interface components { ilpAddress: string; /** @description The base64 url-encoded shared secret to use when establishing a STREAM connection. */ sharedSecret: string; - assetCode: external["https://raw.githubusercontent.com/interledger/open-payments/62c4b4a9875e3adaa21f89f597e88db43016fe0b/openapi/schemas.yaml"]["components"]["schemas"]["assetCode"]; - assetScale: external["https://raw.githubusercontent.com/interledger/open-payments/62c4b4a9875e3adaa21f89f597e88db43016fe0b/openapi/schemas.yaml"]["components"]["schemas"]["assetScale"]; + assetCode: external["https://raw.githubusercontent.com/interledger/open-payments/0523804eeb21d9b8cd2eb65e4119738a0512a095/openapi/schemas.yaml"]["components"]["schemas"]["assetCode"]; + assetScale: external["https://raw.githubusercontent.com/interledger/open-payments/0523804eeb21d9b8cd2eb65e4119738a0512a095/openapi/schemas.yaml"]["components"]["schemas"]["assetScale"]; }; /** * Incoming Payment @@ -168,9 +168,9 @@ export interface components { /** @description Describes whether the incoming payment has completed receiving fund. */ completed: boolean; /** @description The maximum amount that should be paid into the payment pointer under this incoming payment. */ - incomingAmount?: external["https://raw.githubusercontent.com/interledger/open-payments/62c4b4a9875e3adaa21f89f597e88db43016fe0b/openapi/schemas.yaml"]["components"]["schemas"]["amount"]; + incomingAmount?: external["https://raw.githubusercontent.com/interledger/open-payments/0523804eeb21d9b8cd2eb65e4119738a0512a095/openapi/schemas.yaml"]["components"]["schemas"]["amount"]; /** @description The total amount that has been paid into the payment pointer under this incoming payment. */ - receivedAmount: external["https://raw.githubusercontent.com/interledger/open-payments/62c4b4a9875e3adaa21f89f597e88db43016fe0b/openapi/schemas.yaml"]["components"]["schemas"]["amount"]; + receivedAmount: external["https://raw.githubusercontent.com/interledger/open-payments/0523804eeb21d9b8cd2eb65e4119738a0512a095/openapi/schemas.yaml"]["components"]["schemas"]["amount"]; /** * Format: date-time * @description The date and time when payments under this incoming payment will no longer be accepted. @@ -232,13 +232,13 @@ export interface components { /** @description Describes whether the payment failed to send its full amount. */ failed?: boolean; /** @description The URL of the incoming payment or ILP STREAM Connection that is being paid. */ - receiver: external["https://raw.githubusercontent.com/interledger/open-payments/62c4b4a9875e3adaa21f89f597e88db43016fe0b/openapi/schemas.yaml"]["components"]["schemas"]["receiver"]; + receiver: external["https://raw.githubusercontent.com/interledger/open-payments/0523804eeb21d9b8cd2eb65e4119738a0512a095/openapi/schemas.yaml"]["components"]["schemas"]["receiver"]; /** @description The total amount that should be received by the receiver when this outgoing payment has been paid. */ - receiveAmount: external["https://raw.githubusercontent.com/interledger/open-payments/62c4b4a9875e3adaa21f89f597e88db43016fe0b/openapi/schemas.yaml"]["components"]["schemas"]["amount"]; + receiveAmount: external["https://raw.githubusercontent.com/interledger/open-payments/0523804eeb21d9b8cd2eb65e4119738a0512a095/openapi/schemas.yaml"]["components"]["schemas"]["amount"]; /** @description The total amount that should be sent when this outgoing payment has been paid. */ - sendAmount: external["https://raw.githubusercontent.com/interledger/open-payments/62c4b4a9875e3adaa21f89f597e88db43016fe0b/openapi/schemas.yaml"]["components"]["schemas"]["amount"]; + sendAmount: external["https://raw.githubusercontent.com/interledger/open-payments/0523804eeb21d9b8cd2eb65e4119738a0512a095/openapi/schemas.yaml"]["components"]["schemas"]["amount"]; /** @description The total amount that has been sent under this outgoing payment. */ - sentAmount: external["https://raw.githubusercontent.com/interledger/open-payments/62c4b4a9875e3adaa21f89f597e88db43016fe0b/openapi/schemas.yaml"]["components"]["schemas"]["amount"]; + sentAmount: external["https://raw.githubusercontent.com/interledger/open-payments/0523804eeb21d9b8cd2eb65e4119738a0512a095/openapi/schemas.yaml"]["components"]["schemas"]["amount"]; /** @description Human readable description of the outgoing payment that will be visible to the account holder and shared with the receiver. */ description?: string; /** @description A reference that can be used by external systems to reconcile this payment with their systems. E.g. An invoice number. (Optional) */ @@ -269,9 +269,9 @@ export interface components { * @description The URL of the payment pointer from which this quote's payment would be sent. */ paymentPointer: string; - receiver: external["https://raw.githubusercontent.com/interledger/open-payments/62c4b4a9875e3adaa21f89f597e88db43016fe0b/openapi/schemas.yaml"]["components"]["schemas"]["receiver"]; - receiveAmount: external["https://raw.githubusercontent.com/interledger/open-payments/62c4b4a9875e3adaa21f89f597e88db43016fe0b/openapi/schemas.yaml"]["components"]["schemas"]["amount"]; - sendAmount: external["https://raw.githubusercontent.com/interledger/open-payments/62c4b4a9875e3adaa21f89f597e88db43016fe0b/openapi/schemas.yaml"]["components"]["schemas"]["amount"]; + receiver: external["https://raw.githubusercontent.com/interledger/open-payments/0523804eeb21d9b8cd2eb65e4119738a0512a095/openapi/schemas.yaml"]["components"]["schemas"]["receiver"]; + receiveAmount: external["https://raw.githubusercontent.com/interledger/open-payments/0523804eeb21d9b8cd2eb65e4119738a0512a095/openapi/schemas.yaml"]["components"]["schemas"]["amount"]; + sendAmount: external["https://raw.githubusercontent.com/interledger/open-payments/0523804eeb21d9b8cd2eb65e4119738a0512a095/openapi/schemas.yaml"]["components"]["schemas"]["amount"]; /** @description The date and time when the calculated `sendAmount` is no longer valid. */ expiresAt?: string; /** @@ -332,7 +332,7 @@ export interface components { id: string; /** @description The signature generated based on the Signature-Input, using the signing algorithm specified in the "alg" field of the JWK. */ signature: string; - /** @description The Signature-Input field is a Dictionary structured field containing the metadata for one or more message signatures generated from components within the HTTP message. Each member describes a single message signature. The member's key is the label that uniquely identifies the message signature within the context of the HTTP message. The member's value is the serialization of the covered components Inner List plus all signature metadata parameters identified by the label. The following components MUST be included: - "@method" - "@target-uri" - "authorization" When the message contains a request body, the covered components MUST also include the following: - "content-digest" The keyid parameter of the signature MUST be set to the kid value of the JWK. See [ietf-httpbis-message-signatures](https://datatracker.ietf.org/doc/html/draft-ietf-httpbis-message-signatures#section-4.1) for more details. */ + /** @description The Signature-Input field is a Dictionary structured field containing the metadata for one or more message signatures generated from components within the HTTP message. Each member describes a single message signature. The member's key is the label that uniquely identifies the message signature within the context of the HTTP message. The member's value is the serialization of the covered components Inner List plus all signature metadata parameters identified by the label. The following components MUST be included: - "@method" - "@target-uri" - "authorization". When the message contains a request body, the covered components MUST also include the following: - "content-digest" The keyid parameter of the signature MUST be set to the kid value of the JWK. See [ietf-httpbis-message-signatures](https://datatracker.ietf.org/doc/html/draft-ietf-httpbis-message-signatures#section-4.1) for more details. */ "signature-input": string; }; } @@ -405,7 +405,7 @@ export interface operations { pagination?: components["schemas"]["pagination"]; }; header: { - /** The Signature-Input field is a Dictionary structured field containing the metadata for one or more message signatures generated from components within the HTTP message. Each member describes a single message signature. The member's key is the label that uniquely identifies the message signature within the context of the HTTP message. The member's value is the serialization of the covered components Inner List plus all signature metadata parameters identified by the label. The following components MUST be included: - "@method" - "@target-uri" - "authorization" When the message contains a request body, the covered components MUST also include the following: - "content-digest" The keyid parameter of the signature MUST be set to the kid value of the JWK. See [ietf-httpbis-message-signatures](https://datatracker.ietf.org/doc/html/draft-ietf-httpbis-message-signatures#section-4.1) for more details. */ + /** The Signature-Input field is a Dictionary structured field containing the metadata for one or more message signatures generated from components within the HTTP message. Each member describes a single message signature. The member's key is the label that uniquely identifies the message signature within the context of the HTTP message. The member's value is the serialization of the covered components Inner List plus all signature metadata parameters identified by the label. The following components MUST be included: - "@method" - "@target-uri" - "authorization". When the message contains a request body, the covered components MUST also include the following: - "content-digest" The keyid parameter of the signature MUST be set to the kid value of the JWK. See [ietf-httpbis-message-signatures](https://datatracker.ietf.org/doc/html/draft-ietf-httpbis-message-signatures#section-4.1) for more details. */ "Signature-Input": components["parameters"]["signature-input"]; /** The signature generated based on the Signature-Input, using the signing algorithm specified in the "alg" field of the JWK. */ Signature: components["parameters"]["signature"]; @@ -433,7 +433,7 @@ export interface operations { "create-incoming-payment": { parameters: { header: { - /** The Signature-Input field is a Dictionary structured field containing the metadata for one or more message signatures generated from components within the HTTP message. Each member describes a single message signature. The member's key is the label that uniquely identifies the message signature within the context of the HTTP message. The member's value is the serialization of the covered components Inner List plus all signature metadata parameters identified by the label. The following components MUST be included: - "@method" - "@target-uri" - "authorization" When the message contains a request body, the covered components MUST also include the following: - "content-digest" The keyid parameter of the signature MUST be set to the kid value of the JWK. See [ietf-httpbis-message-signatures](https://datatracker.ietf.org/doc/html/draft-ietf-httpbis-message-signatures#section-4.1) for more details. */ + /** The Signature-Input field is a Dictionary structured field containing the metadata for one or more message signatures generated from components within the HTTP message. Each member describes a single message signature. The member's key is the label that uniquely identifies the message signature within the context of the HTTP message. The member's value is the serialization of the covered components Inner List plus all signature metadata parameters identified by the label. The following components MUST be included: - "@method" - "@target-uri" - "authorization". When the message contains a request body, the covered components MUST also include the following: - "content-digest" The keyid parameter of the signature MUST be set to the kid value of the JWK. See [ietf-httpbis-message-signatures](https://datatracker.ietf.org/doc/html/draft-ietf-httpbis-message-signatures#section-4.1) for more details. */ "Signature-Input": components["parameters"]["signature-input"]; /** The signature generated based on the Signature-Input, using the signing algorithm specified in the "alg" field of the JWK. */ Signature: components["parameters"]["signature"]; @@ -458,7 +458,7 @@ export interface operations { content: { "application/json": { /** @description The maximum amount that should be paid into the payment pointer under this incoming payment. */ - incomingAmount?: external["https://raw.githubusercontent.com/interledger/open-payments/62c4b4a9875e3adaa21f89f597e88db43016fe0b/openapi/schemas.yaml"]["components"]["schemas"]["amount"]; + incomingAmount?: external["https://raw.githubusercontent.com/interledger/open-payments/0523804eeb21d9b8cd2eb65e4119738a0512a095/openapi/schemas.yaml"]["components"]["schemas"]["amount"]; /** * Format: date-time * @description The date and time when payments into the incoming payment must no longer be accepted. @@ -480,7 +480,7 @@ export interface operations { pagination?: components["schemas"]["pagination"]; }; header: { - /** The Signature-Input field is a Dictionary structured field containing the metadata for one or more message signatures generated from components within the HTTP message. Each member describes a single message signature. The member's key is the label that uniquely identifies the message signature within the context of the HTTP message. The member's value is the serialization of the covered components Inner List plus all signature metadata parameters identified by the label. The following components MUST be included: - "@method" - "@target-uri" - "authorization" When the message contains a request body, the covered components MUST also include the following: - "content-digest" The keyid parameter of the signature MUST be set to the kid value of the JWK. See [ietf-httpbis-message-signatures](https://datatracker.ietf.org/doc/html/draft-ietf-httpbis-message-signatures#section-4.1) for more details. */ + /** The Signature-Input field is a Dictionary structured field containing the metadata for one or more message signatures generated from components within the HTTP message. Each member describes a single message signature. The member's key is the label that uniquely identifies the message signature within the context of the HTTP message. The member's value is the serialization of the covered components Inner List plus all signature metadata parameters identified by the label. The following components MUST be included: - "@method" - "@target-uri" - "authorization". When the message contains a request body, the covered components MUST also include the following: - "content-digest" The keyid parameter of the signature MUST be set to the kid value of the JWK. See [ietf-httpbis-message-signatures](https://datatracker.ietf.org/doc/html/draft-ietf-httpbis-message-signatures#section-4.1) for more details. */ "Signature-Input": components["parameters"]["signature-input"]; /** The signature generated based on the Signature-Input, using the signing algorithm specified in the "alg" field of the JWK. */ Signature: components["parameters"]["signature"]; @@ -504,7 +504,7 @@ export interface operations { "create-outgoing-payment": { parameters: { header: { - /** The Signature-Input field is a Dictionary structured field containing the metadata for one or more message signatures generated from components within the HTTP message. Each member describes a single message signature. The member's key is the label that uniquely identifies the message signature within the context of the HTTP message. The member's value is the serialization of the covered components Inner List plus all signature metadata parameters identified by the label. The following components MUST be included: - "@method" - "@target-uri" - "authorization" When the message contains a request body, the covered components MUST also include the following: - "content-digest" The keyid parameter of the signature MUST be set to the kid value of the JWK. See [ietf-httpbis-message-signatures](https://datatracker.ietf.org/doc/html/draft-ietf-httpbis-message-signatures#section-4.1) for more details. */ + /** The Signature-Input field is a Dictionary structured field containing the metadata for one or more message signatures generated from components within the HTTP message. Each member describes a single message signature. The member's key is the label that uniquely identifies the message signature within the context of the HTTP message. The member's value is the serialization of the covered components Inner List plus all signature metadata parameters identified by the label. The following components MUST be included: - "@method" - "@target-uri" - "authorization". When the message contains a request body, the covered components MUST also include the following: - "content-digest" The keyid parameter of the signature MUST be set to the kid value of the JWK. See [ietf-httpbis-message-signatures](https://datatracker.ietf.org/doc/html/draft-ietf-httpbis-message-signatures#section-4.1) for more details. */ "Signature-Input": components["parameters"]["signature-input"]; /** The signature generated based on the Signature-Input, using the signing algorithm specified in the "alg" field of the JWK. */ Signature: components["parameters"]["signature"]; @@ -545,7 +545,7 @@ export interface operations { "create-quote": { parameters: { header: { - /** The Signature-Input field is a Dictionary structured field containing the metadata for one or more message signatures generated from components within the HTTP message. Each member describes a single message signature. The member's key is the label that uniquely identifies the message signature within the context of the HTTP message. The member's value is the serialization of the covered components Inner List plus all signature metadata parameters identified by the label. The following components MUST be included: - "@method" - "@target-uri" - "authorization" When the message contains a request body, the covered components MUST also include the following: - "content-digest" The keyid parameter of the signature MUST be set to the kid value of the JWK. See [ietf-httpbis-message-signatures](https://datatracker.ietf.org/doc/html/draft-ietf-httpbis-message-signatures#section-4.1) for more details. */ + /** The Signature-Input field is a Dictionary structured field containing the metadata for one or more message signatures generated from components within the HTTP message. Each member describes a single message signature. The member's key is the label that uniquely identifies the message signature within the context of the HTTP message. The member's value is the serialization of the covered components Inner List plus all signature metadata parameters identified by the label. The following components MUST be included: - "@method" - "@target-uri" - "authorization". When the message contains a request body, the covered components MUST also include the following: - "content-digest" The keyid parameter of the signature MUST be set to the kid value of the JWK. See [ietf-httpbis-message-signatures](https://datatracker.ietf.org/doc/html/draft-ietf-httpbis-message-signatures#section-4.1) for more details. */ "Signature-Input": components["parameters"]["signature-input"]; /** The signature generated based on the Signature-Input, using the signing algorithm specified in the "alg" field of the JWK. */ Signature: components["parameters"]["signature"]; @@ -571,9 +571,9 @@ export interface operations { requestBody: { content: { "application/json": { - receiver: external["https://raw.githubusercontent.com/interledger/open-payments/62c4b4a9875e3adaa21f89f597e88db43016fe0b/openapi/schemas.yaml"]["components"]["schemas"]["receiver"]; - receiveAmount?: external["https://raw.githubusercontent.com/interledger/open-payments/62c4b4a9875e3adaa21f89f597e88db43016fe0b/openapi/schemas.yaml"]["components"]["schemas"]["amount"]; - sendAmount?: external["https://raw.githubusercontent.com/interledger/open-payments/62c4b4a9875e3adaa21f89f597e88db43016fe0b/openapi/schemas.yaml"]["components"]["schemas"]["amount"]; + receiver: external["https://raw.githubusercontent.com/interledger/open-payments/0523804eeb21d9b8cd2eb65e4119738a0512a095/openapi/schemas.yaml"]["components"]["schemas"]["receiver"]; + receiveAmount?: external["https://raw.githubusercontent.com/interledger/open-payments/0523804eeb21d9b8cd2eb65e4119738a0512a095/openapi/schemas.yaml"]["components"]["schemas"]["amount"]; + sendAmount?: external["https://raw.githubusercontent.com/interledger/open-payments/0523804eeb21d9b8cd2eb65e4119738a0512a095/openapi/schemas.yaml"]["components"]["schemas"]["amount"]; }; }; }; @@ -586,7 +586,7 @@ export interface operations { id: components["parameters"]["id"]; }; header: { - /** The Signature-Input field is a Dictionary structured field containing the metadata for one or more message signatures generated from components within the HTTP message. Each member describes a single message signature. The member's key is the label that uniquely identifies the message signature within the context of the HTTP message. The member's value is the serialization of the covered components Inner List plus all signature metadata parameters identified by the label. The following components MUST be included: - "@method" - "@target-uri" - "authorization" When the message contains a request body, the covered components MUST also include the following: - "content-digest" The keyid parameter of the signature MUST be set to the kid value of the JWK. See [ietf-httpbis-message-signatures](https://datatracker.ietf.org/doc/html/draft-ietf-httpbis-message-signatures#section-4.1) for more details. */ + /** The Signature-Input field is a Dictionary structured field containing the metadata for one or more message signatures generated from components within the HTTP message. Each member describes a single message signature. The member's key is the label that uniquely identifies the message signature within the context of the HTTP message. The member's value is the serialization of the covered components Inner List plus all signature metadata parameters identified by the label. The following components MUST be included: - "@method" - "@target-uri" - "authorization". When the message contains a request body, the covered components MUST also include the following: - "content-digest" The keyid parameter of the signature MUST be set to the kid value of the JWK. See [ietf-httpbis-message-signatures](https://datatracker.ietf.org/doc/html/draft-ietf-httpbis-message-signatures#section-4.1) for more details. */ "Signature-Input": components["parameters"]["signature-input"]; /** The signature generated based on the Signature-Input, using the signing algorithm specified in the "alg" field of the JWK. */ Signature: components["parameters"]["signature"]; @@ -617,7 +617,7 @@ export interface operations { id: components["parameters"]["id"]; }; header: { - /** The Signature-Input field is a Dictionary structured field containing the metadata for one or more message signatures generated from components within the HTTP message. Each member describes a single message signature. The member's key is the label that uniquely identifies the message signature within the context of the HTTP message. The member's value is the serialization of the covered components Inner List plus all signature metadata parameters identified by the label. The following components MUST be included: - "@method" - "@target-uri" - "authorization" When the message contains a request body, the covered components MUST also include the following: - "content-digest" The keyid parameter of the signature MUST be set to the kid value of the JWK. See [ietf-httpbis-message-signatures](https://datatracker.ietf.org/doc/html/draft-ietf-httpbis-message-signatures#section-4.1) for more details. */ + /** The Signature-Input field is a Dictionary structured field containing the metadata for one or more message signatures generated from components within the HTTP message. Each member describes a single message signature. The member's key is the label that uniquely identifies the message signature within the context of the HTTP message. The member's value is the serialization of the covered components Inner List plus all signature metadata parameters identified by the label. The following components MUST be included: - "@method" - "@target-uri" - "authorization". When the message contains a request body, the covered components MUST also include the following: - "content-digest" The keyid parameter of the signature MUST be set to the kid value of the JWK. See [ietf-httpbis-message-signatures](https://datatracker.ietf.org/doc/html/draft-ietf-httpbis-message-signatures#section-4.1) for more details. */ "Signature-Input": components["parameters"]["signature-input"]; /** The signature generated based on the Signature-Input, using the signing algorithm specified in the "alg" field of the JWK. */ Signature: components["parameters"]["signature"]; @@ -644,7 +644,7 @@ export interface operations { id: components["parameters"]["id"]; }; header: { - /** The Signature-Input field is a Dictionary structured field containing the metadata for one or more message signatures generated from components within the HTTP message. Each member describes a single message signature. The member's key is the label that uniquely identifies the message signature within the context of the HTTP message. The member's value is the serialization of the covered components Inner List plus all signature metadata parameters identified by the label. The following components MUST be included: - "@method" - "@target-uri" - "authorization" When the message contains a request body, the covered components MUST also include the following: - "content-digest" The keyid parameter of the signature MUST be set to the kid value of the JWK. See [ietf-httpbis-message-signatures](https://datatracker.ietf.org/doc/html/draft-ietf-httpbis-message-signatures#section-4.1) for more details. */ + /** The Signature-Input field is a Dictionary structured field containing the metadata for one or more message signatures generated from components within the HTTP message. Each member describes a single message signature. The member's key is the label that uniquely identifies the message signature within the context of the HTTP message. The member's value is the serialization of the covered components Inner List plus all signature metadata parameters identified by the label. The following components MUST be included: - "@method" - "@target-uri" - "authorization". When the message contains a request body, the covered components MUST also include the following: - "content-digest" The keyid parameter of the signature MUST be set to the kid value of the JWK. See [ietf-httpbis-message-signatures](https://datatracker.ietf.org/doc/html/draft-ietf-httpbis-message-signatures#section-4.1) for more details. */ "Signature-Input": components["parameters"]["signature-input"]; /** The signature generated based on the Signature-Input, using the signing algorithm specified in the "alg" field of the JWK. */ Signature: components["parameters"]["signature"]; @@ -671,7 +671,7 @@ export interface operations { id: components["parameters"]["id"]; }; header: { - /** The Signature-Input field is a Dictionary structured field containing the metadata for one or more message signatures generated from components within the HTTP message. Each member describes a single message signature. The member's key is the label that uniquely identifies the message signature within the context of the HTTP message. The member's value is the serialization of the covered components Inner List plus all signature metadata parameters identified by the label. The following components MUST be included: - "@method" - "@target-uri" - "authorization" When the message contains a request body, the covered components MUST also include the following: - "content-digest" The keyid parameter of the signature MUST be set to the kid value of the JWK. See [ietf-httpbis-message-signatures](https://datatracker.ietf.org/doc/html/draft-ietf-httpbis-message-signatures#section-4.1) for more details. */ + /** The Signature-Input field is a Dictionary structured field containing the metadata for one or more message signatures generated from components within the HTTP message. Each member describes a single message signature. The member's key is the label that uniquely identifies the message signature within the context of the HTTP message. The member's value is the serialization of the covered components Inner List plus all signature metadata parameters identified by the label. The following components MUST be included: - "@method" - "@target-uri" - "authorization". When the message contains a request body, the covered components MUST also include the following: - "content-digest" The keyid parameter of the signature MUST be set to the kid value of the JWK. See [ietf-httpbis-message-signatures](https://datatracker.ietf.org/doc/html/draft-ietf-httpbis-message-signatures#section-4.1) for more details. */ "Signature-Input": components["parameters"]["signature-input"]; /** The signature generated based on the Signature-Input, using the signing algorithm specified in the "alg" field of the JWK. */ Signature: components["parameters"]["signature"]; @@ -693,17 +693,17 @@ export interface operations { } export interface external { - "https://raw.githubusercontent.com/interledger/open-payments/62c4b4a9875e3adaa21f89f597e88db43016fe0b/openapi/schemas.yaml": { + "https://raw.githubusercontent.com/interledger/open-payments/0523804eeb21d9b8cd2eb65e4119738a0512a095/openapi/schemas.yaml": { paths: {}; components: { schemas: { /** @description A description of the rights associated with this access token. */ - access: external["https://raw.githubusercontent.com/interledger/open-payments/62c4b4a9875e3adaa21f89f597e88db43016fe0b/openapi/schemas.yaml"]["components"]["schemas"]["access-item"][]; + access: external["https://raw.githubusercontent.com/interledger/open-payments/0523804eeb21d9b8cd2eb65e4119738a0512a095/openapi/schemas.yaml"]["components"]["schemas"]["access-item"][]; /** @description The access associated with the access token is described using objects that each contain multiple dimensions of access. */ "access-item": - | external["https://raw.githubusercontent.com/interledger/open-payments/62c4b4a9875e3adaa21f89f597e88db43016fe0b/openapi/schemas.yaml"]["components"]["schemas"]["access-incoming"] - | external["https://raw.githubusercontent.com/interledger/open-payments/62c4b4a9875e3adaa21f89f597e88db43016fe0b/openapi/schemas.yaml"]["components"]["schemas"]["access-outgoing"] - | external["https://raw.githubusercontent.com/interledger/open-payments/62c4b4a9875e3adaa21f89f597e88db43016fe0b/openapi/schemas.yaml"]["components"]["schemas"]["access-quote"]; + | external["https://raw.githubusercontent.com/interledger/open-payments/0523804eeb21d9b8cd2eb65e4119738a0512a095/openapi/schemas.yaml"]["components"]["schemas"]["access-incoming"] + | external["https://raw.githubusercontent.com/interledger/open-payments/0523804eeb21d9b8cd2eb65e4119738a0512a095/openapi/schemas.yaml"]["components"]["schemas"]["access-outgoing"] + | external["https://raw.githubusercontent.com/interledger/open-payments/0523804eeb21d9b8cd2eb65e4119738a0512a095/openapi/schemas.yaml"]["components"]["schemas"]["access-quote"]; /** access-incoming */ "access-incoming": { /** @description The type of resource request as a string. This field defines which other fields are allowed in the request object. */ @@ -734,7 +734,7 @@ export interface external { * @description A string identifier indicating a specific resource at the RS. */ identifier: string; - limits?: external["https://raw.githubusercontent.com/interledger/open-payments/62c4b4a9875e3adaa21f89f597e88db43016fe0b/openapi/schemas.yaml"]["components"]["schemas"]["limits-outgoing"]; + limits?: external["https://raw.githubusercontent.com/interledger/open-payments/0523804eeb21d9b8cd2eb65e4119738a0512a095/openapi/schemas.yaml"]["components"]["schemas"]["limits-outgoing"]; }; /** access-quote */ "access-quote": { @@ -753,8 +753,8 @@ export interface external { * @description The value is an unsigned 64-bit integer amount, represented as a string. */ value: string; - assetCode: external["https://raw.githubusercontent.com/interledger/open-payments/62c4b4a9875e3adaa21f89f597e88db43016fe0b/openapi/schemas.yaml"]["components"]["schemas"]["assetCode"]; - assetScale: external["https://raw.githubusercontent.com/interledger/open-payments/62c4b4a9875e3adaa21f89f597e88db43016fe0b/openapi/schemas.yaml"]["components"]["schemas"]["assetScale"]; + assetCode: external["https://raw.githubusercontent.com/interledger/open-payments/0523804eeb21d9b8cd2eb65e4119738a0512a095/openapi/schemas.yaml"]["components"]["schemas"]["assetCode"]; + assetScale: external["https://raw.githubusercontent.com/interledger/open-payments/0523804eeb21d9b8cd2eb65e4119738a0512a095/openapi/schemas.yaml"]["components"]["schemas"]["assetScale"]; }; /** * Asset code @@ -807,10 +807,10 @@ export interface external { * @description Open Payments specific property that defines the limits under which outgoing payments can be created. */ "limits-outgoing": Partial & { - receiver?: external["https://raw.githubusercontent.com/interledger/open-payments/62c4b4a9875e3adaa21f89f597e88db43016fe0b/openapi/schemas.yaml"]["components"]["schemas"]["receiver"]; - sendAmount?: external["https://raw.githubusercontent.com/interledger/open-payments/62c4b4a9875e3adaa21f89f597e88db43016fe0b/openapi/schemas.yaml"]["components"]["schemas"]["amount"]; - receiveAmount?: external["https://raw.githubusercontent.com/interledger/open-payments/62c4b4a9875e3adaa21f89f597e88db43016fe0b/openapi/schemas.yaml"]["components"]["schemas"]["amount"]; - interval?: external["https://raw.githubusercontent.com/interledger/open-payments/62c4b4a9875e3adaa21f89f597e88db43016fe0b/openapi/schemas.yaml"]["components"]["schemas"]["interval"]; + receiver?: external["https://raw.githubusercontent.com/interledger/open-payments/0523804eeb21d9b8cd2eb65e4119738a0512a095/openapi/schemas.yaml"]["components"]["schemas"]["receiver"]; + sendAmount?: external["https://raw.githubusercontent.com/interledger/open-payments/0523804eeb21d9b8cd2eb65e4119738a0512a095/openapi/schemas.yaml"]["components"]["schemas"]["amount"]; + receiveAmount?: external["https://raw.githubusercontent.com/interledger/open-payments/0523804eeb21d9b8cd2eb65e4119738a0512a095/openapi/schemas.yaml"]["components"]["schemas"]["amount"]; + interval?: external["https://raw.githubusercontent.com/interledger/open-payments/0523804eeb21d9b8cd2eb65e4119738a0512a095/openapi/schemas.yaml"]["components"]["schemas"]["interval"]; }; "list-actions": "list" | "list-all"; "read-actions": "read" | "read-all"; From 6eff96c528e937c7ae2d0c551b80179dc10b01d0 Mon Sep 17 00:00:00 2001 From: Max Kurapov Date: Thu, 17 Nov 2022 01:25:57 +0100 Subject: [PATCH 09/16] feat(open-payments): update grant request --- packages/open-payments/src/client/grant.ts | 95 ++++++++++++++++++---- 1 file changed, 77 insertions(+), 18 deletions(-) diff --git a/packages/open-payments/src/client/grant.ts b/packages/open-payments/src/client/grant.ts index 0f09972d87..049a985351 100644 --- a/packages/open-payments/src/client/grant.ts +++ b/packages/open-payments/src/client/grant.ts @@ -1,4 +1,4 @@ -import { HttpMethod } from 'openapi' +import { HttpMethod, ResponseValidator } from 'openapi' import { RouteDeps } from '.' import { getASPath, @@ -26,33 +26,92 @@ export interface GrantRoutes { export const createGrantRoutes = (deps: RouteDeps): GrantRoutes => { const { axiosInstance, openApi, logger } = deps - const createInteractiveGrantValidator = - openApi.createResponseValidator({ - path: getASPath('/'), - method: HttpMethod.POST - }) - const createNonInteractiveGrantValidator = - openApi.createResponseValidator({ - path: getASPath('/'), - method: HttpMethod.POST - }) - + const requestGrantValidator = openApi.createResponseValidator< + InteractiveGrant | NonInteractiveGrant + >({ + path: getASPath('/'), + method: HttpMethod.POST + }) return { requestInteractiveGrant: ( args: RequestGrantArgs ) => - post( + requestInteractiveGrant( { axiosInstance, logger }, - { url: args.url, body: args.request }, - createInteractiveGrantValidator + args, + requestGrantValidator ), requestNonInteractiveGrant: ( args: RequestGrantArgs ) => - post( + requestNonInteractiveGrant( { axiosInstance, logger }, - { url: args.url, body: args.request }, - createNonInteractiveGrantValidator + args, + requestGrantValidator ) } } + +export const requestInteractiveGrant = async ( + deps: Pick, + args: RequestGrantArgs, + validateOpenApiResponse: ResponseValidator< + InteractiveGrant | NonInteractiveGrant + > +) => { + const { axiosInstance, logger } = deps + const { url } = args + + const grant = await post( + { axiosInstance, logger }, + { url: args.url, body: args.request }, + validateOpenApiResponse + ) + + if (!isInteractiveGrant(grant)) { + const errorMessage = 'Could not validate interactive grant' + logger.error({ url, grant }, errorMessage) + throw new Error(errorMessage) + } + + return grant +} + +export const requestNonInteractiveGrant = async ( + deps: Pick, + args: RequestGrantArgs, + validateOpenApiResponse: ResponseValidator< + InteractiveGrant | NonInteractiveGrant + > +) => { + const { axiosInstance, logger } = deps + const { url } = args + + const grant = await post( + { axiosInstance, logger }, + { url: args.url, body: args.request }, + validateOpenApiResponse + ) + + if (!isNonInteractiveGrant(grant)) { + const errorMessage = 'Could not validate non-interactive grant' + logger.error({ url, grant }, errorMessage) + throw new Error(errorMessage) + } + + return grant +} + +export const isInteractiveGrant = ( + grant: InteractiveGrant | NonInteractiveGrant +): grant is InteractiveGrant => + !('access_token' in grant) && + 'interact' in grant && + !!(grant as InteractiveGrant).interact + +export const isNonInteractiveGrant = ( + grant: InteractiveGrant | NonInteractiveGrant +): grant is NonInteractiveGrant => + !('interact' in grant) && + 'access_token' in grant && + !!(grant as NonInteractiveGrant).access_token From b219aef9c3480582f74f881019afae42a29b9dbd Mon Sep 17 00:00:00 2001 From: Max Kurapov Date: Thu, 17 Nov 2022 15:26:19 +0100 Subject: [PATCH 10/16] feat(open-payments) adding tests for new grant fetching methods --- .../open-payments/src/client/grant.test.ts | 142 +++++++++++++++++- packages/open-payments/src/test/helpers.ts | 119 ++++++++++++++- 2 files changed, 253 insertions(+), 8 deletions(-) diff --git a/packages/open-payments/src/client/grant.test.ts b/packages/open-payments/src/client/grant.test.ts index 7fcd22d128..c812f0a895 100644 --- a/packages/open-payments/src/client/grant.test.ts +++ b/packages/open-payments/src/client/grant.test.ts @@ -1,7 +1,22 @@ -import { createGrantRoutes } from './grant' +import nock from 'nock' +import { + createGrantRoutes, + isInteractiveGrant, + isNonInteractiveGrant, + requestInteractiveGrant, + requestNonInteractiveGrant +} from './grant' import { OpenAPI, HttpMethod, createOpenAPI } from 'openapi' import config from '../config' -import { defaultAxiosInstance, silentLogger } from '../test/helpers' +import { + defaultAxiosInstance, + mockInteractiveGrant, + mockInteractiveGrantRequest, + mockNonInteractiveGrant, + mockNonInteractiveGrantRequest, + mockOpenApiResponseValidators, + silentLogger +} from '../test/helpers' describe('grant', (): void => { let openApi: OpenAPI @@ -12,20 +27,133 @@ describe('grant', (): void => { const axiosInstance = defaultAxiosInstance const logger = silentLogger + const baseUrl = 'http://localhost:1000' + const openApiValidators = mockOpenApiResponseValidators() describe('createGrantRoutes', (): void => { - test('creates response validators for grant requests', async (): Promise => { + test('creates response validator for grant requests', async (): Promise => { jest.spyOn(openApi, 'createResponseValidator') createGrantRoutes({ axiosInstance, openApi, logger }) - expect(openApi.createResponseValidator).toHaveBeenNthCalledWith(1, { + expect(openApi.createResponseValidator).toHaveBeenCalledTimes(1) + expect(openApi.createResponseValidator).toHaveBeenCalledWith({ path: '/', method: HttpMethod.POST }) - expect(openApi.createResponseValidator).toHaveBeenNthCalledWith(2, { - path: '/', - method: HttpMethod.POST + }) + }) + + describe('requestInteractiveGrant', (): void => { + test('returns interactive grant if passes validation', async (): Promise => { + const interactiveGrant = mockInteractiveGrant() + + nock(baseUrl).post('/').reply(200, interactiveGrant) + + const result = await requestInteractiveGrant( + { + axiosInstance, + logger + }, + { + url: `${baseUrl}/`, + request: mockInteractiveGrantRequest() + }, + openApiValidators.successfulValidator + ) + expect(result).toStrictEqual(interactiveGrant) + }) + + test('throws if interactive grant does not pass validation', async (): Promise => { + const incorrectInteractiveGrant = mockInteractiveGrant({ + interact: undefined }) + + nock(baseUrl).post('/').reply(200, incorrectInteractiveGrant) + + expect( + requestInteractiveGrant( + { + axiosInstance, + logger + }, + { + url: `${baseUrl}/`, + request: mockInteractiveGrantRequest() + }, + openApiValidators.successfulValidator + ) + ).rejects.toThrow('Could not validate interactive grant') + }) + }) + + describe('requestNonInteractiveGrant', (): void => { + test('returns non-interactive grant if passes validation', async (): Promise => { + const nonInteractiveGrant = mockNonInteractiveGrant() + + nock(baseUrl).post('/').reply(200, nonInteractiveGrant) + + const result = await requestNonInteractiveGrant( + { + axiosInstance, + logger + }, + { + url: `${baseUrl}/`, + request: mockNonInteractiveGrantRequest() + }, + openApiValidators.successfulValidator + ) + expect(result).toStrictEqual(nonInteractiveGrant) + }) + + test('throws if non-interactive grant does not pass validation', async (): Promise => { + const incorrectNonInteractiveGrant = mockNonInteractiveGrant({ + access_token: undefined + }) + + nock(baseUrl).post('/').reply(200, incorrectNonInteractiveGrant) + + expect( + requestNonInteractiveGrant( + { + axiosInstance, + logger + }, + { + url: `${baseUrl}/`, + request: mockNonInteractiveGrantRequest() + }, + openApiValidators.successfulValidator + ) + ).rejects.toThrow('Could not validate non-interactive grant') + }) + }) + + describe('isInteractiveGrant', (): void => { + test('returns true if has interact property', async (): Promise => { + expect(isInteractiveGrant(mockInteractiveGrant())).toBe(true) + }) + + test('returns false if has access_token property', async (): Promise => { + const grant = mockInteractiveGrant() + + grant['access_token'] = { value: 'token' } + + expect(isInteractiveGrant(grant)).toBe(false) + }) + }) + + describe('isNonInteractiveGrant', (): void => { + test('returns true if has access_token property', async (): Promise => { + expect(isNonInteractiveGrant(mockNonInteractiveGrant())).toBe(true) + }) + + test('returns false if has interact property', async (): Promise => { + const grant = mockNonInteractiveGrant() + + grant['interact'] = { redirect: 'http://example.com/redirect' } + + expect(isNonInteractiveGrant(grant)).toBe(false) }) }) }) diff --git a/packages/open-payments/src/test/helpers.ts b/packages/open-payments/src/test/helpers.ts index 113e3b1507..6594b4342d 100644 --- a/packages/open-payments/src/test/helpers.ts +++ b/packages/open-payments/src/test/helpers.ts @@ -1,7 +1,14 @@ import { generateKeyPairSync } from 'crypto' import createLogger from 'pino' import { createAxiosInstance } from '../client/requests' -import { ILPStreamConnection, IncomingPayment } from '../types' +import { + ILPStreamConnection, + IncomingPayment, + InteractiveGrant, + InteractiveGrantRequest, + NonInteractiveGrant, + NonInteractiveGrantRequest +} from '../types' import base64url from 'base64url' import { v4 as uuid } from 'uuid' import { ResponseValidator } from 'openapi' @@ -60,3 +67,113 @@ export const mockIncomingPayment = ( ilpStreamConnection: mockILPStreamConnection(), ...overrides }) + +export const mockInteractiveGrant = ( + overrides?: Partial +): InteractiveGrant => ({ + interact: { + redirect: 'http://example.com/redirect', + finish: 'EF5C2D8DF0663FD5' + }, + continue: { + access_token: { + value: 'BBBDD7BDD6CB8659' + }, + uri: 'http://example.com/continue', + wait: 5 + }, + ...overrides +}) + +export const mockInteractiveGrantRequest = ( + overrides?: Partial +): InteractiveGrantRequest => ({ + access_token: { + access: [ + { + type: 'quote', + actions: ['create', 'read'] + } + ] + }, + client: { + display: { + name: 'Shoe Shop', + uri: 'https://shoe-shop.com/' + }, + key: { + proof: 'httpsig', + jwk: { + alg: 'EdDSA', + kty: 'OKP', + use: 'sig', + crv: 'Ed25519', + kid: 'http://fynbos/keys/12345', + x: 'jfiusdhvherui4vueurygygb8' + } + } + }, + interact: { + start: ['redirect'], + finish: { + method: 'redirect', + uri: 'http://localhost:3030/mock-idp/fake-client', + nonce: '456' + } + }, + ...overrides +}) + +export const mockNonInteractiveGrant = ( + overrides?: Partial +): NonInteractiveGrant => ({ + access_token: { + value: '99C36C2A4DB5BEBC', + manage: 'http://example.com/token/', + access: [ + { + type: 'incoming-payment', + actions: ['create', 'read', 'list', 'complete'] + } + ], + expires_in: 600 + }, + continue: { + access_token: { + value: 'DECCCF3D2229DB48' + }, + uri: 'http://example.com/continue/' + }, + ...overrides +}) + +export const mockNonInteractiveGrantRequest = ( + overrides?: Partial +): NonInteractiveGrantRequest => ({ + access_token: { + access: [ + { + type: 'quote', + actions: ['create', 'read'] + } + ] + }, + client: { + display: { + name: 'Shoe Shop', + uri: 'https://shoe-shop.com/' + }, + key: { + proof: 'httpsig', + jwk: { + alg: 'EdDSA', + kty: 'OKP', + use: 'sig', + crv: 'Ed25519', + kid: 'http://fynbos/keys/12345', + x: 'jfiusdhvherui4vueurygygb8' + } + } + }, + ...overrides +}) From 7b6407fd3afce9ea1dfaaf29c2bde2119af3b91f Mon Sep 17 00:00:00 2001 From: Max Kurapov Date: Thu, 17 Nov 2022 16:30:30 +0100 Subject: [PATCH 11/16] chore(open-payments): rename dependency var --- packages/open-payments/src/client/index.ts | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/packages/open-payments/src/client/index.ts b/packages/open-payments/src/client/index.ts index 57fe1caac1..3f43473445 100644 --- a/packages/open-payments/src/client/index.ts +++ b/packages/open-payments/src/client/index.ts @@ -25,7 +25,7 @@ export interface BaseDeps { interface ClientDeps extends BaseDeps { resourceServerOpenApi: OpenAPI - authorizationServerOpenApi: OpenAPI + authServerOpenApi: OpenAPI } export interface RouteDeps extends BaseDeps { @@ -46,14 +46,14 @@ const createDeps = async ( const resourceServerOpenApi = await createOpenAPI( config.OPEN_PAYMENTS_RS_OPEN_API_URL ) - const authorizationServerOpenApi = await createOpenAPI( + const authServerOpenApi = await createOpenAPI( config.OPEN_PAYMENTS_AS_OPEN_API_URL ) const logger = args?.logger ?? createLogger() return { axiosInstance, resourceServerOpenApi, - authorizationServerOpenApi, + authServerOpenApi, logger } } @@ -103,12 +103,8 @@ export interface AuthenticatedClient extends UnauthenticatedClient { export const createAuthenticatedClient = async ( args: CreateAuthenticatedClientArgs ): Promise => { - const { - axiosInstance, - resourceServerOpenApi, - authorizationServerOpenApi, - logger - } = await createDeps(args) + const { axiosInstance, resourceServerOpenApi, authServerOpenApi, logger } = + await createDeps(args) return { incomingPayment: createIncomingPaymentRoutes({ @@ -128,7 +124,7 @@ export const createAuthenticatedClient = async ( }), grant: createGrantRoutes({ axiosInstance, - openApi: authorizationServerOpenApi, + openApi: authServerOpenApi, logger }) } From 86304fe6e4e55135ecbf8b07cd15fe9b767b30ed Mon Sep 17 00:00:00 2001 From: Max Kurapov Date: Thu, 17 Nov 2022 18:56:58 +0100 Subject: [PATCH 12/16] feat(open-payments): unify the grant request call --- .../open-payments/src/client/grant.test.ts | 124 +++--------------- packages/open-payments/src/client/grant.ts | 100 ++------------ packages/open-payments/src/test/helpers.ts | 58 ++------ packages/open-payments/src/types.ts | 4 +- 4 files changed, 44 insertions(+), 242 deletions(-) diff --git a/packages/open-payments/src/client/grant.test.ts b/packages/open-payments/src/client/grant.test.ts index c812f0a895..80a1bc33f6 100644 --- a/packages/open-payments/src/client/grant.test.ts +++ b/packages/open-payments/src/client/grant.test.ts @@ -1,20 +1,14 @@ -import nock from 'nock' import { createGrantRoutes, isInteractiveGrant, - isNonInteractiveGrant, - requestInteractiveGrant, - requestNonInteractiveGrant + isNonInteractiveGrant } from './grant' import { OpenAPI, HttpMethod, createOpenAPI } from 'openapi' import config from '../config' import { defaultAxiosInstance, mockInteractiveGrant, - mockInteractiveGrantRequest, mockNonInteractiveGrant, - mockNonInteractiveGrantRequest, - mockOpenApiResponseValidators, silentLogger } from '../test/helpers' @@ -27,8 +21,6 @@ describe('grant', (): void => { const axiosInstance = defaultAxiosInstance const logger = silentLogger - const baseUrl = 'http://localhost:1000' - const openApiValidators = mockOpenApiResponseValidators() describe('createGrantRoutes', (): void => { test('creates response validator for grant requests', async (): Promise => { @@ -43,103 +35,19 @@ describe('grant', (): void => { }) }) - describe('requestInteractiveGrant', (): void => { - test('returns interactive grant if passes validation', async (): Promise => { - const interactiveGrant = mockInteractiveGrant() - - nock(baseUrl).post('/').reply(200, interactiveGrant) - - const result = await requestInteractiveGrant( - { - axiosInstance, - logger - }, - { - url: `${baseUrl}/`, - request: mockInteractiveGrantRequest() - }, - openApiValidators.successfulValidator - ) - expect(result).toStrictEqual(interactiveGrant) - }) - - test('throws if interactive grant does not pass validation', async (): Promise => { - const incorrectInteractiveGrant = mockInteractiveGrant({ - interact: undefined - }) - - nock(baseUrl).post('/').reply(200, incorrectInteractiveGrant) - - expect( - requestInteractiveGrant( - { - axiosInstance, - logger - }, - { - url: `${baseUrl}/`, - request: mockInteractiveGrantRequest() - }, - openApiValidators.successfulValidator - ) - ).rejects.toThrow('Could not validate interactive grant') - }) - }) - - describe('requestNonInteractiveGrant', (): void => { - test('returns non-interactive grant if passes validation', async (): Promise => { - const nonInteractiveGrant = mockNonInteractiveGrant() - - nock(baseUrl).post('/').reply(200, nonInteractiveGrant) - - const result = await requestNonInteractiveGrant( - { - axiosInstance, - logger - }, - { - url: `${baseUrl}/`, - request: mockNonInteractiveGrantRequest() - }, - openApiValidators.successfulValidator - ) - expect(result).toStrictEqual(nonInteractiveGrant) - }) - - test('throws if non-interactive grant does not pass validation', async (): Promise => { - const incorrectNonInteractiveGrant = mockNonInteractiveGrant({ - access_token: undefined - }) - - nock(baseUrl).post('/').reply(200, incorrectNonInteractiveGrant) - - expect( - requestNonInteractiveGrant( - { - axiosInstance, - logger - }, - { - url: `${baseUrl}/`, - request: mockNonInteractiveGrantRequest() - }, - openApiValidators.successfulValidator - ) - ).rejects.toThrow('Could not validate non-interactive grant') - }) - }) - describe('isInteractiveGrant', (): void => { test('returns true if has interact property', async (): Promise => { expect(isInteractiveGrant(mockInteractiveGrant())).toBe(true) }) - test('returns false if has access_token property', async (): Promise => { - const grant = mockInteractiveGrant() - - grant['access_token'] = { value: 'token' } - - expect(isInteractiveGrant(grant)).toBe(false) + test('returns false if does not have interact property', async (): Promise => { + expect( + isInteractiveGrant( + mockInteractiveGrant({ + interact: undefined + }) + ) + ).toBe(false) }) }) @@ -148,12 +56,14 @@ describe('grant', (): void => { expect(isNonInteractiveGrant(mockNonInteractiveGrant())).toBe(true) }) - test('returns false if has interact property', async (): Promise => { - const grant = mockNonInteractiveGrant() - - grant['interact'] = { redirect: 'http://example.com/redirect' } - - expect(isNonInteractiveGrant(grant)).toBe(false) + test('returns false if does not have access_token property', async (): Promise => { + expect( + isNonInteractiveGrant( + mockNonInteractiveGrant({ + access_token: undefined + }) + ) + ).toBe(false) }) }) }) diff --git a/packages/open-payments/src/client/grant.ts b/packages/open-payments/src/client/grant.ts index 049a985351..03e5fdd5c4 100644 --- a/packages/open-payments/src/client/grant.ts +++ b/packages/open-payments/src/client/grant.ts @@ -1,117 +1,41 @@ -import { HttpMethod, ResponseValidator } from 'openapi' +import { HttpMethod } from 'openapi' import { RouteDeps } from '.' import { getASPath, InteractiveGrant, - InteractiveGrantRequest, NonInteractiveGrant, - NonInteractiveGrantRequest + GrantRequest } from '../types' import { post } from './requests' -interface RequestGrantArgs { +interface RequestGrantArgs { url: string - request: T + request: GrantRequest } export interface GrantRoutes { - requestInteractiveGrant( - args: RequestGrantArgs - ): Promise - requestNonInteractiveGrant( - args: RequestGrantArgs - ): Promise + requestGrant( + args: RequestGrantArgs + ): Promise } export const createGrantRoutes = (deps: RouteDeps): GrantRoutes => { - const { axiosInstance, openApi, logger } = deps - - const requestGrantValidator = openApi.createResponseValidator< + const requestGrantValidator = deps.openApi.createResponseValidator< InteractiveGrant | NonInteractiveGrant >({ path: getASPath('/'), method: HttpMethod.POST }) return { - requestInteractiveGrant: ( - args: RequestGrantArgs - ) => - requestInteractiveGrant( - { axiosInstance, logger }, - args, - requestGrantValidator - ), - requestNonInteractiveGrant: ( - args: RequestGrantArgs - ) => - requestNonInteractiveGrant( - { axiosInstance, logger }, - args, - requestGrantValidator - ) - } -} - -export const requestInteractiveGrant = async ( - deps: Pick, - args: RequestGrantArgs, - validateOpenApiResponse: ResponseValidator< - InteractiveGrant | NonInteractiveGrant - > -) => { - const { axiosInstance, logger } = deps - const { url } = args - - const grant = await post( - { axiosInstance, logger }, - { url: args.url, body: args.request }, - validateOpenApiResponse - ) - - if (!isInteractiveGrant(grant)) { - const errorMessage = 'Could not validate interactive grant' - logger.error({ url, grant }, errorMessage) - throw new Error(errorMessage) + requestGrant: (args: RequestGrantArgs) => + post(deps, { url: args.url, body: args.request }, requestGrantValidator) } - - return grant -} - -export const requestNonInteractiveGrant = async ( - deps: Pick, - args: RequestGrantArgs, - validateOpenApiResponse: ResponseValidator< - InteractiveGrant | NonInteractiveGrant - > -) => { - const { axiosInstance, logger } = deps - const { url } = args - - const grant = await post( - { axiosInstance, logger }, - { url: args.url, body: args.request }, - validateOpenApiResponse - ) - - if (!isNonInteractiveGrant(grant)) { - const errorMessage = 'Could not validate non-interactive grant' - logger.error({ url, grant }, errorMessage) - throw new Error(errorMessage) - } - - return grant } export const isInteractiveGrant = ( grant: InteractiveGrant | NonInteractiveGrant -): grant is InteractiveGrant => - !('access_token' in grant) && - 'interact' in grant && - !!(grant as InteractiveGrant).interact +): grant is InteractiveGrant => !!(grant as InteractiveGrant).interact export const isNonInteractiveGrant = ( grant: InteractiveGrant | NonInteractiveGrant -): grant is NonInteractiveGrant => - !('interact' in grant) && - 'access_token' in grant && - !!(grant as NonInteractiveGrant).access_token +): grant is NonInteractiveGrant => !!(grant as NonInteractiveGrant).access_token diff --git a/packages/open-payments/src/test/helpers.ts b/packages/open-payments/src/test/helpers.ts index 6594b4342d..6dcec0ab59 100644 --- a/packages/open-payments/src/test/helpers.ts +++ b/packages/open-payments/src/test/helpers.ts @@ -5,9 +5,8 @@ import { ILPStreamConnection, IncomingPayment, InteractiveGrant, - InteractiveGrantRequest, - NonInteractiveGrant, - NonInteractiveGrantRequest + GrantRequest, + NonInteractiveGrant } from '../types' import base64url from 'base64url' import { v4 as uuid } from 'uuid' @@ -85,45 +84,6 @@ export const mockInteractiveGrant = ( ...overrides }) -export const mockInteractiveGrantRequest = ( - overrides?: Partial -): InteractiveGrantRequest => ({ - access_token: { - access: [ - { - type: 'quote', - actions: ['create', 'read'] - } - ] - }, - client: { - display: { - name: 'Shoe Shop', - uri: 'https://shoe-shop.com/' - }, - key: { - proof: 'httpsig', - jwk: { - alg: 'EdDSA', - kty: 'OKP', - use: 'sig', - crv: 'Ed25519', - kid: 'http://fynbos/keys/12345', - x: 'jfiusdhvherui4vueurygygb8' - } - } - }, - interact: { - start: ['redirect'], - finish: { - method: 'redirect', - uri: 'http://localhost:3030/mock-idp/fake-client', - nonce: '456' - } - }, - ...overrides -}) - export const mockNonInteractiveGrant = ( overrides?: Partial ): NonInteractiveGrant => ({ @@ -147,9 +107,9 @@ export const mockNonInteractiveGrant = ( ...overrides }) -export const mockNonInteractiveGrantRequest = ( - overrides?: Partial -): NonInteractiveGrantRequest => ({ +export const mockGrantRequest = ( + overrides?: Partial +): GrantRequest => ({ access_token: { access: [ { @@ -175,5 +135,13 @@ export const mockNonInteractiveGrantRequest = ( } } }, + interact: { + start: ['redirect'], + finish: { + method: 'redirect', + uri: 'http://localhost:3030/mock-idp/fake-client', + nonce: '456' + } + }, ...overrides }) diff --git a/packages/open-payments/src/types.ts b/packages/open-payments/src/types.ts index 504119fb60..30f1c13ac7 100644 --- a/packages/open-payments/src/types.ts +++ b/packages/open-payments/src/types.ts @@ -26,10 +26,10 @@ export type NonInteractiveGrant = { access_token: ASComponents['schemas']['access_token'] continue: ASComponents['schemas']['continue'] } -export type InteractiveGrantRequest = { +export type GrantRequest = { access_token: ASOperations['post-request']['requestBody']['content']['application/json']['access_token'] client: ASOperations['post-request']['requestBody']['content']['application/json']['client'] - interact: ASOperations['post-request']['requestBody']['content']['application/json']['interact'] + interact?: ASOperations['post-request']['requestBody']['content']['application/json']['interact'] } export type InteractiveGrant = { interact: ASComponents['schemas']['interact-response'] From f8154ba079915f42e86a1d678172bfcc1a66a72b Mon Sep 17 00:00:00 2001 From: Max Kurapov Date: Thu, 17 Nov 2022 18:58:44 +0100 Subject: [PATCH 13/16] chore(open-payments): simplify type --- packages/open-payments/src/client/incoming-payment.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/open-payments/src/client/incoming-payment.ts b/packages/open-payments/src/client/incoming-payment.ts index 4d42222b65..d160bd4824 100644 --- a/packages/open-payments/src/client/incoming-payment.ts +++ b/packages/open-payments/src/client/incoming-payment.ts @@ -1,5 +1,5 @@ import { HttpMethod, ResponseValidator } from 'openapi' -import { RouteDeps } from '.' +import { BaseDeps, RouteDeps } from '.' import { IncomingPayment, getRSPath } from '../types' import { get } from './requests' @@ -34,7 +34,7 @@ export const createIncomingPaymentRoutes = ( } export const getIncomingPayment = async ( - deps: Pick, + deps: BaseDeps, args: GetArgs, validateOpenApiResponse: ResponseValidator ) => { From 1e5a71aef9ebd52ec862c6d5f13ee0ead97a2760 Mon Sep 17 00:00:00 2001 From: Max Kurapov Date: Thu, 17 Nov 2022 19:08:26 +0100 Subject: [PATCH 14/16] feat(open-payments): move around type guards --- .../open-payments/src/client/grant.test.ts | 45 +------------------ packages/open-payments/src/index.ts | 4 +- packages/open-payments/src/types.test.ts | 36 +++++++++++++++ packages/open-payments/src/types.ts | 7 +++ 4 files changed, 48 insertions(+), 44 deletions(-) create mode 100644 packages/open-payments/src/types.test.ts diff --git a/packages/open-payments/src/client/grant.test.ts b/packages/open-payments/src/client/grant.test.ts index 80a1bc33f6..f1706108f1 100644 --- a/packages/open-payments/src/client/grant.test.ts +++ b/packages/open-payments/src/client/grant.test.ts @@ -1,16 +1,7 @@ -import { - createGrantRoutes, - isInteractiveGrant, - isNonInteractiveGrant -} from './grant' +import { createGrantRoutes } from './grant' import { OpenAPI, HttpMethod, createOpenAPI } from 'openapi' import config from '../config' -import { - defaultAxiosInstance, - mockInteractiveGrant, - mockNonInteractiveGrant, - silentLogger -} from '../test/helpers' +import { defaultAxiosInstance, silentLogger } from '../test/helpers' describe('grant', (): void => { let openApi: OpenAPI @@ -34,36 +25,4 @@ describe('grant', (): void => { }) }) }) - - describe('isInteractiveGrant', (): void => { - test('returns true if has interact property', async (): Promise => { - expect(isInteractiveGrant(mockInteractiveGrant())).toBe(true) - }) - - test('returns false if does not have interact property', async (): Promise => { - expect( - isInteractiveGrant( - mockInteractiveGrant({ - interact: undefined - }) - ) - ).toBe(false) - }) - }) - - describe('isNonInteractiveGrant', (): void => { - test('returns true if has access_token property', async (): Promise => { - expect(isNonInteractiveGrant(mockNonInteractiveGrant())).toBe(true) - }) - - test('returns false if does not have access_token property', async (): Promise => { - expect( - isNonInteractiveGrant( - mockNonInteractiveGrant({ - access_token: undefined - }) - ) - ).toBe(false) - }) - }) }) diff --git a/packages/open-payments/src/index.ts b/packages/open-payments/src/index.ts index 4f7cb830f6..906af96ef1 100644 --- a/packages/open-payments/src/index.ts +++ b/packages/open-payments/src/index.ts @@ -2,7 +2,9 @@ export { IncomingPayment, ILPStreamConnection, InteractiveGrant, - NonInteractiveGrant + NonInteractiveGrant, + isInteractiveGrant, + isNonInteractiveGrant } from './types' export { diff --git a/packages/open-payments/src/types.test.ts b/packages/open-payments/src/types.test.ts new file mode 100644 index 0000000000..fcf0994d39 --- /dev/null +++ b/packages/open-payments/src/types.test.ts @@ -0,0 +1,36 @@ +import { mockInteractiveGrant, mockNonInteractiveGrant } from './test/helpers' +import { isInteractiveGrant, isNonInteractiveGrant } from './types' + +describe('types', (): void => { + describe('isInteractiveGrant', (): void => { + test('returns true if has interact property', async (): Promise => { + expect(isInteractiveGrant(mockInteractiveGrant())).toBe(true) + }) + + test('returns false if does not have interact property', async (): Promise => { + expect( + isInteractiveGrant( + mockInteractiveGrant({ + interact: undefined + }) + ) + ).toBe(false) + }) + }) + + describe('isNonInteractiveGrant', (): void => { + test('returns true if has access_token property', async (): Promise => { + expect(isNonInteractiveGrant(mockNonInteractiveGrant())).toBe(true) + }) + + test('returns false if does not have access_token property', async (): Promise => { + expect( + isNonInteractiveGrant( + mockNonInteractiveGrant({ + access_token: undefined + }) + ) + ).toBe(false) + }) + }) +}) diff --git a/packages/open-payments/src/types.ts b/packages/open-payments/src/types.ts index 30f1c13ac7..a9a0fd660b 100644 --- a/packages/open-payments/src/types.ts +++ b/packages/open-payments/src/types.ts @@ -35,3 +35,10 @@ export type InteractiveGrant = { interact: ASComponents['schemas']['interact-response'] continue: ASComponents['schemas']['continue'] } +export const isInteractiveGrant = ( + grant: InteractiveGrant | NonInteractiveGrant +): grant is InteractiveGrant => !!(grant as InteractiveGrant).interact + +export const isNonInteractiveGrant = ( + grant: InteractiveGrant | NonInteractiveGrant +): grant is NonInteractiveGrant => !!(grant as NonInteractiveGrant).access_token From d9d7f7da4867f521d99e5c4eb83f91c85f969976 Mon Sep 17 00:00:00 2001 From: Max Kurapov Date: Thu, 17 Nov 2022 19:11:38 +0100 Subject: [PATCH 15/16] chore(open-payments): remove duplicate fns --- packages/open-payments/src/client/grant.ts | 8 -------- 1 file changed, 8 deletions(-) diff --git a/packages/open-payments/src/client/grant.ts b/packages/open-payments/src/client/grant.ts index 03e5fdd5c4..721e6172e6 100644 --- a/packages/open-payments/src/client/grant.ts +++ b/packages/open-payments/src/client/grant.ts @@ -31,11 +31,3 @@ export const createGrantRoutes = (deps: RouteDeps): GrantRoutes => { post(deps, { url: args.url, body: args.request }, requestGrantValidator) } } - -export const isInteractiveGrant = ( - grant: InteractiveGrant | NonInteractiveGrant -): grant is InteractiveGrant => !!(grant as InteractiveGrant).interact - -export const isNonInteractiveGrant = ( - grant: InteractiveGrant | NonInteractiveGrant -): grant is NonInteractiveGrant => !!(grant as NonInteractiveGrant).access_token From d2c02954bab07713732c165218acc871f840d6cf Mon Sep 17 00:00:00 2001 From: Max Kurapov Date: Thu, 17 Nov 2022 19:47:35 +0100 Subject: [PATCH 16/16] feat(open-payments): address comments --- packages/open-payments/src/client/grant.ts | 4 ++-- packages/open-payments/src/types.ts | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/open-payments/src/client/grant.ts b/packages/open-payments/src/client/grant.ts index 721e6172e6..8b6bf2a79f 100644 --- a/packages/open-payments/src/client/grant.ts +++ b/packages/open-payments/src/client/grant.ts @@ -14,7 +14,7 @@ interface RequestGrantArgs { } export interface GrantRoutes { - requestGrant( + request( args: RequestGrantArgs ): Promise } @@ -27,7 +27,7 @@ export const createGrantRoutes = (deps: RouteDeps): GrantRoutes => { method: HttpMethod.POST }) return { - requestGrant: (args: RequestGrantArgs) => + request: (args: RequestGrantArgs) => post(deps, { url: args.url, body: args.request }, requestGrantValidator) } } diff --git a/packages/open-payments/src/types.ts b/packages/open-payments/src/types.ts index a9a0fd660b..c925fd7ba5 100644 --- a/packages/open-payments/src/types.ts +++ b/packages/open-payments/src/types.ts @@ -29,7 +29,7 @@ export type NonInteractiveGrant = { export type GrantRequest = { access_token: ASOperations['post-request']['requestBody']['content']['application/json']['access_token'] client: ASOperations['post-request']['requestBody']['content']['application/json']['client'] - interact?: ASOperations['post-request']['requestBody']['content']['application/json']['interact'] + interact: ASOperations['post-request']['requestBody']['content']['application/json']['interact'] } export type InteractiveGrant = { interact: ASComponents['schemas']['interact-response']