diff --git a/app/presenters/import/legacy/company-contact.presenter.js b/app/presenters/import/legacy/company-contact.presenter.js new file mode 100644 index 0000000000..2e48d2b525 --- /dev/null +++ b/app/presenters/import/legacy/company-contact.presenter.js @@ -0,0 +1,30 @@ +'use strict' + +/** + * Creates a company contact in the WRLS format + * @module CompanyContactPresenter + */ + +/** + * Creates a company contact in the WRLS format + * + * NALD does not have the concept of a 'company contact'. It is a WRLS construct only. + * + * If the NALD party is of type 'PERS', WRLS on import splits it into a 'company' and 'contact' record. The + * 'company-contact' record is the thing that links then together in WRLS. + * + * @param {ImportLegacyContactType} contact - the legacy NALD contact derived from the 'party' + * + * @returns {object} the details needed to persist the company contact in WRLS + */ +function go (contact) { + return { + externalId: contact.external_id, + startDate: contact.start_date, + licenceRoleId: contact.licence_role_id + } +} + +module.exports = { + go +} diff --git a/app/presenters/import/legacy/contact.presenter.js b/app/presenters/import/legacy/contact.presenter.js new file mode 100644 index 0000000000..817b1b2218 --- /dev/null +++ b/app/presenters/import/legacy/contact.presenter.js @@ -0,0 +1,28 @@ +'use strict' + +/** + * Maps the legacy NALD contact data to the WRLS format + * @module ContactPresenter + */ + +/** + * Maps the legacy NALD contact data to the WRLS format + * + * @param {ImportLegacyContactType} contact - the legacy NALD contact + * + * @returns {object} the NALD contact data transformed into the WRLS format for a contact + * ready for validation and persisting + */ +function go (contact) { + return { + externalId: contact.external_id, + salutation: contact.salutation, + initials: contact.initials, + firstName: contact.first_name, + lastName: contact.last_name + } +} + +module.exports = { + go +} diff --git a/app/services/import/legacy/fetch-company.service.js b/app/services/import/legacy/fetch-company.service.js index f9b2a37f86..3f1a278dff 100644 --- a/app/services/import/legacy/fetch-company.service.js +++ b/app/services/import/legacy/fetch-company.service.js @@ -8,7 +8,7 @@ const { db } = require('../../../../db/db.js') /** - * Fetches the licence data from the import.NALD_PARTIES table for the licence ref + * Fetches the party 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 * @@ -29,24 +29,6 @@ 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' @@ -72,8 +54,7 @@ function _query () { END ) END - )) AS name, - np."ID" as id + )) AS name FROM import."NALD_PARTIES" np LEFT JOIN import."NALD_ABS_LIC_VERSIONS" nalv ON nalv."ACON_APAR_ID" = np."ID" @@ -95,10 +76,6 @@ module.exports = { /** * @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'. diff --git a/app/services/import/legacy/fetch-contacts.service.js b/app/services/import/legacy/fetch-contacts.service.js new file mode 100644 index 0000000000..a43a6a5406 --- /dev/null +++ b/app/services/import/legacy/fetch-contacts.service.js @@ -0,0 +1,86 @@ +'use strict' + +/** + * Fetches the contact data from the import.NALD_PARTIES table for the licence ref + * @module FetchContactsService + */ + +const { db } = require('../../../../db/db.js') + +/** + * Fetches the company data from the import.NALD_PARTIES table for the licence ref + * + * The only contact currently imported and saved to WRLS is the licence holder. + * + * @param {string} regionCode - The NALD region code + * @param {string} licenceId - The NALD licence ID + * + * @returns {Promise} + */ +async function go (regionCode, licenceId) { + const query = _query() + + const { rows } = await db.raw(query, [regionCode, licenceId]) + + return rows +} + +function _query () { + return ` + SELECT DISTINCT ON (np."ID") + to_date(nalv."EFF_ST_DATE", 'DD/MM/YYYY') as start_date, + (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."INITIALS" + WHEN 'null' THEN NULL + ELSE np."INITIALS" + END + ) AS initials, + ( + CASE np."FORENAME" + WHEN 'null' THEN NULL + ELSE np."FORENAME" + END + ) AS first_name, + ( + CASE np."NAME" + WHEN 'null' THEN NULL + ELSE np."NAME" + END + ) AS last_name, + lr.id as licence_role_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" + INNER JOIN public.licence_roles lr + ON lr.name = 'licenceHolder' + WHERE np."APAR_TYPE" != 'ORG' + AND nalv."FGAC_REGION_CODE" = ? + AND nalv."AABL_ID" = ? + ORDER BY np."ID", TO_DATE(nalv."EFF_ST_DATE", 'DD/MM/YYYY') ASC; + ` +} + +module.exports = { + go +} + +/** + * @typedef {object} ImportLegacyContactType + * + * @property {string|null} salutation - The salutation of the person, or null if not applicable. + * @property {string|null} initials - The initials 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} external_id - The external ID, formatted as 'FGAC_REGION_CODE:ID'. + * @property {Date} start_date - the earliest start date for the licence versions + * @property {uuid} licence_role_id - the licence role reference data id from WLRS + * + */ diff --git a/app/services/import/legacy/process-licence.service.js b/app/services/import/legacy/process-licence.service.js index 0347f4dac4..71b81bd046 100644 --- a/app/services/import/legacy/process-licence.service.js +++ b/app/services/import/legacy/process-licence.service.js @@ -9,6 +9,7 @@ const LicenceStructureValidator = require('../../../validators/import/licence-st const PersistLicenceService = require('../persist-licence.service.js') const ProcessLicenceReturnLogsService = require('../../jobs/return-logs/process-licence-return-logs.service.js') const TransformCompaniesService = require('./transform-companies.service.js') +const TransformContactsService = require('./transform-contacts.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') @@ -38,6 +39,9 @@ async function go (licenceRef) { // Transform the company data const { transformedCompanies } = await TransformCompaniesService.go(regionCode, naldLicenceId) + // Pass the transformed companies through each transformation step, building the company as we go + await TransformContactsService.go(regionCode, naldLicenceId, transformedCompanies) + // Ensure the built licence has all the valid child records we require LicenceStructureValidator.go(transformedLicence) diff --git a/app/services/import/legacy/transform-contacts.service.js b/app/services/import/legacy/transform-contacts.service.js new file mode 100644 index 0000000000..60e3a17260 --- /dev/null +++ b/app/services/import/legacy/transform-contacts.service.js @@ -0,0 +1,45 @@ +'use strict' + +/** + * Transforms NALD contact data into a valid object that matches the WRLS structure + * @module ImportLegacyTransformContactsService + */ + +const FetchContactsService = require('./fetch-contacts.service.js') +const ContactPresenter = require('../../../presenters/import/legacy/contact.presenter.js') +const CompanyContactPresenter = require('../../../presenters/import/legacy/company-contact.presenter.js') +const ImportContactValidator = require('../../../validators/import/contact.validator.js') + +/** + * Transforms NALD contact data into a validated object that matches the WRLS structure + * + * @param {string} regionCode - The NALD region code + * @param {string} licenceId - The NALD licence ID + * @param {object[]} transformedCompanies + * + */ +async function go (regionCode, licenceId, transformedCompanies) { + const naldContacts = await FetchContactsService.go(regionCode, licenceId) + + naldContacts.forEach((naldContact) => { + const matchingCompany = _matchingCompany(transformedCompanies, naldContact) + + const transformedContact = ContactPresenter.go(naldContact) + + ImportContactValidator.go(transformedContact) + + matchingCompany.contact = { ...transformedContact, dataSource: 'nald' } + + matchingCompany.companyContact = CompanyContactPresenter.go(naldContact) + }) +} + +function _matchingCompany (transformedCompanies, naldContact) { + return transformedCompanies.find((company) => { + return company.externalId === naldContact.external_id + }) +} + +module.exports = { + go +} diff --git a/app/services/import/persist-licence.service.js b/app/services/import/persist-licence.service.js index d116af9487..8731d5cbf9 100644 --- a/app/services/import/persist-licence.service.js +++ b/app/services/import/persist-licence.service.js @@ -5,12 +5,14 @@ * @module PersistLicenceService */ -const { timestampForPostgres } = require('../../lib/general.lib.js') +const CompanyModel = require('../../models/company.model.js') +const ContactModel = require('../../models/contact.model.js') 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') +const LicenceVersionPurposeModel = require('../../models/licence-version-purpose.model.js') +const { timestampForPostgres } = require('../../lib/general.lib.js') +const { db } = require('../../../db/db.js') /** * Creates or updates an imported licence and its child entities that have been transformed and validated @@ -140,11 +142,19 @@ async function _persistLicenceVersionPurposeCondition ( async function _persistCompanies (trx, updatedAt, companies) { for (const company of companies) { await _persistCompany(trx, updatedAt, company) + + if (company.contact) { + await _persistContact(trx, updatedAt, company.contact) + } + + if (company.companyContact) { + await _persistsCompanyContact(trx, updatedAt, company.companyContact) + } } } async function _persistCompany (trx, updatedAt, company) { - const { ...propertiesToPersist } = company + const { contact, companyContact, ...propertiesToPersist } = company return CompanyModel.query(trx) .insert({ ...propertiesToPersist, updatedAt }) @@ -156,6 +166,38 @@ async function _persistCompany (trx, updatedAt, company) { ]) } +async function _persistContact (trx, updatedAt, contact) { + return ContactModel.query(trx) + .insert({ ...contact, updatedAt }) + .onConflict('externalId') + .merge([ + 'salutation', + 'initials', + 'firstName', + 'lastName', + 'updatedAt' + ]) +} + +async function _persistsCompanyContact (trx, updatedAt, companyContact) { + const { externalId, startDate, licenceRoleId } = companyContact + + return db.raw(` + INSERT INTO public."company_contacts" (company_id, contact_id, licence_role_id, start_date, "default", created_at, updated_at) + SELECT com.id, con.id, lr.id, ?, true, NOW(), ? + FROM public.companies com + JOIN public."licence_roles" lr on lr.id = ? + JOIN public.contacts con ON con.external_id = ? + WHERE com.external_id = ? + ON CONFLICT (company_id, contact_id, licence_role_id, start_date) + DO UPDATE SET + contact_id = EXCLUDED.contact_id, + "default" = EXCLUDED."default", + updated_at = EXCLUDED.updated_at + `, [startDate, updatedAt, licenceRoleId, externalId, externalId]) + .transacting(trx) +} + module.exports = { go } diff --git a/app/validators/import/contact.validator.js b/app/validators/import/contact.validator.js new file mode 100644 index 0000000000..97ab628595 --- /dev/null +++ b/app/validators/import/contact.validator.js @@ -0,0 +1,34 @@ +'use strict' + +/** + * @module ImportContactValidator + */ + +const Joi = require('joi') + +/** + * Checks that the imported contact data has been transformed and is valid for persisting to WRLS + * + * @param {object} contact - The transformed contact data + * + * @throws {Joi.ValidationError} - throws a Joi validation error if the validation fails + */ +function go (contact) { + const schema = Joi.object({ + salutation: Joi.string().allow(null), + initials: Joi.string().allow(null), + firstName: Joi.string().allow(null), + lastName: Joi.string().allow(null), + externalId: Joi.string().required() + }) + + const result = schema.validate(contact, { convert: false }) + + if (result.error) { + throw result.error + } +} + +module.exports = { + go +} diff --git a/test/presenters/import/legacy/company-contact.presenter.test.js b/test/presenters/import/legacy/company-contact.presenter.test.js new file mode 100644 index 0000000000..2ea5a1acca --- /dev/null +++ b/test/presenters/import/legacy/company-contact.presenter.test.js @@ -0,0 +1,45 @@ +'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 + +// Test helpers +const { generateUUID } = require('../../../../app/lib/general.lib.js') + +// Thing under test +const CompanyContactPresenter = require('../../../../app/presenters/import/legacy/company-contact.presenter.js') + +describe('Import Legacy Company Contact presenter', () => { + let legacyContact + const licenceRoleId = generateUUID() + + beforeEach(() => { + legacyContact = _legacyContact(licenceRoleId) + }) + + it('correctly transforms the data', () => { + const result = CompanyContactPresenter.go(legacyContact) + + expect(result).to.equal({ + startDate: new Date('2020-01-01'), + licenceRoleId, + externalId: '1:007' + }) + }) +}) + +function _legacyContact (licenceRoleId) { + return { + salutation: 'Mr', + initials: 'H', + first_name: 'James', + last_name: 'Bond', + external_id: '1:007', + start_date: new Date('2020-01-01'), + licence_role_id: licenceRoleId + } +} diff --git a/test/presenters/import/legacy/contact.presenter.test.js b/test/presenters/import/legacy/contact.presenter.test.js new file mode 100644 index 0000000000..770af60fd6 --- /dev/null +++ b/test/presenters/import/legacy/contact.presenter.test.js @@ -0,0 +1,41 @@ +'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 ContactPresenter = require('../../../../app/presenters/import/legacy/contact.presenter.js') + +describe('Import Legacy Contact presenter', () => { + let legacyContact + + beforeEach(() => { + legacyContact = _legacyContact() + }) + + it('correctly transforms the data', () => { + const result = ContactPresenter.go(legacyContact) + + expect(result).to.equal({ + salutation: 'Mr', + initials: 'H', + firstName: 'James', + lastName: 'Bond', + externalId: '1:007' + }) + }) +}) + +function _legacyContact () { + return { + salutation: 'Mr', + initials: 'H', + first_name: 'James', + last_name: 'Bond', + external_id: '1:007' + } +} diff --git a/test/services/import/legacy/process-licence.service.test.js b/test/services/import/legacy/process-licence.service.test.js index 50e2da0bac..30f516fb84 100644 --- a/test/services/import/legacy/process-licence.service.test.js +++ b/test/services/import/legacy/process-licence.service.test.js @@ -20,6 +20,7 @@ const TransformLicenceVersionsService = require('../../../../app/services/import 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') +const TransformContactsService = require('../../../../app/services/import/legacy/transform-contacts.service.js') // Thing under test const ProcessLicenceService = require('../../../../app/services/import/legacy/process-licence.service.js') @@ -47,6 +48,7 @@ describe('Import Legacy Process Licence service', () => { Sinon.stub(TransformLicenceVersionPurposesService, 'go').resolves(transformedLicence) Sinon.stub(TransformLicenceVersionPurposeConditionsService, 'go').resolves(transformedLicence) Sinon.stub(TransformCompaniesService, 'go').resolves({ company: [], transformedCompany: [] }) + Sinon.stub(TransformContactsService, 'go').resolves() // 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 diff --git a/test/services/import/legacy/transform-contacts.service.test.js b/test/services/import/legacy/transform-contacts.service.test.js new file mode 100644 index 0000000000..650a74ba16 --- /dev/null +++ b/test/services/import/legacy/transform-contacts.service.test.js @@ -0,0 +1,88 @@ +'use strict' + +// Test framework dependencies +const Lab = require('@hapi/lab') +const Code = require('@hapi/code') +const Sinon = require('sinon') + +const { describe, it, beforeEach, afterEach } = exports.lab = Lab.script() +const { expect } = Code + +// Test helpers +const { generateUUID } = require('../../../../app/lib/general.lib.js') + +// Things to stub +const FetchCompanyContactService = require('../../../../app/services/import/legacy/fetch-contacts.service.js') + +// Thing under test +const TransformCompanyContactsService = require('../../../../app/services/import/legacy/transform-contacts.service.js') + +describe('Import Legacy Transform Contact service', () => { + const naldLicenceId = '2113' + const regionCode = '1' + const licenceRoleId = generateUUID() + + let legacyContact + let transformedCompanies + + beforeEach(() => { + transformedCompanies = [{ externalId: '1:007' }, { externalId: '1:009' }] + + legacyContact = _legacyContact(licenceRoleId) + }) + + afterEach(() => { + Sinon.restore() + }) + + describe('when matching valid legacy contact is found', () => { + beforeEach(() => { + Sinon.stub(FetchCompanyContactService, 'go').resolves([legacyContact]) + }) + + it('attaches the record transformed and validated for WRLS to the transformed company', async () => { + await TransformCompanyContactsService.go(regionCode, naldLicenceId, transformedCompanies) + + expect(transformedCompanies[0]).to.equal({ + companyContact: { + externalId: '1:007', + licenceRoleId, + startDate: '2020-01-01' + }, + contact: { + dataSource: 'nald', + externalId: '1:007', + firstName: 'James', + initials: 'H', + lastName: 'Bond', + salutation: 'Mr' + }, + externalId: '1:007' + }) + }) + }) + + describe('when no matching valid legacy contact is found', () => { + beforeEach(() => { + Sinon.stub(FetchCompanyContactService, 'go').resolves([]) + }) + + it('returns no contact object on the company', async () => { + await TransformCompanyContactsService.go(regionCode, naldLicenceId, transformedCompanies) + + expect(transformedCompanies[0]).to.equal({ externalId: '1:007' }) + }) + }) +}) + +function _legacyContact (licenceRoleId) { + return { + external_id: '1:007', + first_name: 'James', + initials: 'H', + last_name: 'Bond', + licence_role_id: licenceRoleId, + salutation: 'Mr', + start_date: '2020-01-01' + } +} diff --git a/test/services/import/persist-licence.service.test.js b/test/services/import/persist-licence.service.test.js index 7c59e73bf8..3464cd8e04 100644 --- a/test/services/import/persist-licence.service.test.js +++ b/test/services/import/persist-licence.service.test.js @@ -8,10 +8,14 @@ const { describe, it, beforeEach } = exports.lab = Lab.script() const { expect } = Code // Test helpers +const CompanyContactHelper = require('../../support/helpers/company-contact.helper.js') const CompanyHelper = require('../../support/helpers/company.helper.js') const CompanyModel = require('../../../app/models/company.model.js') +const ContactHelper = require('../../support/helpers/contact.helper.js') +const ContactModel = require('../../../app/models/contact.model.js') const LicenceHelper = require('../../support/helpers/licence.helper.js') const LicenceModel = require('../../../app/models/licence.model.js') +const LicenceRoleHelper = require('../../support/helpers/licence-role.helper.js') const LicenceVersionHelper = require('../../support/helpers/licence-version.helper.js') const LicenceVersionPurposeConditionHelper = require('../../support/helpers/licence-version-purpose-condition.helper.js') const LicenceVersionPurposeConditionTypeHelper = require('../../support/helpers/licence-version-purpose-condition-type.helper.js') @@ -26,6 +30,8 @@ const { randomInteger } = require('../../support/general.js') const PersistLicenceService = require('../../../app/services/import/persist-licence.service.js') describe('Persist licence service', () => { + const companyContactStartDate = new Date('1999-01-01') + let licenceVersionPurposeConditionType let primaryPurpose let purpose @@ -34,6 +40,7 @@ describe('Persist licence service', () => { let transformedCompanies let transformedCompany let transformedLicence + let licenceHolderRoleId beforeEach(async () => { licenceVersionPurposeConditionType = LicenceVersionPurposeConditionTypeHelper.select() @@ -45,7 +52,9 @@ describe('Persist licence service', () => { transformedLicence = _transformedLicence(region.id, primaryPurpose.id, purpose.id, secondaryPurpose.id, licenceVersionPurposeConditionType.id) - transformedCompany = _transformedCompany() + licenceHolderRoleId = LicenceRoleHelper.select().id + + transformedCompany = _transformedCompany(licenceHolderRoleId) transformedCompanies = [{ ...transformedCompany }] }) @@ -85,6 +94,24 @@ describe('Persist licence service', () => { expect(company.name).to.equal('ACME') expect(company.type).to.equal('person') expect(company.externalId).to.equal(transformedCompany.externalId) + + // Contact + const contact = await _fetchPersistedContact(transformedCompany.externalId) + + expect(contact.salutation).to.equal('Mr') + expect(contact.initials).to.equal('H') + expect(contact.firstName).to.equal('James') + expect(contact.lastName).to.equal('Bond') + expect(contact.dataSource).to.equal('nald') + + // Company contact - the company and contact id are used to relate the contact to the company + const companyContact = contact.companyContacts[0] + + expect(companyContact.companyId).to.equal(company.id) + expect(companyContact.contactId).to.equal(contact.id) + expect(companyContact.licenceRoleId).to.equal(licenceHolderRoleId) + expect(companyContact.startDate).to.equal(transformedCompany.companyContact.startDate) + expect(companyContact.default).to.be.true() }) }) @@ -94,6 +121,7 @@ describe('Persist licence service', () => { let existingLicenceVersionPurpose let existingLicenceVersionPurposeCondition let existingCompany + let exisitngContact beforeEach(async () => { existingLicence = await LicenceHelper.add({ @@ -148,7 +176,27 @@ describe('Persist licence service', () => { externalId: transformedCompany.externalId }) - transformedCompanies = [{ ...existingCompany }] + exisitngContact = await ContactHelper.add({ + externalId: transformedCompany.externalId + }) + + await CompanyContactHelper.add({ + companyId: existingCompany.id, + contactId: exisitngContact.id, + licenceRoleId: licenceHolderRoleId, + startDate: companyContactStartDate + }) + + transformedCompanies = [{ + ...existingCompany, + contact: exisitngContact, + companyContact: { + externalId: existingCompany.externalId, + startDate: companyContactStartDate, + licenceRoleId: licenceHolderRoleId + } + } + ] }) it('updates the licence record plus child records in WRLS and returns the licence ID', async () => { @@ -215,6 +263,24 @@ describe('Persist licence service', () => { expect(company.name).to.equal('Example Trading Ltd') expect(company.type).to.equal('organisation') expect(company.externalId).to.equal(existingCompany.externalId) + + // Contact + const contact = await _fetchPersistedContact(exisitngContact.externalId) + + expect(contact.salutation).to.be.null() + expect(contact.initials).to.be.null() + expect(contact.firstName).to.equal('Amara') + expect(contact.lastName).to.equal('Gupta') + expect(contact.dataSource).to.equal('wrls') + + // Company contact - the company and contact id are used to relate the contact to the company + const companyContact = contact.companyContacts[0] + + expect(companyContact.companyId).to.equal(existingCompany.id) + expect(companyContact.contactId).to.equal(exisitngContact.id) + expect(companyContact.licenceRoleId).to.equal(licenceHolderRoleId) + expect(companyContact.startDate).to.equal(new Date('1999-01-01')) + expect(companyContact.default).to.be.true() }) }) }) @@ -309,14 +375,39 @@ async function _fetchPersistedCompany (externalId) { return CompanyModel .query() .where('externalId', externalId) + .withGraphFetched('companyContacts') + .limit(1) + .first() +} + +async function _fetchPersistedContact (externalId) { + return ContactModel + .query() + .where('externalId', externalId) + .withGraphFetched('companyContacts') .limit(1) .first() } -function _transformedCompany () { +function _transformedCompany (licenceRoleId) { + const externalId = CompanyHelper.generateExternalId() + return { - externalId: CompanyHelper.generateExternalId(), + externalId, name: 'ACME', - type: 'person' + type: 'person', + contact: { + salutation: 'Mr', + initials: 'H', + firstName: 'James', + lastName: 'Bond', + externalId, + dataSource: 'nald' + }, + companyContact: { + externalId, + startDate: new Date('1999-01-01'), + licenceRoleId + } } } diff --git a/test/validators/import/contact.validator.test.js b/test/validators/import/contact.validator.test.js new file mode 100644 index 0000000000..61aebc3740 --- /dev/null +++ b/test/validators/import/contact.validator.test.js @@ -0,0 +1,167 @@ +'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 ImportCompanyContactValidator = require('../../../app/validators/import/contact.validator.js') + +describe('Import Contact validator', () => { + let transformedContact + + beforeEach(async () => { + transformedContact = _transformedContact() + }) + + describe('when valid data is provided', () => { + it('does not throw an error', () => { + expect(() => { + ImportCompanyContactValidator.go(transformedContact) + }).to.not.throw() + }) + }) + + describe('the "salutation" property', () => { + describe('when it is not a string', () => { + beforeEach(() => { + transformedContact.salutation = 1 + }) + + it('throws an error', async () => { + expect(() => { + ImportCompanyContactValidator.go(transformedContact) + }).to.throw('"salutation" must be a string') + }) + }) + + describe('when it null', () => { + beforeEach(() => { + transformedContact.salutation = null + }) + + it('does not throw an error', async () => { + expect(() => { + ImportCompanyContactValidator.go(transformedContact) + }).to.not.throw() + }) + }) + }) + + describe('the "initials" property', () => { + describe('when it is not a string', () => { + beforeEach(() => { + transformedContact.initials = 1 + }) + + it('throws an error', async () => { + expect(() => { + ImportCompanyContactValidator.go(transformedContact) + }).to.throw('"initials" must be a string') + }) + }) + + describe('when it null', () => { + beforeEach(() => { + transformedContact.initials = null + }) + + it('does not throw an error', async () => { + expect(() => { + ImportCompanyContactValidator.go(transformedContact) + }).to.not.throw() + }) + }) + }) + + describe('the "firstName" property', () => { + describe('when it is not a string', () => { + beforeEach(() => { + transformedContact.firstName = 1 + }) + + it('throws an error', async () => { + expect(() => { + ImportCompanyContactValidator.go(transformedContact) + }).to.throw('"firstName" must be a string') + }) + }) + + describe('when it null', () => { + beforeEach(() => { + transformedContact.firstName = null + }) + + it('does not throw an error', async () => { + expect(() => { + ImportCompanyContactValidator.go(transformedContact) + }).to.not.throw() + }) + }) + }) + + describe('the "lastName" property', () => { + describe('when it is not a string', () => { + beforeEach(() => { + transformedContact.lastName = 1 + }) + + it('throws an error', async () => { + expect(() => { + ImportCompanyContactValidator.go(transformedContact) + }).to.throw('"lastName" must be a string') + }) + }) + + describe('when it null', () => { + beforeEach(() => { + transformedContact.lastName = null + }) + + it('does not throw an error', async () => { + expect(() => { + ImportCompanyContactValidator.go(transformedContact) + }).to.not.throw() + }) + }) + }) + + describe('the "externalId" property', () => { + describe('when it is not a string', () => { + beforeEach(() => { + transformedContact.externalId = 1 + }) + + it('throws an error', async () => { + expect(() => { + ImportCompanyContactValidator.go(transformedContact) + }).to.throw('"externalId" must be a string') + }) + }) + + describe('when it is not present', () => { + beforeEach(() => { + delete transformedContact.externalId + }) + + it('throws an error', async () => { + expect(() => { + ImportCompanyContactValidator.go(transformedContact) + }).to.throw('"externalId" is required') + }) + }) + }) +}) + +function _transformedContact () { + return { + salutation: 'Mr', + initials: 'H', + firstName: 'James', + lastName: 'Bond', + externalId: '1:1940' + } +}