Skip to content
Snippets Groups Projects
Commit ee923652 authored by Hugo SUBTIL's avatar Hugo SUBTIL
Browse files

feat: add new safety onboarding

parent 42179a75
No related branches found
No related tags found
1 merge request!21US878 feat(rechercherpoint): add two fallback in case of no rechercher point match
This commit is part of merge request !21. Comments created here will be created in the context of that merge request.
const xml2js = require('xml2js')
const { errors } = require('cozy-konnector-libs')
const { findUserAddress } = require('../../src/core/findUserAddress')
const mockSoapRequest = jest.fn()
jest.mock('easy-soap-request', () => async () => mockSoapRequest())
jest.spyOn(xml2js, 'parseStringPromise').mockResolvedValue({
Envelope: {
Body: {
Fault: { detail: { erreur: { resultat: { $: { code: 401 } } } } },
faultstring: 'Mock error',
},
},
})
const mockParseUserPdl = jest.fn()
jest.mock('../../src/helpers/parsing', () => ({
parseUserPdl: () => mockParseUserPdl(),
}))
const responseMock = {
response: {
body: 'mockedBody',
},
}
describe('findUserAddress', () => {
it('should throw LOGIN_FAIL if soapRequest fails', async () => {
mockSoapRequest.mockRejectedValueOnce('reject')
try {
await findUserAddress()
expect(true).toBe(false)
} catch (error) {
expect(error).toBe(errors.VENDOR_DOWN)
}
})
it('should return a correct pdl number', async () => {
mockSoapRequest.mockResolvedValue(responseMock)
mockParseUserPdl.mockResolvedValue('12345')
try {
await findUserAddress()
expect(true).toBe(false)
} catch (error) {
expect(error).toBe(errors.NOT_EXISTING_DIRECTORY)
}
})
})
...@@ -26,12 +26,8 @@ const responseMock = { ...@@ -26,12 +26,8 @@ const responseMock = {
} }
describe('recherchePoint', () => { describe('recherchePoint', () => {
it('should throw LOGIN_FAILED for too many responses', async () => { it('should throw LOGIN_FAIL if soapRequest fails', async () => {
mockSoapRequest.mockResolvedValue(responseMock) mockSoapRequest.mockRejectedValueOnce('reject')
mockParseUserPdl.mockImplementationOnce(() => {
throw new Error('Error')
})
try { try {
await findUserPdl() await findUserPdl()
expect(true).toBe(false) expect(true).toBe(false)
...@@ -40,8 +36,17 @@ describe('recherchePoint', () => { ...@@ -40,8 +36,17 @@ describe('recherchePoint', () => {
} }
}) })
it('should throw LOGIN_FAIL if soapRequest fails', async () => { it('should return a correct pdl number', async () => {
mockSoapRequest.mockRejectedValueOnce('reject') mockSoapRequest.mockResolvedValueOnce(responseMock)
mockParseUserPdl.mockResolvedValueOnce('12345')
expect(await findUserPdl()).toBe('12345')
})
it('should handle issue on parsing', async () => {
mockSoapRequest.mockResolvedValueOnce(responseMock)
mockParseUserPdl.mockRejectedValue(new Error('Async error'))
try { try {
await findUserPdl() await findUserPdl()
expect(true).toBe(false) expect(true).toBe(false)
...@@ -49,11 +54,4 @@ describe('recherchePoint', () => { ...@@ -49,11 +54,4 @@ describe('recherchePoint', () => {
expect(error).toBe(errors.LOGIN_FAILED) expect(error).toBe(errors.LOGIN_FAILED)
} }
}) })
it('should return a correct pdl number', async () => {
mockSoapRequest.mockResolvedValue(responseMock)
mockParseUserPdl.mockResolvedValue('12345')
expect(await findUserPdl()).toBe('12345')
})
}) })
// @ts-check
const { log, errors } = require('cozy-konnector-libs')
const soapRequest = require('easy-soap-request')
const {
parseTags,
parseValue,
parseUserAddress,
} = require('../helpers/parsing')
const xml2js = require('xml2js')
const { consulterDonneesTechniquesContractuelles } = require('../requests/sge')
/**
* Get user contract start date
* @param {string} url
* @param {string} apiAuthKey
* @param {string} userLogin
* @param {number} pointId
* @returns {Promise<Address>}
*/
async function findUserAddress(url, apiAuthKey, userLogin, pointId) {
log('info', 'Fetching user address')
const sgeHeaders = {
'Content-Type': 'text/xml;charset=UTF-8',
apikey: apiAuthKey,
}
const { response } = await soapRequest({
url: `${url}/enedis_SGE_ConsultationDonneesTechniquesContractuelles/1.0`,
headers: sgeHeaders,
xml: consulterDonneesTechniquesContractuelles(pointId, userLogin, false),
}).catch(err => {
log('error', 'Error while fetching user : ' + err)
throw errors.VENDOR_DOWN
})
const result = await xml2js.parseStringPromise(response.body, {
tagNameProcessors: [parseTags],
valueProcessors: [parseValue],
explicitArray: false,
})
try {
return parseUserAddress(result)
} catch (error) {
log('error', 'Error while processing user address: ' + error)
log(
'error',
`Enedis issue ${result.Envelope.Body.Fault.detail.erreur.resultat.$.code}: ${result.Envelope.Body.Fault.faultstring}`
)
throw errors.NOT_EXISTING_DIRECTORY
}
}
module.exports = { findUserAddress }
...@@ -22,7 +22,8 @@ async function findUserPdl( ...@@ -22,7 +22,8 @@ async function findUserPdl(
name, name,
address, address,
postalCode, postalCode,
inseeCode inseeCode,
escalierEtEtageEtAppartement = ''
) { ) {
log('info', 'Fetching user data') log('info', 'Fetching user data')
const sgeHeaders = { const sgeHeaders = {
...@@ -33,7 +34,14 @@ async function findUserPdl( ...@@ -33,7 +34,14 @@ async function findUserPdl(
const { response } = await soapRequest({ const { response } = await soapRequest({
url: url, url: url,
headers: sgeHeaders, headers: sgeHeaders,
xml: rechercherPoint(appLogin, name, postalCode, inseeCode, address), xml: rechercherPoint(
appLogin,
name,
postalCode,
inseeCode,
address,
escalierEtEtageEtAppartement
),
}).catch(err => { }).catch(err => {
log('error', 'rechercherPointResponse') log('error', 'rechercherPointResponse')
log('error', err) log('error', err)
...@@ -50,13 +58,13 @@ async function findUserPdl( ...@@ -50,13 +58,13 @@ async function findUserPdl(
return parseUserPdl(parsedReply) return parseUserPdl(parsedReply)
} catch (error) { } catch (error) {
log('error', 'Error while parsing user PDL: ' + error) log('error', 'Error while parsing user PDL: ' + error)
console.log(error)
if (parsedReply.Envelope.Body.Fault) { if (parsedReply.Envelope.Body.Fault) {
log( log(
'error', 'error',
`Enedis issue ${parsedReply.Envelope.Body.Fault.detail.erreur.resultat.$.code}: ${parsedReply.Envelope.Body.Fault.faultstring}` `Enedis issue ${parsedReply.Envelope.Body.Fault.detail.erreur.resultat.$.code}: ${parsedReply.Envelope.Body.Fault.faultstring}`
) )
} }
throw errors.LOGIN_FAILED
} }
} }
......
...@@ -4,6 +4,7 @@ const { terminateContract } = require('./contractTermination') ...@@ -4,6 +4,7 @@ const { terminateContract } = require('./contractTermination')
const { verifyContract } = require('./contractVerification') const { verifyContract } = require('./contractVerification')
const { findUserPdl } = require('./findUserPdl') const { findUserPdl } = require('./findUserPdl')
const { verifyUserIdentity } = require('./verifyUserIdentity') const { verifyUserIdentity } = require('./verifyUserIdentity')
const { findUserAddress } = require('./findUserAddress')
module.exports = { module.exports = {
activateContract, activateContract,
...@@ -12,4 +13,5 @@ module.exports = { ...@@ -12,4 +13,5 @@ module.exports = {
verifyContract, verifyContract,
findUserPdl, findUserPdl,
verifyUserIdentity, verifyUserIdentity,
findUserAddress,
} }
...@@ -87,3 +87,19 @@ ...@@ -87,3 +87,19 @@
* @property {number} consentId * @property {number} consentId
* @property {string} inseeCode * @property {string} inseeCode
*/ */
/**
* Address definition
* @typedef {object} Address
* @property {string} escalierEtEtageEtAppartement
* @property {string} numeroEtNomVoie
* @property {string} codePostal
* @property {Commune} commune
*/
/**
* Commune definition
* @typedef {object} Commune
* @property {{code: string}} $
* @property {string} libelle
*/
...@@ -2,6 +2,11 @@ ...@@ -2,6 +2,11 @@
const { log, errors } = require('cozy-konnector-libs') const { log, errors } = require('cozy-konnector-libs')
const { findUserPdl } = require('./findUserPdl') const { findUserPdl } = require('./findUserPdl')
const { getInseeCode } = require('../requests/insee') const { getInseeCode } = require('../requests/insee')
const { findUserAddress } = require('./findUserAddress')
const {
removeMultipleSpaces,
removeAddressnumber,
} = require('../helpers/parsing')
/** /**
* Verify user identity * Verify user identity
...@@ -26,7 +31,8 @@ async function verifyUserIdentity( ...@@ -26,7 +31,8 @@ async function verifyUserIdentity(
inseeCode = await getInseeCode(fields.postalCode, fields.city) inseeCode = await getInseeCode(fields.postalCode, fields.city)
} }
const pdl = await findUserPdl( // First try with user adresse
let pdl = await findUserPdl(
`${baseUrl}/enedis_SDE_recherche-point/1.0`, `${baseUrl}/enedis_SDE_recherche-point/1.0`,
apiAuthKey, apiAuthKey,
loginUtilisateur, loginUtilisateur,
...@@ -36,8 +42,50 @@ async function verifyUserIdentity( ...@@ -36,8 +42,50 @@ async function verifyUserIdentity(
inseeCode inseeCode
) )
if (!pdl) {
log('warn', 'Second change for sge onboarding')
// Backup verification
const userAddress = await findUserAddress(
baseUrl,
apiAuthKey,
loginUtilisateur,
fields.pointId
)
const escalierEtEtageEtAppartement = userAddress.escalierEtEtageEtAppartement
? removeMultipleSpaces(userAddress.escalierEtEtageEtAppartement)
: ''
pdl = await findUserPdl(
`${baseUrl}/enedis_SDE_recherche-point/1.0`,
apiAuthKey,
loginUtilisateur,
fields.lastname,
removeMultipleSpaces(userAddress.numeroEtNomVoie),
userAddress.codePostal,
userAddress.commune.$.code,
escalierEtEtageEtAppartement
)
// Third try, remove address number because it's buggy on SGE side
if (!pdl) {
log('warn', 'Lastchance onboarding for sge')
pdl = await findUserPdl(
`${baseUrl}/enedis_SDE_recherche-point/1.0`,
apiAuthKey,
loginUtilisateur,
fields.lastname,
removeMultipleSpaces(removeAddressnumber(userAddress.numeroEtNomVoie)),
userAddress.codePostal,
userAddress.commune.$.code,
escalierEtEtageEtAppartement
)
}
}
if (fields.pointId != pdl) { if (fields.pointId != pdl) {
log('error', 'PointId does not match') log('error', 'PointId does not match')
if (isAlternateStart) { if (isAlternateStart) {
throw errors.TERMS_VERSION_MISMATCH throw errors.TERMS_VERSION_MISMATCH
} else { } else {
......
...@@ -29,6 +29,18 @@ function parseContractStartDate(result) { ...@@ -29,6 +29,18 @@ function parseContractStartDate(result) {
'dateDerniereModificationFormuleTarifaireAcheminement' 'dateDerniereModificationFormuleTarifaireAcheminement'
] ]
} }
/**
* Return User address
* @param {string} result
* @returns {Address}
*/
function parseUserAddress(result) {
log('info', 'Parsing user Address')
const json = JSON.stringify(result)
return JSON.parse(json)['Envelope']['Body'][
'consulterDonneesTechniquesContractuellesResponse'
]['point']['donneesGenerales']['adresseInstallation']
}
/** /**
* Return User contract start date * Return User contract start date
...@@ -127,6 +139,24 @@ function parseValue(value, name) { ...@@ -127,6 +139,24 @@ function parseValue(value, name) {
return value return value
} }
/**
* Remove SGE useless multiple white spaces
* @param {string} str
* @returns {string}
*/
function removeMultipleSpaces(str) {
return str.replace(/ +/g, ' ')
}
/**
* Remove SGE useless multiple white spaces
* @param {string} str
* @returns {string}
*/
function removeAddressnumber(str) {
return str.replace(/[0-9]|b |B |T |t /g, '')
}
module.exports = { module.exports = {
parseSgeXmlData, parseSgeXmlData,
formateDataForDoctype, formateDataForDoctype,
...@@ -136,5 +166,8 @@ module.exports = { ...@@ -136,5 +166,8 @@ module.exports = {
parseContracts, parseContracts,
parseContractStartDate, parseContractStartDate,
parseServiceId, parseServiceId,
parseUserAddress,
checkContractExists, checkContractExists,
removeMultipleSpaces,
removeAddressnumber,
} }
...@@ -102,7 +102,11 @@ function consultationMesuresDetailleesMaxPower( ...@@ -102,7 +102,11 @@ function consultationMesuresDetailleesMaxPower(
* @param {string} appLogin * @param {string} appLogin
* @returns {string} * @returns {string}
*/ */
function consulterDonneesTechniquesContractuelles(pointId, appLogin) { function consulterDonneesTechniquesContractuelles(
pointId,
appLogin,
consent = true
) {
log('info', `Query consulterDonneesTechniquesContractuelles`) log('info', `Query consulterDonneesTechniquesContractuelles`)
return `<?xml version='1.0' encoding='utf-8'?> return `<?xml version='1.0' encoding='utf-8'?>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
...@@ -113,7 +117,7 @@ function consulterDonneesTechniquesContractuelles(pointId, appLogin) { ...@@ -113,7 +117,7 @@ function consulterDonneesTechniquesContractuelles(pointId, appLogin) {
<v2:consulterDonneesTechniquesContractuelles> <v2:consulterDonneesTechniquesContractuelles>
<pointId>${pointId}</pointId> <pointId>${pointId}</pointId>
<loginUtilisateur>${appLogin}</loginUtilisateur> <loginUtilisateur>${appLogin}</loginUtilisateur>
<autorisationClient>true</autorisationClient> <autorisationClient>${consent}</autorisationClient>
</v2:consulterDonneesTechniquesContractuelles> </v2:consulterDonneesTechniquesContractuelles>
</soapenv:Body> </soapenv:Body>
</soapenv:Envelope> </soapenv:Envelope>
...@@ -125,14 +129,45 @@ function consulterDonneesTechniquesContractuelles(pointId, appLogin) { ...@@ -125,14 +129,45 @@ function consulterDonneesTechniquesContractuelles(pointId, appLogin) {
* @param {string} name * @param {string} name
* @param {string} postalCode * @param {string} postalCode
* @param {string} inseeCode * @param {string} inseeCode
* @param {string} [address] * @param {string} address
* @param {string} [escalierEtEtageEtAppartement]
* @returns {string} PDL * @returns {string} PDL
*/ */
function rechercherPoint(appLogin, name, postalCode, inseeCode, address) { function rechercherPoint(
appLogin,
name,
postalCode,
inseeCode,
address,
escalierEtEtageEtAppartement
) {
log( log(
'info', 'info',
`Query rechercherPoint - postal code / insee code: ${postalCode} / ${inseeCode}` `Query rechercherPoint - postal code / insee code: ${postalCode} / ${inseeCode}`
) )
if (escalierEtEtageEtAppartement) {
return `<?xml version='1.0' encoding='utf-8'?>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:v2="http://www.enedis.fr/sge/b2b/services/rechercherpoint/v2.0"
xmlns:v1="http://www.enedis.fr/sge/b2b/technique/v1.0">
<soapenv:Header/>
<soapenv:Body>
<v2:rechercherPoint>
<criteres>
<adresseInstallation>
<escalierEtEtageEtAppartement>${escalierEtEtageEtAppartement}</escalierEtEtageEtAppartement>
<numeroEtNomVoie>${address}</numeroEtNomVoie>
<codePostal>${postalCode}</codePostal>
<codeInseeCommune>${inseeCode}</codeInseeCommune>
</adresseInstallation>
<nomClientFinalOuDenominationSociale>${name}</nomClientFinalOuDenominationSociale>
<rechercheHorsPerimetre>true</rechercheHorsPerimetre>
</criteres>
<loginUtilisateur>${appLogin}</loginUtilisateur>
</v2:rechercherPoint>
</soapenv:Body>
</soapenv:Envelope>`
}
return `<?xml version='1.0' encoding='utf-8'?> return `<?xml version='1.0' encoding='utf-8'?>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:v2="http://www.enedis.fr/sge/b2b/services/rechercherpoint/v2.0" xmlns:v2="http://www.enedis.fr/sge/b2b/services/rechercherpoint/v2.0"
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment