Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(open-payments): export OpenAPI specs #440

Merged
merged 4 commits into from
Mar 14, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/mean-penguins-cheat.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@interledger/open-payments': minor
---

Add and export functions that allow fetching the OpenAPI objects for the Open Payments specs
8 changes: 4 additions & 4 deletions packages/open-payments/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,10 @@
"build:deps": "pnpm --filter openapi build && pnpm --filter http-signature-utils build",
"build": "pnpm build:deps && pnpm clean && tsc --build tsconfig.json && pnpm copy-files",
"clean": "rm -fr dist/",
"copy-files": "cp ./src/openapi/*.yaml ./dist/openapi/",
"generate:auth-server-types": "openapi-typescript src/openapi/auth-server.yaml --output src/openapi/generated/auth-server-types.ts",
"generate:resource-server-types": "openapi-typescript src/openapi/resource-server.yaml --output src/openapi/generated/resource-server-types.ts",
"generate:wallet-address-server-types": "openapi-typescript src/openapi/wallet-address-server.yaml --output src/openapi/generated/wallet-address-server-types.ts",
"copy-files": "mkdir ./dist/openapi/specs/ && cp ./src/openapi/specs/*.yaml ./dist/openapi/specs/",
"generate:auth-server-types": "openapi-typescript src/openapi/specs/auth-server.yaml --output src/openapi/generated/auth-server-types.ts",
"generate:resource-server-types": "openapi-typescript src/openapi/specs/resource-server.yaml --output src/openapi/generated/resource-server-types.ts",
"generate:wallet-address-server-types": "openapi-typescript src/openapi/specs/wallet-address-server.yaml --output src/openapi/generated/wallet-address-server-types.ts",
"generate:types": "pnpm generate:auth-server-types && pnpm generate:resource-server-types && pnpm generate:wallet-address-server-types",
"prepack": "pnpm build",
"test": "jest --passWithNoTests"
Expand Down
8 changes: 3 additions & 5 deletions packages/open-payments/src/client/grant.test.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import { createGrantRoutes } from './grant'
import { OpenAPI, HttpMethod, createOpenAPI } from '@interledger/openapi'
import path from 'path'
import { OpenAPI, HttpMethod } from '@interledger/openapi'
import { createTestDeps, mockGrantRequest } from '../test/helpers'
import * as requestors from './requests'
import { v4 as uuid } from 'uuid'
import { getAuthServerOpenAPI } from '../openapi'

