Skip to content
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

Haar 1906+1907 update flow #32

Merged
merged 17 commits into from
Nov 2, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
84 changes: 83 additions & 1 deletion server/controllers/baseClientController.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import listBaseClientsPresenter from '../views/presenters/listBaseClientsPresent
import createUserToken from '../testutils/createUserToken'
import viewBaseClientPresenter from '../views/presenters/viewBaseClientPresenter'
import nunjucksUtils from '../views/helpers/nunjucksUtils'
import editBaseClientPresenter from '../views/presenters/editBaseClientPresenter'

describe('BaseClientController', () => {
const token = createUserToken(['ADMIN'])
Expand Down Expand Up @@ -109,7 +110,7 @@ describe('BaseClientController', () => {
})

it('if grant is specified with authorization-code renders the details screen', async () => {
// GIVEN a request with grant="client-credentials" parameter
// GIVEN a request with grant="authorization-code" parameter
request = createMock<Request>({ query: { grant: 'authorization-code' } })

// WHEN the create base client page is requested
Expand Down Expand Up @@ -184,4 +185,85 @@ describe('BaseClientController', () => {
})
})
})

describe('update base client details', () => {
it('displays update base client details screen', async () => {
// GIVEN a request to edit a base client
const baseClient = baseClientFactory.build()
baseClientService.getBaseClient.mockResolvedValue(baseClient)
request = createMock<Request>({ params: { baseClientId: baseClient.baseClientId } })

// WHEN the edit base client details page is requested
await baseClientController.displayEditBaseClient()(request, response, next)

// THEN the base client is retrieved from the base client service
expect(baseClientService.getBaseClient).toHaveBeenCalledWith(token, baseClient.baseClientId)

// AND the page is rendered
const presenter = editBaseClientPresenter(baseClient)
expect(response.render).toHaveBeenCalledWith('pages/edit-base-client-details.njk', {
baseClient,
presenter,
...nunjucksUtils,
})
})

it('updates and redirects to view base client screen', async () => {
// GIVEN the service will return without an error
const baseClient = baseClientFactory.build()
request = createMock<Request>({
params: { baseClientId: baseClient.baseClientId },
body: { baseClientId: baseClient.baseClientId },
})
baseClientService.updateBaseClient.mockResolvedValue(new Response())

// WHEN it is posted
await baseClientController.updateBaseClientDetails()(request, response, next)

// THEN the base client service is updated
expect(baseClientService.updateBaseClient).toHaveBeenCalled()

// AND the user is redirected to the view base client page
expect(response.redirect).toHaveBeenCalledWith(`/base-clients/${baseClient.baseClientId}`)
})
})

describe('update base client deployment', () => {
it('displays update base client deployment screen', async () => {
// GIVEN a request to edit base client deployment
const baseClient = baseClientFactory.build()
baseClientService.getBaseClient.mockResolvedValue(baseClient)
request = createMock<Request>({ params: { baseClientId: baseClient.baseClientId } })

// WHEN the edit base client details page is requested
await baseClientController.displayEditBaseClientDeployment()(request, response, next)

// THEN the base client is retrieved from the base client service
expect(baseClientService.getBaseClient).toHaveBeenCalledWith(token, baseClient.baseClientId)

// AND the page is rendered
expect(response.render).toHaveBeenCalledWith('pages/edit-base-client-deployment.njk', {
baseClient,
})
})

it('updates and redirects to view base client screen', async () => {
// GIVEN the service will return without an error
const baseClient = baseClientFactory.build()
request = createMock<Request>({
params: { baseClientId: baseClient.baseClientId },
body: { baseClientId: baseClient.baseClientId },
})
baseClientService.updateBaseClientDeployment.mockResolvedValue(new Response())

// WHEN it is posted
await baseClientController.updateBaseClientDeployment()(request, response, next)

// THEN the base client service is updated
expect(baseClientService.updateBaseClientDeployment).toHaveBeenCalled()

// AND the user is redirected to the view base client page
expect(response.redirect).toHaveBeenCalledWith(`/base-clients/${baseClient.baseClientId}`)
})
})
})
67 changes: 66 additions & 1 deletion server/controllers/baseClientController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { BaseClientService } from '../services'
import listBaseClientsPresenter from '../views/presenters/listBaseClientsPresenter'
import viewBaseClientPresenter from '../views/presenters/viewBaseClientPresenter'
import nunjucksUtils from '../views/helpers/nunjucksUtils'
import { mapCreateBaseClientForm } from '../mappers'
import { mapCreateBaseClientForm, mapEditBaseClientDeploymentForm, mapEditBaseClientDetailsForm } from '../mappers'
import { BaseClient } from '../interfaces/baseClientApi/baseClient'
import editBaseClientPresenter from '../views/presenters/editBaseClientPresenter'

