-
Notifications
You must be signed in to change notification settings - Fork 89
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): adding open-payments package #669
Changes from 13 commits
d3470c4
7f968c3
79e7cd7
32e2a31
050ebef
9893029
c15fdbc
130e310
e067011
dc28878
39b561b
9393e47
3c3a0b7
120dcae
43335f6
f560c79
e11ec65
0dad54d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
'use strict' | ||
// eslint-disable-next-line @typescript-eslint/no-var-requires | ||
const baseConfig = require('../../jest.config.base.js') | ||
// eslint-disable-next-line @typescript-eslint/no-var-requires | ||
const packageName = require('./package.json').name | ||
|
||
module.exports = { | ||
...baseConfig, | ||
clearMocks: true, | ||
roots: [`<rootDir>/packages/${packageName}`], | ||
testRegex: `(packages/${packageName}/.*/__tests__/.*|\\.(test|spec))\\.tsx?$`, | ||
moduleDirectories: [`node_modules`, `packages/${packageName}/node_modules`], | ||
modulePaths: [`<rootDir>/packages/${packageName}/src/`], | ||
id: packageName, | ||
displayName: packageName, | ||
rootDir: '../..' | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
{ | ||
"name": "open-payments", | ||
"description": "", | ||
"main": "dist/index.js", | ||
"types": "dist/index.d.ts", | ||
"files": [ | ||
"dist/**/*" | ||
], | ||
"scripts": { | ||
"build": "pnpm clean && tsc --build tsconfig.json", | ||
"clean": "rm -fr dist/", | ||
"generate:types": "npx ts-node scripts/generate-types.ts", | ||
"prepack": "pnpm build", | ||
"test": "jest --passWithNoTests" | ||
}, | ||
"devDependencies": { | ||
"@types/node": "^18.7.12", | ||
"nock": "^13.2.9", | ||
"openapi-typescript": "^4.5.0", | ||
"ts-node": "^10.7.0", | ||
"typescript": "^4.3.0" | ||
}, | ||
"dependencies": { | ||
"axios": "^1.1.2" | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
import fs from 'fs' | ||
import openapiTS from 'openapi-typescript' | ||
import config from '../src/config' | ||
;(async () => { | ||
try { | ||
const output = await openapiTS(config.OPEN_PAYMENTS_OPEN_API_URL) | ||
const fileName = 'src/generated/types.ts' | ||
|
||
fs.writeFile(fileName, output, (error) => { | ||
if (error) { | ||
console.log(`Error when writing types to ${fileName}`, { error }) | ||
} | ||
}) | ||
} catch (error) { | ||
console.log('Error when generating types', { | ||
error | ||
}) | ||
} | ||
})() |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
import { createAxiosInstance, get } from './client' | ||
import nock from 'nock' | ||
|
||
describe('open-payments', (): void => { | ||
describe('createAxiosInstance', (): void => { | ||
test('sets timeout properly', async (): Promise<void> => { | ||
expect(createAxiosInstance({ timeout: 1000 }).defaults.timeout).toBe(1000) | ||
}) | ||
test('sets Content-Type header properly', async (): Promise<void> => { | ||
expect( | ||
createAxiosInstance().defaults.headers.common['Content-Type'] | ||
).toBe('application/json') | ||
}) | ||
}) | ||
|
||
describe('get', (): void => { | ||
const axiosInstance = createAxiosInstance() | ||
const baseUrl = 'http://localhost:1000' | ||
|
||
beforeEach(() => { | ||
jest.spyOn(axiosInstance, 'get') | ||
}) | ||
|
||
test('sets headers properly if accessToken provided', async (): Promise<void> => { | ||
nock(baseUrl) | ||
.get('/incoming-payment') | ||
.reply(200, () => ({ | ||
validReceiver: 0 | ||
})) | ||
|
||
await get(axiosInstance, { | ||
url: `${baseUrl}/incoming-payment`, | ||
accessToken: 'accessToken' | ||
Check failure Code scanning / CodeQL Hard-coded credentials
The hard-coded value "accessToken" is used as [authorization header](1).
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. (this is a test file) |
||
}) | ||
|
||
expect(axiosInstance.get).toHaveBeenCalledWith( | ||
`${baseUrl}/incoming-payment`, | ||
{ | ||
headers: { | ||
Authorization: 'GNAP accessToken', | ||
Check failure Code scanning / CodeQL Hard-coded credentials
The hard-coded value "GNAP accessToken" is used as [authorization header](1).
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. (this is a test file) |
||
Signature: 'TODO', | ||
'Signature-Input': 'TODO' | ||
} | ||
} | ||
) | ||
}) | ||
|
||
test('sets headers properly if accessToken is not provided', async (): Promise<void> => { | ||
nock(baseUrl) | ||
.get('/incoming-payment') | ||
.reply(200, () => ({ | ||
validReceiver: 0 | ||
})) | ||
|
||
await get(axiosInstance, { | ||
url: `${baseUrl}/incoming-payment` | ||
}) | ||
|
||
expect(axiosInstance.get).toHaveBeenCalledWith( | ||
`${baseUrl}/incoming-payment`, | ||
{ | ||
headers: {} | ||
} | ||
) | ||
}) | ||
}) | ||
}) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
import { ILPStreamConnection, IncomingPayment } from './types' | ||
import axios, { AxiosInstance } from 'axios' | ||
import config from './config' | ||
|
||
interface CreateOpenPaymentClientArgs { | ||
timeout?: number | ||
} | ||
|
||
interface GetArgs { | ||
url: string | ||
accessToken?: string | ||
} | ||
|
||
export interface OpenPaymentsClient { | ||
incomingPayment: { | ||
get(args: GetArgs): Promise<IncomingPayment> | ||
} | ||
ilpStreamConnection: { | ||
get(args: GetArgs): Promise<ILPStreamConnection> | ||
} | ||
} | ||
|
||
export const get = async <T>( | ||
axios: AxiosInstance, | ||
args: GetArgs | ||
): Promise<T> => { | ||
const { url, accessToken } = args | ||
|
||
const { data } = await axios.get(url, { | ||
headers: accessToken | ||
? { | ||
Authorization: `GNAP ${accessToken}`, | ||
Signature: 'TODO', | ||
'Signature-Input': 'TODO' | ||
} | ||
: {} | ||
}) | ||
|
||
return data | ||
} | ||
|
||
export const createAxiosInstance = ( | ||
args?: CreateOpenPaymentClientArgs | ||
): AxiosInstance => { | ||
const axiosInstance = axios.create({ | ||
timeout: args?.timeout ?? config.DEFAULT_REQUEST_TIMEOUT | ||
}) | ||
|
||
axiosInstance.defaults.headers.common['Content-Type'] = 'application/json' | ||
|
||
return axiosInstance | ||
} | ||
|
||
export const createClient = ( | ||
args?: CreateOpenPaymentClientArgs | ||
): OpenPaymentsClient => { | ||
const axios = createAxiosInstance(args) | ||
|
||
return { | ||
incomingPayment: { | ||
get: (args: GetArgs) => get<IncomingPayment>(axios, args) | ||
}, | ||
ilpStreamConnection: { | ||
get: (args: GetArgs) => get<ILPStreamConnection>(axios, args) | ||
} | ||
} | ||
} | ||
Comment on lines
+54
to
+67
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Wanted to keep very simple for the first PR, will be adding open-api validation next. |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
export default { | ||
OPEN_PAYMENTS_OPEN_API_URL: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. want to make sure the same URL is used for type generation and (soon to be added) open API response validation |
||
'https://raw.githubusercontent.com/interledger/open-payments/main/open-api-spec.yaml', | ||
mkurapov marked this conversation as resolved.
Show resolved
Hide resolved
|
||
DEFAULT_REQUEST_TIMEOUT: 3_000 | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wrested quite a bit with this, but I wasn't able to use the newest version of this library. Using the programmatic way of generating types via
openapiTS
anything above v5 does not work and result inERR_REQUIRE_ESM
errors. Versions 5 and above do not support backwards compatibility with CommonJS, especially given the fact that havingtype: module
inpackage.json
breaks using this package as a workspace in the/backend
project.one two three are the related issues in the
openapi-typescript
repo.