jest.mock('./requests', () => ({
...jest.requireActual('./requests.ts'),
Expand All @@ -17,9 +17,7 @@ describe('grant', (): void => {
let openApi: OpenAPI

beforeAll(async () => {
openApi = await createOpenAPI(
path.resolve(__dirname, '../openapi/auth-server.yaml')
)
openApi = await getAuthServerOpenAPI()
})

const deps = createTestDeps()
Expand Down
8 changes: 3 additions & 5 deletions packages/open-payments/src/client/incoming-payment.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import {
createUnauthenticatedIncomingPaymentRoutes,
getPublicIncomingPayment
} from './incoming-payment'
import { OpenAPI, HttpMethod, createOpenAPI } from '@interledger/openapi'
import { OpenAPI, HttpMethod } from '@interledger/openapi'
import {
createTestDeps,
mockIncomingPayment,
Expand All @@ -20,12 +20,12 @@ import {
mockPublicIncomingPayment
} from '../test/helpers'
import nock from 'nock'
import path from 'path'
import { v4 as uuid } from 'uuid'
import * as requestors from './requests'
import { getRSPath } from '../types'
import { OpenPaymentsClientError } from './error'
import assert from 'assert'
import { getResourceServerOpenAPI } from '../openapi'

jest.mock('./requests', () => {
return {
Expand All @@ -39,9 +39,7 @@ describe('incoming-payment', (): void => {
let openApi: OpenAPI

beforeAll(async () => {
openApi = await createOpenAPI(
path.resolve(__dirname, '../openapi/resource-server.yaml')
)
openApi = await getResourceServerOpenAPI()
})

const deps = createTestDeps()
Expand Down
27 changes: 11 additions & 16 deletions packages/open-payments/src/client/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { loadKey } from '@interledger/http-signature-utils'
import fs from 'fs'
import { createOpenAPI, OpenAPI } from '@interledger/openapi'
import { OpenAPI } from '@interledger/openapi'
import path from 'path'
import createLogger, { LevelWithSilent, Logger } from 'pino'
import config from '../config'
Expand Down Expand Up @@ -29,6 +29,11 @@ import { createTokenRoutes, TokenRoutes } from './token'
import { createQuoteRoutes, QuoteRoutes } from './quote'
import { KeyLike, KeyObject, createPrivateKey } from 'crypto'
import { OpenPaymentsClientError } from './error'
import {
getResourceServerOpenAPI,
getWalletAddressServerOpenAPI,
getAuthServerOpenAPI
} from '../openapi'
export * from './error'

export interface BaseDeps {
Expand Down Expand Up @@ -140,12 +145,8 @@ const createUnauthenticatedDeps = async ({
args?.requestTimeoutMs ?? config.DEFAULT_REQUEST_TIMEOUT_MS
})

const walletAddressServerOpenApi = await createOpenAPI(
path.resolve(__dirname, '../openapi/wallet-address-server.yaml')
)
const resourceServerOpenApi = await createOpenAPI(
path.resolve(__dirname, '../openapi/resource-server.yaml')
)
const walletAddressServerOpenApi = await getWalletAddressServerOpenAPI()
const resourceServerOpenApi = await getResourceServerOpenAPI()

return {
axiosInstance,
Expand Down Expand Up @@ -199,15 +200,9 @@ const createAuthenticatedClientDeps = async ({
})
}

const walletAddressServerOpenApi = await createOpenAPI(
path.resolve(__dirname, '../openapi/wallet-address-server.yaml')
)
const resourceServerOpenApi = await createOpenAPI(
path.resolve(__dirname, '../openapi/resource-server.yaml')
)
const authServerOpenApi = await createOpenAPI(
path.resolve(__dirname, '../openapi/auth-server.yaml')
)
const walletAddressServerOpenApi = await getWalletAddressServerOpenAPI()
const resourceServerOpenApi = await getResourceServerOpenAPI()
const authServerOpenApi = await getAuthServerOpenAPI()

return {
axiosInstance,
Expand Down
8 changes: 3 additions & 5 deletions packages/open-payments/src/client/outgoing-payment.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,19 @@ import {
listOutgoingPayments,
validateOutgoingPayment
} from './outgoing-payment'
import { OpenAPI, HttpMethod, createOpenAPI } from '@interledger/openapi'
import { OpenAPI, HttpMethod } from '@interledger/openapi'
import {
mockOutgoingPayment,
mockOpenApiResponseValidators,
mockOutgoingPaymentPaginationResult,
createTestDeps
} from '../test/helpers'
import nock from 'nock'
import path from 'path'
import { v4 as uuid } from 'uuid'
import * as requestors from './requests'
import { OpenPaymentsClientError } from './error'
import assert from 'assert'
import { getResourceServerOpenAPI } from '../openapi'

jest.mock('./requests', () => {
return {
Expand All @@ -31,9 +31,7 @@ describe('outgoing-payment', (): void => {
let openApi: OpenAPI

beforeAll(async () => {
openApi = await createOpenAPI(
path.resolve(__dirname, '../openapi/resource-server.yaml')
)
openApi = await getResourceServerOpenAPI()
})

const deps = createTestDeps()
Expand Down
8 changes: 3 additions & 5 deletions packages/open-payments/src/client/quote.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { createQuoteRoutes, getQuote, createQuote } from './quote'
import { OpenAPI, HttpMethod, createOpenAPI } from '@interledger/openapi'
import path from 'path'
import { OpenAPI, HttpMethod } from '@interledger/openapi'
import {
createTestDeps,
mockOpenApiResponseValidators,
Expand All @@ -9,6 +8,7 @@ import {
import nock from 'nock'
import * as requestors from './requests'
import { getRSPath } from '../types'
import { getResourceServerOpenAPI } from '../openapi'

jest.mock('./requests', () => {
return {
Expand All @@ -21,9 +21,7 @@ describe('quote', (): void => {
let openApi: OpenAPI

beforeAll(async () => {
openApi = await createOpenAPI(
path.resolve(__dirname, '../openapi/resource-server.yaml')
)
openApi = await getResourceServerOpenAPI()
})

const deps = createTestDeps()
Expand Down
8 changes: 3 additions & 5 deletions packages/open-payments/src/client/token.test.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import { createTokenRoutes, revokeToken, rotateToken } from './token'
import { OpenAPI, HttpMethod, createOpenAPI } from '@interledger/openapi'
import path from 'path'
import { OpenAPI, HttpMethod } from '@interledger/openapi'
import nock from 'nock'
import {
createTestDeps,
mockAccessToken,
mockOpenApiResponseValidators
} from '../test/helpers'
import * as requestors from './requests'
import { getAuthServerOpenAPI } from '../openapi'

jest.mock('./requests', () => {
return {
Expand All @@ -22,9 +22,7 @@ describe('token', (): void => {
let openApi: OpenAPI

beforeAll(async () => {
openApi = await createOpenAPI(
path.resolve(__dirname, '../openapi/auth-server.yaml')
)
openApi = await getAuthServerOpenAPI()
})

const deps = createTestDeps()
Expand Down
8 changes: 3 additions & 5 deletions packages/open-payments/src/client/wallet-address.test.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import { createWalletAddressRoutes } from './wallet-address'
import { OpenAPI, HttpMethod, createOpenAPI } from '@interledger/openapi'
import path from 'path'
import { OpenAPI, HttpMethod } from '@interledger/openapi'
import {
createTestDeps,
mockDIDDocument,
mockJwk,
mockWalletAddress
} from '../test/helpers'
import * as requestors from './requests'
import { getWalletAddressServerOpenAPI } from '../openapi'

jest.mock('./requests', () => {
return {
Expand All @@ -21,9 +21,7 @@ describe('wallet-address', (): void => {
let openApi: OpenAPI

beforeAll(async () => {
openApi = await createOpenAPI(
path.resolve(__dirname, '../openapi/resource-server.yaml')
)
openApi = await getWalletAddressServerOpenAPI()
})

const deps = createTestDeps()
Expand Down
6 changes: 6 additions & 0 deletions packages/open-payments/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,12 @@ export {
OpenPaymentsClientError
} from './client'

export {
getAuthServerOpenAPI,
getResourceServerOpenAPI,
getWalletAddressServerOpenAPI
} from './openapi'

export {
mockWalletAddress,
mockIncomingPayment,
Expand Down
1 change: 0 additions & 1 deletion packages/open-payments/src/openapi/auth-server.yaml

This file was deleted.

90 changes: 90 additions & 0 deletions packages/open-payments/src/openapi/index.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
import {
getResourceServerOpenAPI,
getAuthServerOpenAPI,
getWalletAddressServerOpenAPI
} from '.'

describe('OpenAPI', (): void => {
describe('getResourceServerOpenAPI', () => {
test('properly generates API paths', async () => {
const openApi = await getResourceServerOpenAPI()

expect(openApi).toBeDefined()
expect(Object.keys(openApi.paths)).toEqual(
expect.arrayContaining([
'/incoming-payments',
'/outgoing-payments',
'/quotes',
'/incoming-payments/{id}',
'/incoming-payments/{id}/complete',
'/outgoing-payments/{id}',
'/quotes/{id}'
])
)
})

test('properly references $ref to external ./schemas.yaml', async () => {
const openApi = await getResourceServerOpenAPI()

expect(
Object.keys(
openApi.paths?.['/incoming-payments']?.['post']?.['requestBody']?.[
'content'
]['application/json']['schema']['properties']['incomingAmount'][
'properties'
]
).sort()
).toEqual(['assetCode', 'assetScale', 'value'].sort())
})
})

describe('getAuthServerOpenAPI', () => {
test('properly generates API paths', async () => {
const openApi = await getAuthServerOpenAPI()

expect(openApi).toBeDefined()
expect(Object.keys(openApi.paths)).toEqual(
expect.arrayContaining(['/', '/continue/{id}', '/token/{id}'])
)
})

test('properly references $ref to external ./schemas.yaml', async () => {
const openApi = await getAuthServerOpenAPI()

expect(
Object.keys(
openApi.paths?.['/']?.['post']?.['requestBody']?.['content'][
'application/json'
]['schema']['properties']['access_token']['properties']['access'][
'items'
]['oneOf'][1]['properties']['limits']['properties']['debitAmount'][
'properties'
]
).sort()
).toEqual(['assetCode', 'assetScale', 'value'].sort())
})
})

describe('getWalletAddressServerOpenAPI', () => {
test('properly generates API paths', async () => {
const openApi = await getWalletAddressServerOpenAPI()

expect(openApi).toBeDefined()
expect(Object.keys(openApi.paths)).toEqual(
expect.arrayContaining(['/', '/jwks.json', '/did.json'])
)
})

test('properly references $ref to external ./schemas.yaml', async () => {
const openApi = await getWalletAddressServerOpenAPI()

const getWalletAddressResponse =
openApi.paths?.['/']?.['get']?.['responses']['200']['content'][
'application/json'
]['schema']['properties']

expect(getWalletAddressResponse['assetCode'].type).toBe('string')
expect(getWalletAddressResponse['assetScale'].type).toBe('integer')
})
})
})
31 changes: 31 additions & 0 deletions packages/open-payments/src/openapi/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { createOpenAPI } from '@interledger/openapi'
import path from 'path'

/**
* Returns the OpenAPI object for the Open Payments Resource Server OpenAPI spec.
* This object allows validating requests and responses against the spec.
* See more: https://github.com/interledger/open-payments/blob/main/packages/openapi/README.md
*/
export async function getResourceServerOpenAPI() {
return createOpenAPI(path.resolve(__dirname, './specs/resource-server.yaml'))
}

/**
* Returns the OpenAPI object for the Open Payments Wallet Address Server OpenAPI spec.
* This object allows validating requests and responses against the spec.
* See more: https://github.com/interledger/open-payments/blob/main/packages/openapi/README.md
*/
export async function getWalletAddressServerOpenAPI() {
return createOpenAPI(
path.resolve(__dirname, './specs/wallet-address-server.yaml')
)
}

/**
* Returns the OpenAPI object for the Open Payments Auth Server OpenAPI spec.
* This object allows validating requests and responses against the spec.
* See more: https://github.com/interledger/open-payments/blob/main/packages/openapi/README.md
*/
export async function getAuthServerOpenAPI() {
return createOpenAPI(path.resolve(__dirname, './specs/auth-server.yaml'))
}
1 change: 0 additions & 1 deletion packages/open-payments/src/openapi/resource-server.yaml

This file was deleted.

1 change: 0 additions & 1 deletion packages/open-payments/src/openapi/schemas.yaml

This file was deleted.

1 change: 1 addition & 0 deletions packages/open-payments/src/openapi/specs/auth-server.yaml
Loading
Loading