Skip to content

Commit

Permalink
439/mk/export openapi specs (#440)
Browse files Browse the repository at this point in the history
* feat(open-payments): export OpenAPI specs

* chore: add changelog

* chore: fix generate types command

* chore: add jsdoc
  • Loading branch information
mkurapov authored Mar 14, 2024
1 parent c0ea7c0 commit 1797345
Show file tree
Hide file tree
Showing 20 changed files with 169 additions and 54 deletions.
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

0 comments on commit 1797345

Please sign in to comment.