Skip to content
Closed
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
55 changes: 55 additions & 0 deletions src/routes/admin/proxy/delete.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
/*********************************************************************
* Copyright (c) Intel Corporation 2025
* SPDX-License-Identifier: Apache-2.0
**********************************************************************/

import { createSpyObj } from '../../../test/helper/jest.js'
import { deleteProxyProfile } from './delete.js'
import { jest } from '@jest/globals'
import { type SpyInstance, spyOn } from 'jest-mock'

describe('Proxy - Delete', () => {
let resSpy
let req
let deleteSpy: SpyInstance<any>
let checkSpy: SpyInstance<any>

beforeEach(() => {
resSpy = createSpyObj('Response', [
'status',
'json',
'end',
'send'
])
req = {
db: { proxyConfigs: { delete: jest.fn(), checkProfileExits: jest.fn() } },
query: {},
params: { proxyName: 'proxyConfigName' },
tenantId: '',
method: 'DELETE'
}
deleteSpy = spyOn(req.db.proxyConfigs, 'delete').mockResolvedValue({})
checkSpy = spyOn(req.db.proxyConfigs, 'checkProfileExits').mockResolvedValue(true)

resSpy.status.mockReturnThis()
resSpy.json.mockReturnThis()
resSpy.send.mockReturnThis()
})
it('should delete', async () => {
checkSpy = spyOn(req.db.proxyConfigs, 'checkProfileExits').mockResolvedValue(true)
await deleteProxyProfile(req, resSpy)
expect(resSpy.status).toHaveBeenCalledWith(204)
})
it('should handle not found', async () => {
checkSpy = spyOn(req.db.proxyConfigs, 'checkProfileExits').mockResolvedValue(false)
await deleteProxyProfile(req, resSpy)
expect(resSpy.status).toHaveBeenCalledWith(404)
})
it('should handle error', async () => {
checkSpy = spyOn(req.db.proxyConfigs, 'checkProfileExits').mockResolvedValue(true)
spyOn(req.db.proxyConfigs, 'delete').mockRejectedValue(null)
await deleteProxyProfile(req, resSpy)
expect(deleteSpy).toHaveBeenCalledWith('proxyConfigName', req.tenantId)
expect(resSpy.status).toHaveBeenCalledWith(500)
})
})
34 changes: 34 additions & 0 deletions src/routes/admin/proxy/delete.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/*********************************************************************
* Copyright (c) Intel Corporation 2022
* SPDX-License-Identifier: Apache-2.0
**********************************************************************/

import Logger from '../../../Logger.js'
import { MqttProvider } from '../../../utils/MqttProvider.js'
import { type Request, type Response } from 'express'
import handleError from '../../../utils/handleError.js'
import { RPSError } from '../../../utils/RPSError.js'
import { API_UNEXPECTED_EXCEPTION, NOT_FOUND_EXCEPTION, NOT_FOUND_MESSAGE } from '../../../utils/constants.js'

export async function deleteProxyProfile(req: Request, res: Response): Promise<void> {
const { proxyName } = req.params
const tenantId = req.tenantId || ''
const log = new Logger('deleteProxyProfile')
try {
const proxyConfigExists: boolean = await req.db.proxyConfigs.checkProfileExits(proxyName, tenantId)
if (!proxyConfigExists) {
throw new RPSError(NOT_FOUND_MESSAGE('Proxy', proxyName), NOT_FOUND_EXCEPTION)
} else {
const results: boolean | null = await req.db.proxyConfigs.delete(proxyName, tenantId)
if (results) {
log.verbose(`Deleted proxy profile : ${proxyName}`)
MqttProvider.publishEvent('success', ['deleteProxyProfile'], `Deleted proxy configuration : ${proxyName}`)
res.status(204).json(results).end()
} else {
throw new RPSError(API_UNEXPECTED_EXCEPTION('Error deleting proxy configuration'))
}
}
} catch (error) {
handleError(log, 'deleteProxyProfile', req, res, error)
}
}
64 changes: 64 additions & 0 deletions src/routes/admin/proxy/edit.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
/*********************************************************************
* Copyright (c) Intel Corporation 2025
* SPDX-License-Identifier: Apache-2.0
**********************************************************************/

import { createSpyObj } from '../../../test/helper/jest.js'
import { jest } from '@jest/globals'
import { type SpyInstance, spyOn } from 'jest-mock'
import { editProxyProfile } from './edit.js'