Expand Down Expand Up @@ -94,4 +94,69 @@ export default class BaseClientController {
return ''
}
}

public displayEditBaseClient(): RequestHandler {
return async (req, res) => {
const userToken = res.locals.user.token
const { baseClientId } = req.params
const baseClient = await this.baseClientService.getBaseClient(userToken, baseClientId)

const presenter = editBaseClientPresenter(baseClient)
res.render('pages/edit-base-client-details.njk', {
baseClient,
presenter,
...nunjucksUtils,
})
}
}

public updateBaseClientDetails(): RequestHandler {
return async (req, res, next) => {
const userToken = res.locals.user.token
const { baseClientId } = req.params

// get current values
const baseClient = await this.baseClientService.getBaseClient(userToken, baseClientId)

// map form values to updated base client
const updatedClient = mapEditBaseClientDetailsForm(baseClient, req)

// update base client
await this.baseClientService.updateBaseClient(userToken, updatedClient)

// return to view base client page
res.redirect(`/base-clients/${baseClientId}`)
}
}

public displayEditBaseClientDeployment(): RequestHandler {
return async (req, res) => {
const userToken = res.locals.user.token
const { baseClientId } = req.params
const baseClient = await this.baseClientService.getBaseClient(userToken, baseClientId)

res.render('pages/edit-base-client-deployment.njk', {
baseClient,
})
}
}

public updateBaseClientDeployment(): RequestHandler {
return async (req, res, next) => {
const userToken = res.locals.user.token
const { baseClientId } = req.params

// get current values
const baseClient = await this.baseClientService.getBaseClient(userToken, baseClientId)

// map form values to updated base client
const updatedClient = mapEditBaseClientDeploymentForm(baseClient, req)

// update base client
await this.baseClientService.updateBaseClientDeployment(userToken, updatedClient)

// return to view base client page
res.redirect(`/base-clients/${baseClientId}`)
}
}
}
12 changes: 12 additions & 0 deletions server/data/localMockData/baseClientsResponseMock.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,4 +35,16 @@ export const getBaseClientResponseMock: GetBaseClientResponse = {
databaseUserName: 'databaseUserName',
validDays: 1,
accessTokenValidityMinutes: 60,
deployment: {
team: 'deployment team',
teamContact: 'deployment team contact',
teamSlack: 'deployment team slack',
hosting: 'deployment hosting',
namespace: 'deployment namespace',
deployment: 'deployment deployment',
secretName: 'deployment secret name',
clientIdKey: 'deployment client id key',
secretKey: 'deployment secret key',
deploymentInfo: 'deployment deployment info',
},
}
12 changes: 12 additions & 0 deletions server/interfaces/baseClientApi/baseClientResponse.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,18 @@ export interface GetBaseClientResponse {
databaseUserName?: string
validDays?: number
accessTokenValidityMinutes?: number
deployment: {
team: string
teamContact: string
teamSlack: string
hosting: string
namespace: string
deployment: string
secretName: string
clientIdKey: string
secretKey: string
deploymentInfo: string
}
}

