removing traces of aws and cleaning up workers/tests
This commit is contained in:
parent
2c953cbc57
commit
2b9fa53c5f
13 changed files with 63 additions and 57 deletions
17
package-lock.json
generated
17
package-lock.json
generated
|
|
@ -1807,12 +1807,6 @@
|
||||||
"integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==",
|
"integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"@types/aws-lambda": {
|
|
||||||
"version": "8.10.85",
|
|
||||||
"resolved": "https://registry.npmjs.org/@types/aws-lambda/-/aws-lambda-8.10.85.tgz",
|
|
||||||
"integrity": "sha512-cMRXVxb+NMb6EekKel1fPBfz2ZqE5cGhIS14G7FVUM4Bqilx0lHKnZbsDLWLSeckDpkvlp5six2F7UWyEEJSoQ==",
|
|
||||||
"dev": true
|
|
||||||
},
|
|
||||||
"@types/babel__core": {
|
"@types/babel__core": {
|
||||||
"version": "7.1.16",
|
"version": "7.1.16",
|
||||||
"resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.1.16.tgz",
|
"resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.1.16.tgz",
|
||||||
|
|
@ -1944,6 +1938,12 @@
|
||||||
"integrity": "sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw==",
|
"integrity": "sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"@types/uuid": {
|
||||||
|
"version": "8.3.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-8.3.4.tgz",
|
||||||
|
"integrity": "sha512-c/I8ZRb51j+pYGAu5CrFMRxqZ2ke4y2grEBO5AUjgSkSk+qT2Ea+OdWElz/OiMf5MNpn2b17kuVBwZLQJXzihw==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"@types/yargs": {
|
"@types/yargs": {
|
||||||
"version": "16.0.4",
|
"version": "16.0.4",
|
||||||
"resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.4.tgz",
|
"resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.4.tgz",
|
||||||
|
|
@ -6143,6 +6143,11 @@
|
||||||
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
|
||||||
"integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8="
|
"integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8="
|
||||||
},
|
},
|
||||||
|
"uuid": {
|
||||||
|
"version": "8.3.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
|
||||||
|
"integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg=="
|
||||||
|
},
|
||||||
"v8-compile-cache": {
|
"v8-compile-cache": {
|
||||||
"version": "2.3.0",
|
"version": "2.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz",
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,8 @@
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"dayjs": "^1.10.7",
|
"dayjs": "^1.10.7",
|
||||||
"puppeteer": "^11.0.0"
|
"puppeteer": "^11.0.0",
|
||||||
|
"uuid": "^8.3.2"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/core": "^7.16.0",
|
"@babel/core": "^7.16.0",
|
||||||
|
|
@ -32,9 +33,9 @@
|
||||||
"@rollup/plugin-commonjs": "^21.0.1",
|
"@rollup/plugin-commonjs": "^21.0.1",
|
||||||
"@rollup/plugin-node-resolve": "^13.0.6",
|
"@rollup/plugin-node-resolve": "^13.0.6",
|
||||||
"@rollup/plugin-typescript": "^8.3.0",
|
"@rollup/plugin-typescript": "^8.3.0",
|
||||||
"@types/aws-lambda": "^8.10.85",
|
|
||||||
"@types/jest": "^27.0.2",
|
"@types/jest": "^27.0.2",
|
||||||
"@types/puppeteer": "^5.4.4",
|
"@types/puppeteer": "^5.4.4",
|
||||||
|
"@types/uuid": "^8.3.4",
|
||||||
"@typescript-eslint/eslint-plugin": "^5.4.0",
|
"@typescript-eslint/eslint-plugin": "^5.4.0",
|
||||||
"@typescript-eslint/parser": "^5.4.0",
|
"@typescript-eslint/parser": "^5.4.0",
|
||||||
"babel-jest": "^27.3.1",
|
"babel-jest": "^27.3.1",
|
||||||
|
|
|
||||||
|
|
@ -8,10 +8,11 @@ export class Logger {
|
||||||
private static instance: LoggerInstance
|
private static instance: LoggerInstance
|
||||||
|
|
||||||
public static instantiate(
|
public static instantiate(
|
||||||
|
tag: string,
|
||||||
correlationId: string,
|
correlationId: string,
|
||||||
level = LogLevel.ERROR
|
level = LogLevel.ERROR
|
||||||
): LoggerInstance {
|
): LoggerInstance {
|
||||||
Logger.instance = new LoggerInstance(correlationId, level)
|
Logger.instance = new LoggerInstance(tag, correlationId, level)
|
||||||
return Logger.instance
|
return Logger.instance
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -33,10 +34,12 @@ export class Logger {
|
||||||
}
|
}
|
||||||
|
|
||||||
export class LoggerInstance {
|
export class LoggerInstance {
|
||||||
|
private readonly tag: string
|
||||||
private readonly correlationId: string
|
private readonly correlationId: string
|
||||||
private readonly level: LogLevel
|
private readonly level: LogLevel
|
||||||
|
|
||||||
public constructor(correlationId: string, level = LogLevel.ERROR) {
|
public constructor(tag: string, correlationId: string, level = LogLevel.ERROR) {
|
||||||
|
this.tag = tag
|
||||||
this.correlationId = correlationId
|
this.correlationId = correlationId
|
||||||
this.level = level
|
this.level = level
|
||||||
}
|
}
|
||||||
|
|
@ -60,8 +63,8 @@ export class LoggerInstance {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
let fmtString = '[%s] %s: %s'
|
let fmtString = '<%s> [%s] %s: %s'
|
||||||
const params: Array<unknown> = [this.correlationId, levelString, message]
|
const params: Array<unknown> = [this.tag, this.correlationId, levelString, message]
|
||||||
if (details) {
|
if (details) {
|
||||||
params.push(details)
|
params.push(details)
|
||||||
fmtString += ' - %O'
|
fmtString += ' - %O'
|
||||||
|
|
|
||||||
|
|
@ -101,7 +101,6 @@ const transformRequestBody = (body: string): ReservationRequest => {
|
||||||
try {
|
try {
|
||||||
json = JSON.parse(body)
|
json = JSON.parse(body)
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error(err)
|
|
||||||
throw new ValidationError(
|
throw new ValidationError(
|
||||||
'Invalid request',
|
'Invalid request',
|
||||||
ValidationErrorCode.INVALID_JSON
|
ValidationErrorCode.INVALID_JSON
|
||||||
|
|
@ -155,12 +154,12 @@ const validateRequestDateRange = (dateRange: DateRange): void => {
|
||||||
const validateRequestOpponent = (opponent?: Opponent): void => {
|
const validateRequestOpponent = (opponent?: Opponent): void => {
|
||||||
if (!opponent) return
|
if (!opponent) return
|
||||||
|
|
||||||
|
const idRegex = /^-1$|^[^-]\d+$/
|
||||||
|
const nameRegex = /^[A-Za-z0-9 -.'()]+$/
|
||||||
const { id, name } = opponent
|
const { id, name } = opponent
|
||||||
if (
|
if (
|
||||||
typeof id !== 'string' ||
|
!idRegex.test(id) ||
|
||||||
typeof name !== 'string' ||
|
!nameRegex.test(name)
|
||||||
id.length < 1 ||
|
|
||||||
name.length < 1
|
|
||||||
) {
|
) {
|
||||||
throw new ValidationError(
|
throw new ValidationError(
|
||||||
'Invalid request',
|
'Invalid request',
|
||||||
|
|
|
||||||
|
|
@ -1,5 +0,0 @@
|
||||||
import { Handler } from 'aws-lambda'
|
|
||||||
|
|
||||||
export const run: Handler = async (): Promise<void> => {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
5
src/workers/reservationRequestor/index.ts
Normal file
5
src/workers/reservationRequestor/index.ts
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
import { Worker } from "../types"
|
||||||
|
|
||||||
|
export const run: Worker<undefined, void> = async (): Promise<void> => {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
@ -8,7 +8,7 @@ import commonjs from '@rollup/plugin-commonjs'
|
||||||
export default {
|
export default {
|
||||||
input: path.join(__dirname, 'index.ts'),
|
input: path.join(__dirname, 'index.ts'),
|
||||||
output: {
|
output: {
|
||||||
file: './dist/reservationHandler/index.js',
|
file: './dist/reservationRequestor/index.js',
|
||||||
format: 'cjs',
|
format: 'cjs',
|
||||||
sourcemap: true,
|
sourcemap: true,
|
||||||
},
|
},
|
||||||
|
|
@ -1,10 +1,11 @@
|
||||||
import { Context, Handler } from 'aws-lambda'
|
|
||||||
import { Dayjs } from 'dayjs'
|
import { Dayjs } from 'dayjs'
|
||||||
|
import { v4 } from 'uuid'
|
||||||
|
|
||||||
import { Logger, LogLevel } from '../../common/logger'
|
import { Logger, LogLevel } from '../../common/logger'
|
||||||
import { Reservation } from '../../common/reservation'
|
import { Reservation } from '../../common/reservation'
|
||||||
import { ReservationRequest, validateJSONRequest } from '../../common/request'
|
import { ReservationRequest, validateJSONRequest } from '../../common/request'
|
||||||
import { scheduleDateToRequestReservation } from '../../common/schedule'
|
import { scheduleDateToRequestReservation } from '../../common/schedule'
|
||||||
|
import { Worker } from '../types'
|
||||||
|
|
||||||
export interface ScheduledReservationRequest {
|
export interface ScheduledReservationRequest {
|
||||||
reservationRequest: ReservationRequest
|
reservationRequest: ReservationRequest
|
||||||
|
|
@ -20,14 +21,10 @@ export interface ReservationSchedulerInput
|
||||||
dateRange: { start: string; end: string }
|
dateRange: { start: string; end: string }
|
||||||
}
|
}
|
||||||
|
|
||||||
export const handler: Handler<
|
export const run: Worker<ReservationSchedulerInput, ReservationSchedulerResult> = async (
|
||||||
ReservationSchedulerInput,
|
|
||||||
ReservationSchedulerResult
|
|
||||||
> = async (
|
|
||||||
payload: ReservationSchedulerInput,
|
payload: ReservationSchedulerInput,
|
||||||
context: Context
|
|
||||||
): Promise<ReservationSchedulerResult> => {
|
): Promise<ReservationSchedulerResult> => {
|
||||||
Logger.instantiate(context.awsRequestId, LogLevel.DEBUG)
|
Logger.instantiate('reservationScheduler', v4(), LogLevel.DEBUG)
|
||||||
Logger.debug('Handling event', { payload })
|
Logger.debug('Handling event', { payload })
|
||||||
let reservationRequest: ReservationRequest
|
let reservationRequest: ReservationRequest
|
||||||
try {
|
try {
|
||||||
1
src/workers/types.ts
Normal file
1
src/workers/types.ts
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
export type Worker<I = unknown, O = unknown> = (payload: I) => O | Promise<O>
|
||||||
|
|
@ -2,7 +2,7 @@ import { Logger, LogLevel } from '../../src/common/logger'
|
||||||
|
|
||||||
describe('Logger', () => {
|
describe('Logger', () => {
|
||||||
test('should create a single instance of LoggerInstance', () => {
|
test('should create a single instance of LoggerInstance', () => {
|
||||||
const a = Logger.instantiate('abc', LogLevel.DEBUG)
|
const a = Logger.instantiate('tag', 'abc', LogLevel.DEBUG)
|
||||||
const b = Logger.getInstance()
|
const b = Logger.getInstance()
|
||||||
|
|
||||||
expect(a).toStrictEqual(b)
|
expect(a).toStrictEqual(b)
|
||||||
|
|
@ -14,21 +14,21 @@ describe('Logger', () => {
|
||||||
jest.spyOn(console, 'log').mockImplementation(consoleLogSpy)
|
jest.spyOn(console, 'log').mockImplementation(consoleLogSpy)
|
||||||
jest.spyOn(console, 'error').mockImplementation(consoleErrorSpy)
|
jest.spyOn(console, 'error').mockImplementation(consoleErrorSpy)
|
||||||
|
|
||||||
Logger.instantiate('abc', LogLevel.DEBUG)
|
Logger.instantiate('tag', 'abc', LogLevel.DEBUG)
|
||||||
Logger.debug('first')
|
Logger.debug('first')
|
||||||
Logger.info('second')
|
Logger.info('second')
|
||||||
Logger.error('third', { errorMessage: 'test' })
|
Logger.error('third', { errorMessage: 'test' })
|
||||||
|
|
||||||
expect(consoleLogSpy).toHaveBeenCalledTimes(2)
|
expect(consoleLogSpy).toHaveBeenCalledTimes(2)
|
||||||
expect(consoleLogSpy).toHaveBeenNthCalledWith(
|
expect(consoleLogSpy).toHaveBeenNthCalledWith(
|
||||||
1, '[%s] %s: %s', 'abc', 'DEBUG', 'first'
|
1, '<%s> [%s] %s: %s', 'tag', 'abc', 'DEBUG', 'first'
|
||||||
)
|
)
|
||||||
expect(consoleLogSpy).toHaveBeenNthCalledWith(
|
expect(consoleLogSpy).toHaveBeenNthCalledWith(
|
||||||
2, '[%s] %s: %s', 'abc', 'INFO', 'second'
|
2, '<%s> [%s] %s: %s', 'tag', 'abc', 'INFO', 'second'
|
||||||
)
|
)
|
||||||
expect(consoleErrorSpy).toHaveBeenCalledTimes(1)
|
expect(consoleErrorSpy).toHaveBeenCalledTimes(1)
|
||||||
expect(consoleErrorSpy).toHaveBeenCalledWith(
|
expect(consoleErrorSpy).toHaveBeenCalledWith(
|
||||||
'[%s] %s: %s - %O', 'abc', 'ERROR', 'third', { "errorMessage": "test" }
|
'<%s> [%s] %s: %s - %O', 'tag', 'abc', 'ERROR', 'third', { "errorMessage": "test" }
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
@ -36,7 +36,7 @@ describe('Logger', () => {
|
||||||
const consoleLogSpy = jest.fn()
|
const consoleLogSpy = jest.fn()
|
||||||
jest.spyOn(console, 'log').mockImplementationOnce(consoleLogSpy)
|
jest.spyOn(console, 'log').mockImplementationOnce(consoleLogSpy)
|
||||||
|
|
||||||
Logger.instantiate('abc', LogLevel.INFO)
|
Logger.instantiate('tag', 'abc', LogLevel.INFO)
|
||||||
Logger.debug('should\'t appear')
|
Logger.debug('should\'t appear')
|
||||||
|
|
||||||
expect(consoleLogSpy).not.toHaveBeenCalled()
|
expect(consoleLogSpy).not.toHaveBeenCalled()
|
||||||
|
|
|
||||||
|
|
@ -8,14 +8,17 @@ import {
|
||||||
} from '../../src/common/request'
|
} from '../../src/common/request'
|
||||||
|
|
||||||
describe('request', () => {
|
describe('request', () => {
|
||||||
|
|
||||||
|
const testDate = dayjs().add(1, 'day')
|
||||||
|
|
||||||
describe('validateStringRequest', () => {
|
describe('validateStringRequest', () => {
|
||||||
test('should return ReservationRequest', () => {
|
test('should return ReservationRequest', () => {
|
||||||
const body = JSON.stringify({
|
const body = JSON.stringify({
|
||||||
username: 'collin',
|
username: 'collin',
|
||||||
password: '123abc',
|
password: '123abc',
|
||||||
dateRange: {
|
dateRange: {
|
||||||
start: '2021-12-25T12:34:56Z',
|
start: testDate.clone().toISOString(),
|
||||||
end: '2021-12-25T12:45:56Z'
|
end: testDate.add(15, 'minutes').toISOString(),
|
||||||
},
|
},
|
||||||
opponent: {
|
opponent: {
|
||||||
id: '123',
|
id: '123',
|
||||||
|
|
@ -70,7 +73,7 @@ describe('request', () => {
|
||||||
password: '123abc',
|
password: '123abc',
|
||||||
dateRange: {
|
dateRange: {
|
||||||
start: 'monkey',
|
start: 'monkey',
|
||||||
end: '2021-12-25T12:45:56Z'
|
end: testDate.add(15, 'minutes').toISOString(),
|
||||||
},
|
},
|
||||||
opponent: {
|
opponent: {
|
||||||
id: '123',
|
id: '123',
|
||||||
|
|
@ -106,8 +109,8 @@ describe('request', () => {
|
||||||
username: 'collin',
|
username: 'collin',
|
||||||
password: '123abc',
|
password: '123abc',
|
||||||
dateRange: {
|
dateRange: {
|
||||||
start: '2021-12-25T12:34:56Z',
|
start: testDate.clone().toISOString(),
|
||||||
end: '2021-12-25T12:45:56Z'
|
end: testDate.add(15, 'minutes').toISOString()
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
@ -115,17 +118,17 @@ describe('request', () => {
|
||||||
})
|
})
|
||||||
|
|
||||||
test.each([
|
test.each([
|
||||||
{ id: 123, name: 'collin' },
|
{ id: '-50', name: 'collin' },
|
||||||
{ id: '', name: 'collin' },
|
{ id: 'abc', name: 'collin' },
|
||||||
{ id: '123', name: true },
|
{ id: '-1', name: '*!@#' },
|
||||||
{ id: '123', name: '' },
|
{ id: '123', name: '!@#' },
|
||||||
])('should fail for invalid opponent id', (opponent) => {
|
])('should fail for invalid opponent $id, $name', (opponent) => {
|
||||||
const body = JSON.stringify({
|
const body = JSON.stringify({
|
||||||
username: 'collin',
|
username: 'collin',
|
||||||
password: '123abc',
|
password: '123abc',
|
||||||
dateRange: {
|
dateRange: {
|
||||||
start: '2021-12-25T12:34:56Z',
|
start: testDate.clone().toISOString(),
|
||||||
end: '2021-12-25T12:45:56Z'
|
end: testDate.add(15, 'minutes').toISOString(),
|
||||||
},
|
},
|
||||||
opponent,
|
opponent,
|
||||||
})
|
})
|
||||||
|
|
@ -140,8 +143,8 @@ describe('request', () => {
|
||||||
username: 'collin',
|
username: 'collin',
|
||||||
password: '123abc',
|
password: '123abc',
|
||||||
dateRange: {
|
dateRange: {
|
||||||
start: '2021-12-25T12:34:56Z',
|
start: testDate.clone().toISOString(),
|
||||||
end: '2021-12-25T12:45:56Z'
|
end: testDate.add(15, 'minutes').toISOString()
|
||||||
},
|
},
|
||||||
opponent: {
|
opponent: {
|
||||||
id: '123',
|
id: '123',
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import dayjs from 'dayjs'
|
import dayjs from 'dayjs'
|
||||||
import { ValidationError, ValidationErrorCode } from '../../src/common/request'
|
import { ValidationError, ValidationErrorCode } from '../../src/common/request'
|
||||||
import { handler, ReservationSchedulerInput, ReservationSchedulerResult } from '../../src/lambdas/reservationScheduler'
|
import { run, ReservationSchedulerInput, ReservationSchedulerResult } from '../../src/workers/reservationScheduler'
|
||||||
|
|
||||||
jest.mock('../../src/common/logger')
|
jest.mock('../../src/common/logger')
|
||||||
|
|
||||||
|
|
@ -16,8 +16,7 @@ describe('reservationScheduler', () => {
|
||||||
opponent: { id: "123", name: "collin" }
|
opponent: { id: "123", name: "collin" }
|
||||||
}
|
}
|
||||||
|
|
||||||
// @ts-expect-error - Stubbing AWS context
|
await expect(run(payload)).resolves
|
||||||
await expect(handler(payload, { awsRequestId: '1234' }, undefined)).resolves
|
|
||||||
.toMatchObject<ReservationSchedulerResult>({
|
.toMatchObject<ReservationSchedulerResult>({
|
||||||
scheduledReservationRequest: {
|
scheduledReservationRequest: {
|
||||||
reservationRequest: {
|
reservationRequest: {
|
||||||
|
|
@ -39,8 +38,7 @@ describe('reservationScheduler', () => {
|
||||||
opponent: { id: "123", name: "collin" }
|
opponent: { id: "123", name: "collin" }
|
||||||
}
|
}
|
||||||
|
|
||||||
// @ts-expect-error - Stubbing AWS context
|
await expect(run(payload)).resolves.toMatchObject<ReservationSchedulerResult>({
|
||||||
await expect(handler(payload, { awsRequestId: '1234' }, undefined)).resolves.toMatchObject<ReservationSchedulerResult>({
|
|
||||||
scheduledReservationRequest: {
|
scheduledReservationRequest: {
|
||||||
reservationRequest: {
|
reservationRequest: {
|
||||||
username: 'collin',
|
username: 'collin',
|
||||||
|
|
@ -63,8 +61,7 @@ describe('reservationScheduler', () => {
|
||||||
opponent: { id: "123", name: "collin" }
|
opponent: { id: "123", name: "collin" }
|
||||||
}
|
}
|
||||||
|
|
||||||
// @ts-expect-error - Stubbing AWS context
|
await expect(run(payload))
|
||||||
await expect(handler(payload, { awsRequestId: '1234' }, undefined))
|
|
||||||
.rejects
|
.rejects
|
||||||
.toThrowError(new ValidationError('Invalid request', ValidationErrorCode.INVALID_REQUEST_BODY))
|
.toThrowError(new ValidationError('Invalid request', ValidationErrorCode.INVALID_REQUEST_BODY))
|
||||||
})
|
})
|
||||||
Loading…
Add table
Reference in a new issue