describe('Proxy - Edit', () => {
let resSpy
let req
let updateSpy: SpyInstance<any>
let getSpy: SpyInstance<any>

beforeEach(() => {
resSpy = createSpyObj('Response', [
'status',
'json',
'end',
'send'
])
req = {
db: { proxyConfigs: { update: jest.fn(), getByName: jest.fn() } },
query: {},
params: { proxyConfigAddress: 'proxyConfigAddress' },
tenantId: '',
method: 'PATCH',
body: {
address: 'intel.com',
infoFormat: 201,
networkDnsSuffix: 'vprodemo',
port: 443,
tenantId: 'foo'
}
}
updateSpy = spyOn(req.db.proxyConfigs, 'update').mockResolvedValue({})
getSpy = spyOn(req.db.proxyConfigs, 'getByName').mockResolvedValue({})

resSpy.status.mockReturnThis()
resSpy.json.mockReturnThis()
resSpy.send.mockReturnThis()
})
it('should update', async () => {
getSpy = spyOn(req.db.proxyConfigs, 'getByName').mockResolvedValue({})
await editProxyProfile(req, resSpy)
expect(getSpy).toHaveBeenCalledWith(req.body.proxyName, req.tenantId)
expect(resSpy.status).toHaveBeenCalledWith(200)
})
it('should handle not found', async () => {
getSpy = spyOn(req.db.proxyConfigs, 'getByName').mockResolvedValue(null)
await editProxyProfile(req, resSpy)
expect(getSpy).toHaveBeenCalledWith(req.body.proxyName, req.tenantId)
expect(resSpy.status).toHaveBeenCalledWith(404)
})
it('should handle error', async () => {
getSpy = spyOn(req.db.proxyConfigs, 'getByName').mockResolvedValue({})
spyOn(req.db.proxyConfigs, 'update').mockRejectedValue(null)
await editProxyProfile(req, resSpy)
expect(getSpy).toHaveBeenCalledWith(req.body.proxyName, req.tenantId)
expect(resSpy.status).toHaveBeenCalledWith(500)
})
})
46 changes: 46 additions & 0 deletions src/routes/admin/proxy/edit.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/*********************************************************************
* Copyright (c) Intel Corporation 2022
* SPDX-License-Identifier: Apache-2.0
**********************************************************************/

import Logger from '../../../Logger.js'
import { MqttProvider } from '../../../utils/MqttProvider.js'
import { type Request, type Response } from 'express'
import handleError from '../../../utils/handleError.js'
import { RPSError } from '../../../utils/RPSError.js'
import { API_UNEXPECTED_EXCEPTION, NOT_FOUND_EXCEPTION, NOT_FOUND_MESSAGE } from '../../../utils/constants.js'
import { ProxyConfig } from 'models/RCS.Config.js'

export async function editProxyProfile(req: Request, res: Response): Promise<void> {
const newProxy: ProxyConfig = req.body
newProxy.tenantId = req.tenantId || ''
const log = new Logger('editProxyProfile')
try {
const oldProxy: ProxyConfig | null = await req.db.proxyConfigs.getByName(newProxy.proxyName, req.tenantId)

if (oldProxy == null) {
throw new RPSError(NOT_FOUND_MESSAGE('Proxy', newProxy.proxyName), NOT_FOUND_EXCEPTION)
} else {
const proxiConfig: ProxyConfig = await getUpdatedData(newProxy, oldProxy)
const results = await req.db.proxyConfigs.update(proxiConfig)
if (results) {
MqttProvider.publishEvent('success', ['editProxyConfig'], `Updated proxy configuration : ${newProxy.proxyName}`)
res.status(200).json(results).end()
} else {
throw new RPSError(API_UNEXPECTED_EXCEPTION('Error updating proxy configuration'))
}
}
} catch (error) {
handleError(log, 'proxyConfigAccessInfo', req, res, error)
}
}

export const getUpdatedData = async (newProxy: ProxyConfig, oldProxy: ProxyConfig): Promise<ProxyConfig> => {
const proxyConfig: ProxyConfig = { proxyName: newProxy.proxyName } as ProxyConfig
proxyConfig.address = newProxy.address ?? oldProxy.address
proxyConfig.infoFormat = newProxy.infoFormat ?? oldProxy.infoFormat
proxyConfig.networkDnsSuffix = newProxy.networkDnsSuffix ?? oldProxy.networkDnsSuffix
proxyConfig.port = newProxy.port ?? oldProxy.port
proxyConfig.tenantId = oldProxy.tenantId
return proxyConfig
}
65 changes: 65 additions & 0 deletions src/routes/admin/proxy/get.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
/*********************************************************************
* Copyright (c) Intel Corporation 2025
* SPDX-License-Identifier: Apache-2.0
**********************************************************************/

import { createSpyObj } from '../../../test/helper/jest.js'
import { jest } from '@jest/globals'
import { type SpyInstance, spyOn } from 'jest-mock'
import { getProxyProfile } from './get.js'
import { ProxyConfig } from 'models/RCS.Config.js'

describe('Proxy - Get', () => {
let resSpy
let req
let proxyConfig: ProxyConfig
let getSpy: SpyInstance<any>

beforeEach(() => {
resSpy = createSpyObj('Response', [
'status',
'json',
'end',
'send'
])

req = {
db: { proxyConfigs: { getByName: jest.fn() } },
query: {},
params: { proxyName: 'proxyConfigName' },
tenantId: '',
method: 'GET'
}

getSpy = spyOn(req.db.proxyConfigs, 'getByName').mockResolvedValue(
(proxyConfig = {
proxyName: 'proxyConfigName',
address: 'intel.com',
infoFormat: 201,
networkDnsSuffix: 'vprodemo',
port: 443,
tenantId: 'foo'
})
)

resSpy.status.mockReturnThis()
resSpy.json.mockReturnThis()
resSpy.send.mockReturnThis()
})
it('should get', async () => {
await getProxyProfile(req, resSpy)
expect(getSpy).toHaveBeenCalledWith('proxyConfigName', req.tenantId)
expect(resSpy.status).toHaveBeenCalledWith(200)
})
it('should handle not found', async () => {
spyOn(req.db.proxyConfigs, 'getByName').mockResolvedValue(null)
await getProxyProfile(req, resSpy)
expect(resSpy.status).toHaveBeenCalledWith(404)
})
it('should handle error', async () => {
spyOn(req.db.proxyConfigs, 'getByName').mockRejectedValue(null)
await getProxyProfile(req, resSpy)
expect(getSpy).toHaveBeenCalledWith('proxyConfigName', req.tenantId)
expect(resSpy.status).toHaveBeenCalledWith(500)
})
})
24 changes: 24 additions & 0 deletions src/routes/admin/proxy/get.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import Logger from '../../../Logger.js'
import { MqttProvider } from '../../../utils/MqttProvider.js'
import { type Request, type Response } from 'express'
import handleError from '../../../utils/handleError.js'
import { RPSError } from '../../../utils/RPSError.js'
import { API_RESPONSE, NOT_FOUND_EXCEPTION, NOT_FOUND_MESSAGE } from '../../../utils/constants.js'
import { ProxyConfig } from 'models/RCS.Config.js'

export async function getProxyProfile(req: Request, res: Response) {
const { proxyName } = req.params
const tenantId = req.tenantId || ''
const log = new Logger('getProxyProfile')
try {
const result: ProxyConfig | null = await req.db.proxyConfigs.getByName(proxyName, tenantId)
if (result == null) {
throw new RPSError(NOT_FOUND_MESSAGE('Proxy', proxyName), NOT_FOUND_EXCEPTION)
} else {
MqttProvider.publishEvent('success', ['getProxyProfile'], `Sent Profile : ${proxyName}`)
res.status(200).json(API_RESPONSE(result)).end()
}
} catch (error) {
handleError(log, 'getProxyProfile', req, res, error)
}
}
16 changes: 11 additions & 5 deletions src/routes/admin/proxy/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,16 @@ import validateMiddleware from '../../../middleware/validate.js'
import { odataValidator } from '../odataValidator.js'
import { allProxyProfiles } from './all.js'
import { createProxyProfile } from './create.js'
import { proxyValidator } from './proxyValidator.js'
import { proxyUpdateValidator, proxyValidator } from './proxyValidator.js'
import { deleteProxyProfile } from './delete.js'
import { getProxyProfile } from './get.js'
import { editProxyProfile } from './edit.js'

const profileRouter: Router = Router()
const proxyRouter: Router = Router()

profileRouter.get('/', odataValidator(), validateMiddleware, allProxyProfiles)
profileRouter.post('/', proxyValidator(), validateMiddleware, createProxyProfile)
export default profileRouter
proxyRouter.get('/', odataValidator(), validateMiddleware, allProxyProfiles)
proxyRouter.get('/:proxyName', getProxyProfile)
proxyRouter.post('/', proxyValidator(), validateMiddleware, createProxyProfile)
proxyRouter.patch('/', proxyUpdateValidator(), validateMiddleware, editProxyProfile)
proxyRouter.delete('/:proxyName', deleteProxyProfile)
export default proxyRouter
43 changes: 43 additions & 0 deletions src/routes/admin/proxy/proxyValidator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,3 +56,46 @@ export const proxyValidator = (): any => [
.isLength({ max: 192 })
.withMessage('Domain name of the network maximum length is 192')
]

export const proxyUpdateValidator = (): any => [
// Validate and normalize infoFormat first so conditional checks can rely on a number
check('infoFormat')
.not()
.isEmpty()
.withMessage('Server address format is required')
.isInt()
.toInt()
.isIn([
3,
4,
201
])
.withMessage('Server address format should be either 3(IPV4), 4(IPV6) or 201(FQDN)'),

// address presence
check('address').not().isEmpty().withMessage('Server address is required'),

// address format based on infoFormat
check('address')
.if((_, { req }) => req.body.infoFormat === 3)
.isIP(4)
.withMessage('infoFormat 3 requires IPV4 server address'),
check('address')
.if((_, { req }) => req.body.infoFormat === 4)
.isIP(6)
.withMessage('infoFormat 4 requires IPV6 server address'),
check('address')
.if((_, { req }) => req.body.infoFormat === 201)
.isFQDN({ require_tld: true, allow_underscores: false, allow_numeric_tld: false })
.withMessage('infoFormat 201 requires FQDN server address'),

check('port').exists().isPort().withMessage('Port value should range between 1 and 65535'),
check('networkDnsSuffix')
.not()
.isEmpty()
.withMessage('Domain name of the network is required')
.isFQDN({ require_tld: true, allow_underscores: false })
.withMessage('Domain name of the network should contain alphanumeric and hyphens in the middle')
.isLength({ max: 192 })
.withMessage('Domain name of the network maximum length is 192')
]
Loading