export interface ClientSecretsResponse {
Expand Down
11 changes: 1 addition & 10 deletions server/mappers/baseClientApi/getBaseClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,16 +27,7 @@ export default (response: GetBaseClientResponse): BaseClient => {
contact: '',
status: '',
},
deployment: {
team: '',
teamContact: '',
teamSlack: '',
hosting: '',
namespace: '',
deployment: '',
secretName: '',
clientIdKey: '',
},
deployment: response.deployment,
config: {
allowedIPs: response.ips ? response.ips : [],
expiryDate: response.validDays
Expand Down
6 changes: 3 additions & 3 deletions server/mappers/baseClientApi/updateBaseClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ import { daysRemaining } from '../../utils/utils'

export default (baseClient: BaseClient): UpdateBaseClientRequest => {
return {
scopes: ['read', 'write'],
authorities: ['ROLE_CLIENT_CREDENTIALS'],
ips: [],
scopes: baseClient.scopes,
authorities: baseClient.clientCredentials.authorities,
ips: baseClient.config.allowedIPs,
jiraNumber: baseClient.audit,
databaseUserName: baseClient.clientCredentials.databaseUserName,
validDays: baseClient.config.expiryDate ? daysRemaining(baseClient.config.expiryDate) : null,
Expand Down
20 changes: 1 addition & 19 deletions server/mappers/forms/mapCreateBaseClientForm.ts
Original file line number Diff line number Diff line change
@@ -1,24 +1,6 @@
import type { Request } from 'express'
import { BaseClient } from '../../interfaces/baseClientApi/baseClient'
import { multiSeparatorSplit } from '../../utils/utils'

function getDayOfExpiry(daysRemaining: string) {
const daysRemainingInt = parseIntWithDefault(daysRemaining, 0)
const timeOfExpiry: Date = new Date(Date.now() + daysRemainingInt * 24 * 60 * 60 * 1000)
return timeOfExpiry.toISOString().split('T')[0]
}

function getAccessTokenValiditySeconds(accessTokenValidity: string, customAccessTokenValidity?: string) {
if (accessTokenValidity === 'custom' && customAccessTokenValidity) {
return parseIntWithDefault(customAccessTokenValidity, 0)
}
return parseIntWithDefault(accessTokenValidity, 0)
}

function parseIntWithDefault(value: string, defaultValue: number) {
const parsed = parseInt(value, 10)
return Number.isNaN(parsed) ? defaultValue : parsed
}
import { getAccessTokenValiditySeconds, getDayOfExpiry, multiSeparatorSplit } from '../../utils/utils'

export default (request: Request): BaseClient => {
// valid days is calculated from expiry date
Expand Down
58 changes: 58 additions & 0 deletions server/mappers/forms/mapEditBaseClientDeploymentForm.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import type { Request } from 'express'
import { createMock } from '@golevelup/ts-jest'
import { baseClientFactory } from '../../testutils/factories'
import { mapEditBaseClientDeploymentForm } from '../index'

const formRequest = (form: Record<string, unknown>) => {
return createMock<Request>({ body: form })
}

describe('mapEditBaseClientDeploymentForm', () => {
describe('updates deployment details only', () => {
it('updates details only', () => {
// Given a base client with fields populated
const detailedBaseClient = baseClientFactory.build({
accessTokenValidity: 3600,
deployment: {
team: 'deployment team',
},
service: {
serviceName: 'service serviceName',
},
})

// and given an edit deployment request with all fields populated
const request = formRequest({
team: 'team',
teamContact: 'contact',
teamSlack: 'slack',
hosting: 'CLOUDPLATFORM',
namespace: 'b',
deployment: 'c',
secretName: 'd',
clientIdKey: 'e',
secretKey: 'f',
deploymentInfo: 'g',
})

// when the form is mapped
const update = mapEditBaseClientDeploymentForm(detailedBaseClient, request)

// then the deployment details are updated
expect(update.deployment.team).toEqual('team')
expect(update.deployment.teamContact).toEqual('contact')
expect(update.deployment.teamSlack).toEqual('slack')
expect(update.deployment.hosting).toEqual('CLOUDPLATFORM')
expect(update.deployment.namespace).toEqual('b')
expect(update.deployment.deployment).toEqual('c')
expect(update.deployment.secretName).toEqual('d')
expect(update.deployment.clientIdKey).toEqual('e')
expect(update.deployment.secretKey).toEqual('f')
expect(update.deployment.deploymentInfo).toEqual('g')

// but regular client details and service details are not updated
expect(update.accessTokenValidity).toEqual(3600)
expect(update.service.serviceName).toEqual(detailedBaseClient.service.serviceName)
})
})
})
21 changes: 21 additions & 0 deletions server/mappers/forms/mapEditBaseClientDeploymentForm.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import type { Request } from 'express'
import { BaseClient } from '../../interfaces/baseClientApi/baseClient'

export default (baseClient: BaseClient, request: Request): BaseClient => {
const data = request.body
return {
...baseClient,
deployment: {
team: data.team,
teamContact: data.teamContact,
teamSlack: data.teamSlack,
hosting: data.hosting,
namespace: data.namespace,
deployment: data.deployment,
secretName: data.secretName,
clientIdKey: data.clientIdKey,
secretKey: data.secretKey,
deploymentInfo: data.deploymentInfo,
},
}
}