Skip to content

Commit

Permalink
Import company data for a licence (#1304)
Browse files Browse the repository at this point in the history
* Import companies (parties) for a licence

https://eaflood.atlassian.net/browse/WATER-4649

We need to replace the import service logic to import a licence from NALD.

The current import service iterates all the companies (known as parties in the import.NALD_PARTIES table) and updates CRM_V2 tables.

This change will use the nald licence id and region to update the companies (parties) data. This means going forwards we will only import companies (parties) when the licence is imported.

We will insert this imported data in the relevant public views.

This change focuses on the companies (parties) data and not address or contacts tables which will be done after this work.
  • Loading branch information
jonathangoulding authored Sep 6, 2024
1 parent 7fa1b87 commit 366538d
Show file tree
Hide file tree
Showing 12 changed files with 561 additions and 6 deletions.
26 changes: 26 additions & 0 deletions app/presenters/import/legacy/company.presenter.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
'use strict'

/**
* Maps the legacy NALD company data to the WRLS format
* @module CompanyPresenter
*/

/**
* Maps the legacy NALD company data to the WRLS format
*
* @param {ImportLegacyCompanyType} company - the legacy NALD company
*
* @returns {object} the NALD company data transformed into the WRLS format for a company
* ready for validation and persisting
*/
function go (company) {
return {
name: company.name,
type: company.type,
externalId: company.external_id
}
}

module.exports = {
go
}
106 changes: 106 additions & 0 deletions app/services/import/legacy/fetch-company.service.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
'use strict'

/**
* Fetches the company data from the import.NALD_PARTIES table for the licence ref
* @module FetchCompanyService
*/

const { db } = require('../../../../db/db.js')

/**
* Fetches the licence data from the import.NALD_PARTIES table for the licence ref
*
* From this point parties will be referred to as companies and a party will be known as a company
*
* @param {string} regionCode - The NALD region code
* @param {string} licenceId - The NALD licence ID
*
* @returns {Promise<ImportLegacyCompanyType[]>}
*/
async function go (regionCode, licenceId) {
const query = _query()

const { rows } = await db.raw(query, [regionCode, licenceId, regionCode, licenceId])

return rows
}

function _query () {
return `
SELECT DISTINCT ON (np."ID")
(concat_ws(':', np."FGAC_REGION_CODE", np."ID")) AS external_id,
(
CASE np."SALUTATION"
WHEN 'null' THEN NULL
ELSE np."SALUTATION"
END
) AS salutation,
(
CASE np."FORENAME"
WHEN 'null' THEN NULL
ELSE np."FORENAME"
END
) AS firstName,
(
CASE np."NAME"
WHEN 'null' THEN NULL
ELSE np."NAME"
END
) AS lastName,
(
CASE np."APAR_TYPE"
WHEN 'PER' THEN 'person'
ELSE 'organisation'
END
) AS type,
TRIM(BOTH ' ' FROM (
CASE
-- If FORENAME or INITIALS are NULL, use NAME directly
WHEN np."FORENAME" = 'null' AND np."INITIALS" = 'null' THEN np."NAME"
ELSE CONCAT_WS(' ',
CASE
WHEN np."SALUTATION" = 'null' THEN NULL
ELSE np."SALUTATION"
END,
CASE
WHEN np."FORENAME" = 'null' THEN np."INITIALS"
ELSE np."FORENAME"
END,
CASE
WHEN np."NAME" = 'null' THEN NULL
ELSE np."NAME"
END
)
END
)) AS name,
np."ID" as id
FROM import."NALD_PARTIES" np
LEFT JOIN import."NALD_ABS_LIC_VERSIONS" nalv
ON nalv."ACON_APAR_ID" = np."ID"
AND nalv."FGAC_REGION_CODE" = np."FGAC_REGION_CODE"
LEFT JOIN import."NALD_LIC_ROLES" nlr
ON nlr."ACON_APAR_ID" = np."ID"
AND nlr."FGAC_REGION_CODE" = np."FGAC_REGION_CODE"
WHERE
(nalv."FGAC_REGION_CODE" = ? AND nalv."AABL_ID" = ?)
OR
(nlr."FGAC_REGION_CODE" = ? AND nlr."AABL_ID" = ?)
`
}

module.exports = {
go
}

/**
* @typedef {object} ImportLegacyCompanyType
*
* @property {string} - The party id
* @property {string|null} salutation - The salutation of the person, or null if not applicable.
* @property {string|null} firstName - The first name of the person, or null if not applicable.
* @property {string|null} lastName - The last name of the person, or null if not applicable.
* @property {string} type - Indicates whether the entry is a 'person' or an 'organisation'.
* @property {string|null} name - The full name, concatenated from salutation, forename/initials, and name.
* @property {string} external_id - The external ID, formatted as 'FGAC_REGION_CODE:ID'.
*
*/
6 changes: 5 additions & 1 deletion app/services/import/legacy/process-licence.service.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

const LicenceStructureValidator = require('../../../validators/import/licence-structure.validator.js')
const PersistLicenceService = require('../persist-licence.service.js')
const TransformCompaniesService = require('./transform-companies.service.js')
const TransformLicenceService = require('./transform-licence.service.js')
const TransformLicenceVersionPurposeConditionsService = require('./transform-licence-version-purpose-conditions.service.js')
const TransformLicenceVersionPurposesService = require('./transform-licence-version-purposes.service.js')
Expand All @@ -32,11 +33,14 @@ async function go (licenceRef) {
await TransformLicenceVersionPurposesService.go(regionCode, naldLicenceId, transformedLicence)
await TransformLicenceVersionPurposeConditionsService.go(regionCode, naldLicenceId, transformedLicence)

// Transform the company data
const { transformedCompanies } = await TransformCompaniesService.go(regionCode, naldLicenceId)

// Ensure the built licence has all the valid child records we require
LicenceStructureValidator.go(transformedLicence)

// Either insert or update the licence in WRLS
const licenceId = await PersistLicenceService.go(transformedLicence)
const licenceId = await PersistLicenceService.go(transformedLicence, transformedCompanies)

calculateAndLogTimeTaken(startTime, 'Legacy licence import complete', { licenceId, licenceRef })
} catch (error) {
Expand Down
42 changes: 42 additions & 0 deletions app/services/import/legacy/transform-companies.service.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
'use strict'

/**
* Transforms NALD company data into a valid object that matches the WRLS structure
* @module ImportLegacyTransformCompaniesService
*/

const FetchCompanyService = require('./fetch-company.service.js')
const CompanyPresenter = require('../../../presenters/import/legacy/company.presenter.js')
const ImportCompanyValidator = require('../../../validators/import/company.validator.js')

/**
* Transforms NALD company data into a validated object that matches the WRLS structure
*
* @param {string} regionCode - The NALD region code
* @param {string} licenceId - The NALD licence ID
*
* @returns {Promise<object>} an object representing an array of valid WRLS transformed companies and
* an array of companies from the db
*/
async function go (regionCode, licenceId) {
const companies = await FetchCompanyService.go(regionCode, licenceId)

const transformedCompanies = []

companies.forEach((company) => {
const transformedCompany = CompanyPresenter.go(company)

ImportCompanyValidator.go(transformedCompany)

transformedCompanies.push(transformedCompany)
})

return {
transformedCompanies,
companies
}
}

module.exports = {
go
}
26 changes: 25 additions & 1 deletion app/services/import/persist-licence.service.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,21 +10,25 @@ const LicenceModel = require('../../models/licence.model.js')
const LicenceVersionModel = require('../../models/licence-version.model.js')
const LicenceVersionPurposeModel = require('../../models/licence-version-purpose.model.js')
const LicenceVersionPurposeConditionModel = require('../../models/licence-version-purpose-condition.model.js')
const CompanyModel = require('../../models/company.model.js')

/**
* Creates or updates an imported licence and its child entities that have been transformed and validated
*
* @param {object} transformedLicence - An object representing a valid WRLS licence
* @param {object[]} transformedCompanies - an array of companies representing a WRLS company
*
* @returns {Promise<object>}
*/
async function go (transformedLicence) {
async function go (transformedLicence, transformedCompanies) {
return LicenceModel.transaction(async (trx) => {
const updatedAt = timestampForPostgres()
const { id } = await _persistLicence(trx, updatedAt, transformedLicence)

await _persistLicenceVersions(trx, updatedAt, transformedLicence.licenceVersions, id)

await _persistCompanies(trx, updatedAt, transformedCompanies)

return id
})
}
Expand Down Expand Up @@ -132,6 +136,26 @@ async function _persistLicenceVersionPurposeCondition (
.returning('id')
}

// Companies
async function _persistCompanies (trx, updatedAt, companies) {
for (const company of companies) {
await _persistCompany(trx, updatedAt, company)
}
}

async function _persistCompany (trx, updatedAt, company) {
const { ...propertiesToPersist } = company

return CompanyModel.query(trx)
.insert({ ...propertiesToPersist, updatedAt })
.onConflict('externalId')
.merge([
'name',
'type',
'updatedAt'
])
}

module.exports = {
go
}
32 changes: 32 additions & 0 deletions app/validators/import/company.validator.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
'use strict'

/**
* @module ImportCompanyValidator
*/

const Joi = require('joi')

/**
* Checks that the imported company data has been transformed and is valid for persisting to WRLS
*
* @param {object} company - The transformed company data
*
* @throws {Joi.ValidationError} - throws a Joi validation error if the validation fails
*/
function go (company) {
const schema = Joi.object({
name: Joi.string().required(),
type: Joi.string().valid('organisation', 'person').required(),
externalId: Joi.string().required()
})

const result = schema.validate(company, { convert: false })

if (result.error) {
throw result.error
}
}

module.exports = {
go
}
37 changes: 37 additions & 0 deletions test/presenters/import/legacy/company.presenter.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
'use strict'

// Test framework dependencies
const Lab = require('@hapi/lab')
const Code = require('@hapi/code')

const { describe, it, beforeEach } = exports.lab = Lab.script()
const { expect } = Code

// Thing under test
const CompanyPresenter = require('../../../../app/presenters/import/legacy/company.presenter.js')

describe('Import Legacy Company presenter', () => {
let legacyCompany

beforeEach(() => {
legacyCompany = _legacyCompany()
})

it('correctly transforms the data', () => {
const result = CompanyPresenter.go(legacyCompany)

expect(result).to.equal({
externalId: '1:1940',
name: 'ACME',
type: 'organisation'
})
})
})

function _legacyCompany () {
return {
name: 'ACME',
type: 'organisation',
external_id: '1:1940'
}
}
2 changes: 2 additions & 0 deletions test/services/import/legacy/process-licence.service.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ const TransformLicenceService = require('../../../../app/services/import/legacy/
const TransformLicenceVersionsService = require('../../../../app/services/import/legacy/transform-licence-versions.service.js')
const TransformLicenceVersionPurposesService = require('../../../../app/services/import/legacy/transform-licence-version-purposes.service.js')
const TransformLicenceVersionPurposeConditionsService = require('../../../../app/services/import/legacy/transform-licence-version-purpose-conditions.service.js')
const TransformCompaniesService = require('../../../../app/services/import/legacy/transform-companies.service.js')

// Thing under test
const ProcessLicenceService = require('../../../../app/services/import/legacy/process-licence.service.js')
Expand All @@ -42,6 +43,7 @@ describe('Import Legacy Process Licence service', () => {
Sinon.stub(TransformLicenceVersionsService, 'go').resolves()
Sinon.stub(TransformLicenceVersionPurposesService, 'go').resolves(transformedLicence)
Sinon.stub(TransformLicenceVersionPurposeConditionsService, 'go').resolves(transformedLicence)
Sinon.stub(TransformCompaniesService, 'go').resolves({ company: [], transformedCompany: [] })

// BaseRequest depends on the GlobalNotifier to have been set. This happens in app/plugins/global-notifier.plugin.js
// when the app starts up and the plugin is registered. As we're not creating an instance of Hapi server in this
Expand Down
Loading

0 comments on commit 366538d

Please sign in to comment.