Skip to content

Commit

Permalink
Add registered user modifier to LicenceModel (#693)
Browse files Browse the repository at this point in the history
https://eaflood.atlassian.net/browse/WATER-4324

> This is part of a series of changes to allow us to replace the legacy view licence page

When viewing a licence using the [legacy water-abstraction-ui](https://github.com/DEFRA/water-abstraction-ui) the page will display information on who the registered user is.

The registered is an external user who has declared that they manage the licence via the external UI. They are not the licence holder, just the person declared to be responsible for managing it.

Unfortunately, this functionality dates from the very start of the service and the data is in one of the oldest legacy schemas; `crm`. It seems this was one of the things which the previous team never managed to migrate to `crm_v2`, their intended better CRM structure.

We have to go through a number of tables using a specific query to identify the registered user and whether they have set a custom name for the licence.

This change adds a [modifier](https://vincit.github.io/objection.js/recipes/modifiers.html#modifiers) to the `LicenceModel` so we only have to do this once!
  • Loading branch information
Cruikshanks authored Jan 29, 2024
1 parent 20c50f5 commit 6a6239a
Show file tree
Hide file tree
Showing 5 changed files with 221 additions and 3 deletions.
64 changes: 64 additions & 0 deletions app/models/licence.model.js
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,28 @@ class LicenceModel extends BaseModel {
'suffix'
])
})
},
/**
* registeredToAndLicenceName modifier fetches the linked `licenceDocumentHeader` which holds the licence name and
* adds to it the registered user's email address if one is set.
*/
registeredToAndLicenceName (query) {
query
.withGraphFetched('licenceDocumentHeader')
.modifyGraph('licenceDocumentHeader', (builder) => {
builder.select([
'licenceDocumentHeaders.id',
'licenceDocumentHeaders.licenceName',
'licenceEntityRoles.role',
'licenceEntities.name AS registeredTo'
])
.leftJoin('licenceEntityRoles', function () {
this
.on('licenceEntityRoles.companyEntityId', '=', 'licenceDocumentHeaders.companyEntityId')
.andOn('licenceEntityRoles.role', '=', Model.raw('?', ['primary_user']))
})
.leftJoin('licenceEntities', 'licenceEntities.id', 'licenceEntityRoles.licenceEntityId')
})
}
}
}
Expand Down Expand Up @@ -230,6 +252,48 @@ class LicenceModel extends BaseModel {

return company.name
}

/**
* Determine the licence name for the licence
*
* > We recommend adding the `registeredToAndLicenceName` modifier to your query to ensure the joined records are
* > available to determine this
*
* If set this is visible on the view licence page above the licence reference and on the legacy external view as a
* field in the summary tab.
*
* The licence name is a custom name the registered user of the licence can set. So, you will only see a licence name
* if the licence is registered to a user and they have chosen to set a name for it via the external UI.
*
* @returns {(string|null)} `null` if this instance does not have the additional properties needed to determine the
* licence name else the licence's custom name
*/
$licenceName () {
const licenceName = this?.licenceDocumentHeader?.licenceName

return licenceName || null
}

/**
* Determine who the licence is registered to
*
* > We recommend adding the `registeredToAndLicenceName` modifier to your query to ensure the joined records are
* > available to determine this
*
* If set this is visible on the view licence page below the licence reference.
*
* When an external user has an account they can add a licence via the external UI. We'll generate a letter with a
* code which gets sent to the licence's address. Once they receive it they can enter the code in the UI and they
* then become the registered user for it.
*
* @returns {(string|null)} `null` if this instance does not have the additional properties needed to determine who
* the licence is registered to else the email of the user
*/
$registeredTo () {
const registeredUserName = this?.licenceDocumentHeader?.registeredTo

return registeredUserName || null
}
}

module.exports = LicenceModel
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
'use strict'

const viewName = 'licence_document_headers'

exports.up = function (knex) {
return knex
.schema
.dropView(viewName)
.createView(viewName, (view) => {
view.as(knex('document_header').withSchema('crm').select([
'document_id AS id',
// This could be ignored as it is always set to the same ID. But that id comes from a single record in the
// crm.entity table which has the `entity_type` regime. So, for the purposes of testing we just have to live
// with always populating it even though we don't care about it.
'regime_entity_id',
// 'system_id',
'system_internal_id AS nald_id',
'system_external_id AS licence_ref',
'metadata',
'company_entity_id',
// 'verification_id',
'document_name AS licence_name',
'date_created AS created_at',
'date_updated AS updated_at',
'date_deleted AS deleted_at'
]))
})
}

exports.down = function (knex) {
return knex
.schema
.dropView(viewName)
.createView(viewName, (view) => {
// NOTE: We have commented out unused columns from the source table
view.as(knex('document_header').withSchema('crm').select([
'document_id AS id',
// This could be ignored as it is always set to the same ID. But that id comes from a single record in the
// crm.entity table which has the `entity_type` regime. So, for the purposes of testing we just have to live
// with always populating it even though we don't care about it.
'regime_entity_id',
// 'system_id',
'system_internal_id AS nald_id',
'system_external_id AS licence_ref',
'metadata',
// 'company_entity_id',
// 'verification_id',
// 'document_name',
'date_created AS created_at',
'date_updated AS updated_at',
'date_deleted AS deleted_at'
]))
})
}
53 changes: 53 additions & 0 deletions test/models/licence.model.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ const LicenceVersionHelper = require('../support/helpers/licence-version.helper.
const LicenceVersionModel = require('../../app/models/licence-version.model.js')
const RegionHelper = require('../support/helpers/region.helper.js')
const RegionModel = require('../../app/models/region.model.js')
const RegisteredToAndLicenceNameSeeder = require('../support/seeders/registered-to-and-licence-name.seeder.js')
const WorkflowHelper = require('../support/helpers/workflow.helper.js')
const WorkflowModel = require('../../app/models/workflow.model.js')

Expand Down Expand Up @@ -562,4 +563,56 @@ describe('Licence model', () => {
})
})
})

describe('$licenceName', () => {
describe('when instance has not been set with the additional properties needed', () => {
it('returns null', () => {
const result = testRecord.$licenceName()

expect(result).to.be.null()
})
})

describe('when the instance has been set with the additional properties needed', () => {
beforeEach(async () => {
const licence = await LicenceHelper.add()

await RegisteredToAndLicenceNameSeeder.seed(licence)

testRecord = await LicenceModel.query().findById(licence.id).modify('registeredToAndLicenceName')
})

it('returns the licence name', async () => {
const result = testRecord.$licenceName()

expect(result).to.equal('My custom licence name')
})
})
})

describe('$registeredTo', () => {
describe('when instance has not been set with the additional properties needed', () => {
it('returns null', () => {
const result = testRecord.$registeredTo()

expect(result).to.be.null()
})
})

describe('when the instance has been set with the additional properties needed', () => {
beforeEach(async () => {
const licence = await LicenceHelper.add()

await RegisteredToAndLicenceNameSeeder.seed(licence)

testRecord = await LicenceModel.query().findById(licence.id).modify('registeredToAndLicenceName')
})

it('returns who the licence is registered to', async () => {
const result = testRecord.$registeredTo()

expect(result).to.equal('grace.hopper@example.com')
})
})
})
})
6 changes: 3 additions & 3 deletions test/support/helpers/licence-entity.helper.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ const LicenceEntityModel = require('../../../app/models/licence-entity.model.js'
* If no `data` is provided, default values will be used. These are
*
* - `id` - [random UUID]
* - `name` - Grace Hopper
* - `name` - grace.hopper@example.com
* - `type` - individual
*
* @param {Object} [data] Any data you want to use instead of the defaults used here or in the database
Expand All @@ -39,8 +39,8 @@ async function add (data = {}) {
function defaults (data = {}) {
const defaults = {
id: generateUUID(),
name: 'Grace Hopper',
type: 'Licence Holder'
name: 'grace.hopper@example.com',
type: 'individual'
}

return {
Expand Down
47 changes: 47 additions & 0 deletions test/support/seeders/registered-to-and-licence-name.seeder.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
'use strict'

/**
* Seeder to setup registered to and licence name details for a LicenceModel
* @module RegisteredToAndLicenceNameSeeder
*/

const LicenceDocumentHeaderHelper = require('../helpers/licence-document-header.helper.js')
const LicenceEntityRoleHelper = require('../helpers/licence-entity-role.helper.js')
const LicenceEntityHelper = require('../helpers/licence-entity.helper.js')

/**
* Sets up the additional records needed for `$licenceName()` and `%registeredTo()` on the licence to return values
*
* The registered to and licence name details are seen on the view licence page (if a licence has them). Unfortunately,
* we have to link through a number of the legacy tables to extract the data.
*
* This seeder ensures the records needed are created and specifically support the `registeredToAndLicenceName()`
* modifier on the `LicenceModel`.
*
* @param {module:LicenceModel} licence - The licence instance we are setting up the records for
* @param {String} [licenceName] The custom licence name to use
*/
async function seed (licence, licenceName = 'My custom licence name') {
const { licenceRef } = licence

// We get from the licence to the registered user via LicenceDocumentHeader and LicenceEntityRole which are linked
// by the same companyEntityId
const companyEntityId = 'c960a4a1-94f9-4c05-9db1-a70ce5d08738'

// Create a licence document header record
await LicenceDocumentHeaderHelper.add({
companyEntityId,
licenceName,
licenceRef
})

// Create the licence entity record. It's `name` field holds the user email that the licence is registered to
const { id: licenceEntityId } = await LicenceEntityHelper.add()

// Create the licence entity role record that is the part of the link between the licence and the user email
await LicenceEntityRoleHelper.add({ companyEntityId, licenceEntityId })
}

module.exports = {
seed
}

0 comments on commit 6a6239a

Please sign in to comment.