Skip to content

Commit

Permalink
feat: #1369 The Web Components Config API should store config by appId (
Browse files Browse the repository at this point in the history
  • Loading branch information
NghiaPham authored Jun 19, 2020
1 parent 2f4094c commit e09eb0c
Show file tree
Hide file tree
Showing 12 changed files with 228 additions and 89 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,9 @@ if (Cypress.env('PACKAGE_NAME') === 'all' || Cypress.env('PACKAGE_NAME') === 'we
})
})
})
it('user should able to call web-components-config-server /v1/web-components-config/<customer_id>', () => {
it('user should able to call web-components-config-server /v1/web-components-config/<customer_id>/<app_id>', () => {
cy.request({
url: `${WEB_COMPONENTS_CONFIG_API_URL}/v1/web-components-config/ZZA`,
url: `${WEB_COMPONENTS_CONFIG_API_URL}/v1/web-components-config/TEST/1`,
method: 'GET',
headers: {
'Content-Type': 'application/json',
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { validateGetById, validateCreate, validateUpdate, validateDelete, validateFollowSchema } from '../validators'
import { validateGetById, validateCreate, validatePatch, validateDelete, validateFollowSchema } from '../validators'

describe('validateGetById', () => {
it('should return correctly', () => {
const result = validateGetById({ customerId: 'id1' })
const result = validateGetById({ customerId: 'id1', appId: 'id1' })
expect(result).toBe(true)
})

Expand Down Expand Up @@ -38,6 +38,7 @@ describe('validateCreate', () => {
it('should return correctly', () => {
const result = validateCreate({
customerId: 'id1',
appId: 'id1',
appointmentLength: 10,
appointmentTimeGap: 15,
appointmentTypes: ['type1', 'type2'],
Expand Down Expand Up @@ -83,6 +84,7 @@ describe('validateCreate', () => {
expect(() => {
validateCreate({
customerId: 'id1',
appId: 'id1',
appointmentLength: 10,
appointmentTimeGap: 15,
appointmentTypes: ['type1', 'type2'],
Expand All @@ -93,9 +95,9 @@ describe('validateCreate', () => {
})
})

describe('validateUpdate', () => {
describe('validatePatch', () => {
it('should return correctly', () => {
const result = validateUpdate({ customerId: 'id1', appointmentLength: 20 })
const result = validatePatch({ customerId: 'id1', appId: 'id1', appointmentLength: 20 })
expect(result).toBe(true)
})

Expand All @@ -104,7 +106,7 @@ describe('validateUpdate', () => {
error.message = 'Invalid params'
error.code = '400'
expect(() => {
validateUpdate({ customerIdFake: 'id1', appointmentLength: 20 })
validatePatch({ customerIdFake: 'id1', appointmentLength: 20 })
}).toThrowError(error)
})

Expand All @@ -113,7 +115,7 @@ describe('validateUpdate', () => {
error.message = 'Invalid params'
error.code = '400'
expect(() => {
validateUpdate({ customerId: 'id1', invalidParam: 'param' })
validatePatch({ customerId: 'id1', invalidParam: 'param' })
}).toThrowError(error)
})

Expand All @@ -122,14 +124,14 @@ describe('validateUpdate', () => {
error.message = 'Invalid daysOfWeek.'
error.code = '400'
expect(() => {
validateUpdate({ customerId: 'id1', appointmentLength: 20, daysOfWeek: [9] })
validatePatch({ customerId: 'id1', appId: 'id1', appointmentLength: 20, daysOfWeek: [9] })
}).toThrowError(error)
})
})

describe('validateDelete', () => {
it('should return correctly', () => {
const result = validateDelete({ customerId: 'id1' })
const result = validateDelete({ customerId: 'id1', appId: 'id1' })
expect(result).toBe(true)
})

Expand All @@ -150,14 +152,6 @@ describe('validateDelete', () => {
validateDelete({ customerId: 'id1', invalidParam: 'param' })
}).toThrowError(error)
})
it('should throw error with invalid schema', () => {
const error: NodeJS.ErrnoException = new Error()
error.message = 'Invalid daysOfWeek.'
error.code = '400'
expect(() => {
validateDelete({ customerId: 'id1', daysOfWeek: [9] })
}).toThrowError(error)
})
})

describe('validateFollowSchema', () => {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,28 +1,87 @@
import { AppRequest, AppResponse } from '../../../app'
import {
webComponentsConfigGetByIdHandler,
webComponentsConfigPutHandler,
webComponentsConfigPatchHandler,
webComponentsConfigDeleteHandler,
webComponentsConfigPutHandler,
webComponentsConfigPostHandler,
} from '../web-components-config'
import { webComponentConfig } from '../__mocks__/web-components-config'

jest.mock('../api', () => {
return {
getConfigByClientId: jest.fn(() => Promise.resolve(webComponentConfig)),
createConfig: jest.fn(() => Promise.resolve(webComponentConfig)),
updateConfig: jest.fn(() => Promise.resolve(webComponentConfig)),
patchConfig: jest.fn(() => Promise.resolve(webComponentConfig)),
deleteConfig: jest.fn(() => Promise.resolve(webComponentConfig)),
putConfig: jest.fn(() => Promise.resolve(webComponentConfig)),
}
})

describe('web-components-config', () => {
describe('webComponentsConfigGetByIdHandler', () => {
describe('webComponentsConfigPutHandler', () => {
it('should update correctly and return service is running', async done => {
const mockRequest = ({
traceId: 'mockTraceId',
headers: {},
body: {
customerId: 'DXX',
appId: '1',
appointmentLength: 1,
daysOfWeek: [1],
appointmentTimeGap: 5,
appointmentTypes: [],
negotiatorIds: [],
},
params: {
clientId: '1',
appId: '1',
traceId: 'mockUUID',
},
} as unknown) as AppRequest
const mockResponse = ({
send: jest.fn().mockReturnValue({}),
status: jest.fn().mockReturnValue(200),
sendStatus: jest.fn().mockReturnValue(200),
json: jest.fn().mockReturnValue({}),
} as unknown) as AppResponse
await webComponentsConfigPutHandler(mockRequest, mockResponse)
setTimeout(() => {
expect(mockResponse.send).toBeCalledWith(webComponentConfig)
done()
}, 1000)
})
})

describe('webComponentsConfigPostHandler', () => {
it('should run correctly and return service is running', async done => {
const mockRequest = ({
traceId: 'mockTraceId',
headers: {},
body: {},
body: {
customerId: 'DXX',
appId: '1',
appointmentLength: 303,
appointmentTimeGap: 5,
appointmentTypes: [
{
M: {
value: {
S: 'value1',
},
id: {
S: 'id1',
},
},
},
],
negotiatorIds: [
{
S: 'AAAN',
},
],
daysOfWeek: [],
},
params: {
clientId: '1',
traceId: 'mockUUID',
Expand All @@ -34,20 +93,20 @@ describe('web-components-config', () => {
sendStatus: jest.fn().mockReturnValue(200),
json: jest.fn().mockReturnValue({}),
} as unknown) as AppResponse
await webComponentsConfigGetByIdHandler(mockRequest, mockResponse)
await webComponentsConfigPostHandler(mockRequest, mockResponse)
setTimeout(() => {
expect(mockResponse.send).toBeCalledWith(webComponentConfig)
done()
}, 1000)
})
})

describe('webComponentsConfigPutHandler', () => {
describe('webComponentsConfigGetByIdHandler', () => {
it('should run correctly and return service is running', async done => {
const mockRequest = ({
traceId: 'mockTraceId',
headers: {},
body: webComponentConfig,
body: {},
params: {
clientId: '1',
traceId: 'mockUUID',
Expand All @@ -59,7 +118,7 @@ describe('web-components-config', () => {
sendStatus: jest.fn().mockReturnValue(200),
json: jest.fn().mockReturnValue({}),
} as unknown) as AppResponse
await webComponentsConfigPutHandler(mockRequest, mockResponse)
await webComponentsConfigGetByIdHandler(mockRequest, mockResponse)
setTimeout(() => {
expect(mockResponse.send).toBeCalledWith(webComponentConfig)
done()
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import dynamoDBMapper from '@/dynamodb-mapper'
import { FunctionExpression, AttributePath } from '@aws/dynamodb-expressions'
import logger from '@/logger'
import { CreateParams, DeleteParams, UpdateParams, GetByClientIdParams } from '@/schemas/api-types'
import { WebComponentConfig } from '@/schemas/schema'
Expand All @@ -20,9 +21,16 @@ export const getConfigByClientId = async ({ traceId, data }: GetByClientIdParams

export const createConfig = async ({ traceId, data }: CreateParams): Promise<WebComponentConfig> => {
try {
logger.info('Creating config...', { traceId, data })
const itemToCreate = generateSchemaItem(data)
const result = await dynamoDBMapper.put(itemToCreate)
const result = await dynamoDBMapper.put(itemToCreate, {
condition: {
type: 'And',
conditions: [
new FunctionExpression('attribute_not_exists', new AttributePath('customerId')),
new FunctionExpression('attribute_not_exists', new AttributePath('appId')),
],
},
})
logger.info('Create config successfully', { traceId, result })
return result
} catch (error) {
Expand All @@ -31,13 +39,26 @@ export const createConfig = async ({ traceId, data }: CreateParams): Promise<Web
}
}

export const updateConfig = async ({ traceId, data }: UpdateParams): Promise<WebComponentConfig> => {
export const patchConfig = async ({ traceId, data }: UpdateParams): Promise<WebComponentConfig> => {
try {
logger.info('Updating config...', { traceId, data })
const { customerId, ...rest } = data
const oldItem = await getConfigByClientId({ traceId, data: { customerId } })
logger.info('Patching config...', { traceId, data })
const { customerId, appId, ...rest } = data
const oldItem = await getConfigByClientId({ traceId, data: { customerId, appId } })
const itemToUpdate = generateSchemaItem({ ...oldItem, ...rest })
const result = await dynamoDBMapper.update(itemToUpdate)
logger.info('Patch config successfully', { traceId, result })
return result
} catch (error) {
await logger.error('Patch config failed', { traceId, error: stringifyError(error) })
throw error
}
}

export const putConfig = async ({ traceId, data }): Promise<WebComponentConfig> => {
try {
logger.info('Updating config...', { traceId, data })
const itemToUpdate = generateSchemaItem(data)
const result = await dynamoDBMapper.put(itemToUpdate)
logger.info('Update config successfully', { traceId, result })
return result
} catch (error) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,22 +1,24 @@
export const ALL_PARAMS = [
'customerId',
'appId',
'appointmentLength',
'appointmentTimeGap',
'appointmentTypes',
'negotiatorIds',
'daysOfWeek',
]
export const GET_BY_ID_REQUIRED_PARAMS = ['customerId']
export const GET_BY_ID_REQUIRED_PARAMS = ['customerId', 'appId']
export const CREATE_REQUIRED_PARAMS = [
'customerId',
'appId',
'appointmentLength',
'appointmentTimeGap',
'appointmentTypes',
'negotiatorIds',
'daysOfWeek',
]
export const UPDATE_REQUIRED_PARAMS = ['customerId']
export const DELETE_REQUIRED_PARAMS = ['customerId']
export const PATCH_REQUIRED_PARAMS = ['customerId', 'appId']
export const DELETE_REQUIRED_PARAMS = ['customerId', 'appId']

/**
* Used to validate if the data object follow our schema type
Expand Down Expand Up @@ -70,6 +72,7 @@ export const validateGetById = (data: { [key: string]: any }) => {

export const validateCreate = (data: { [key: string]: any }) => {
const dataKeys = Object.keys(data)
console.log(dataKeys)

// check if param keys are valid
const isParamsValid =
Expand All @@ -93,12 +96,12 @@ export const validateCreate = (data: { [key: string]: any }) => {
return true
}

export const validateUpdate = (data: { [key: string]: any }) => {
export const validatePatch = (data: { [key: string]: any }) => {
const dataKeys = Object.keys(data)

// check if param keys are valid
const isParamsValid =
dataKeys.some(key => UPDATE_REQUIRED_PARAMS.includes(key)) && dataKeys.every(key => ALL_PARAMS.includes(key))
dataKeys.some(key => CREATE_REQUIRED_PARAMS.includes(key)) && dataKeys.every(key => ALL_PARAMS.includes(key))
// check if param is a valid schema item
const errorMessage = validateFollowSchema(data)

Expand All @@ -124,21 +127,13 @@ export const validateDelete = (data: { [key: string]: any }) => {
// check if param keys are valid
const isParamsValid =
dataKeys.some(key => GET_BY_ID_REQUIRED_PARAMS.includes(key)) && dataKeys.every(key => ALL_PARAMS.includes(key))
// check if param is a valid schema item
const errorMessage = validateFollowSchema(data)

if (!isParamsValid) {
const error: NodeJS.ErrnoException = new Error()
error.message = 'Invalid params'
error.code = '400'
throw error
}
if (errorMessage) {
const error: NodeJS.ErrnoException = new Error()
error.message = errorMessage
error.code = '400'
throw error
}

return true
}
Loading

0 comments on commit e09eb0c

Please sign in to comment.