Skip to content

Commit

Permalink
Add multi words search by name (People API) (#67)
Browse files Browse the repository at this point in the history
  • Loading branch information
williambelle authored Jul 13, 2023
1 parent d0529a5 commit a8a0cd0
Show file tree
Hide file tree
Showing 6 changed files with 122 additions and 12 deletions.
16 changes: 5 additions & 11 deletions src/services/people.service.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
const util = require('../utils/helper.util');
const ldapUtil = require('../utils/ldap.util');
const ldapConfig = require('../configs/ldap.config');
const ldapService = require('../services/ldap.service');

Expand Down Expand Up @@ -25,17 +27,9 @@ function getPersonByPhone (number) {
}

function getPersonByName (name) {
const term = name.split(/\s+/);

if (term.length === 2) {
const filter = `(displayName=*${term.join('*')}*)`;
term.reverse();
return getPerson(
`(|${filter}(displayName=*${term.join('*')}*))`
);
} else {
return getPerson(`(|(displayName=*${term[0]}*))`);
}
const terms = name.split(/\s+/);
const ldapQuery = ldapUtil.buildLdapQueryForPerson(util.permutations(terms));
return getPerson(ldapQuery);
}

module.exports = {
Expand Down
29 changes: 29 additions & 0 deletions src/utils/helper.util.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,34 @@
* @module utils/helper
*/

/**
* Generates all permutations of an array's elements.
*
* @example
* const util = require('../utils/helper.util');
* util.permutations([ 'Bo', 'Katan', 'Kryze' ])
* // => [[ 'Bo', 'Katan', 'Kryze' ], [ 'Bo', 'Kryze', 'Katan' ],
* [ 'Katan', 'Bo', 'Kryze' ], [ 'Katan', 'Kryze', 'Bo' ],
* [ 'Kryze', 'Bo', 'Katan' ], [ 'Kryze', 'Katan', 'Bo' ]]
*
* @param {array} arr An array of terms.
* @returns {array<array>} Return the array of array with all permutations.
*/
function permutations (arr) {
if (arr.length <= 2) {
return arr.length === 2 ? [arr, [arr[1], arr[0]]] : [arr];
}
return arr.reduce(
(acc, item, i) =>
acc.concat(
permutations([...arr.slice(0, i), ...arr.slice(i + 1)]).map(
val => [item, ...val]
)
),
[]
);
};

/**
* Validate environment variables.
*
Expand All @@ -25,5 +53,6 @@ function validateEnv (key) {
}

module.exports = {
permutations,
validateEnv
};
37 changes: 37 additions & 0 deletions src/utils/ldap.util.js
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,42 @@ function sortPersons (obj, q) {
);
}

/**
* Build the LDAP query to search persons by name.
*
* @example
* const ldapUtil = require('../utils/ldap.util');
* const array = [ [ 'Jango' ]];
* ldapUtil.buildLdapQueryForPerson(array);
* // => '(|(displayName=*Jango*)))'
*
* @example
* const array = [ [ 'Fett', 'Boba' ], [ 'Boba', 'Fett' ] ];
* ldapUtil.buildLdapQueryForPerson(array);
* // => '(|(displayName=*Fett*Boba*)(displayName=*Boba*Fett*))'
*
* @example
* const array = [
* [ 'Bo', 'Katan', 'Kryze' ], [ 'Bo', 'Kryze', 'Katan' ],
* [ 'Katan', 'Bo', 'Kryze' ], [ 'Katan', 'Kryze', 'Bo' ],
* [ 'Kryze', 'Bo', 'Katan' ], [ 'Kryze', 'Katan', 'Bo' ]
* ];
* ldapUtil.buildLdapQueryForPerson(array);
* // => '(|(displayName=*Bo*Katan*Kryze*)(displayName=*Bo*Kryze*Katan*) \
* (displayName=*Katan*Bo*Kryze*)(displayName=*Katan*Kryze*Bo*) \
* (displayName=*Kryze*Bo*Katan*)(displayName=*Kryze*Katan*Bo*))'
*
* @param {array} array An array of array with terms permutations.
* @returns {string} Return the LDAP query.
*/
function buildLdapQueryForPerson (array) {
let ldapQuery = '';
for (const terms of array) {
ldapQuery += `(displayName=*${terms.join('*')}*)`;
}
return `(|${ldapQuery})`;
}

/**
* Transform dn to acronym (unit).
*
Expand Down Expand Up @@ -165,6 +201,7 @@ function ldap2api (ldapResults, q, hl) {
}

module.exports = {
buildLdapQueryForPerson,
dn2acronym,
dn2path,
getProfile,
Expand Down
9 changes: 8 additions & 1 deletion tests/people.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ describe('Test API People ("/api/ldap")', () => {
expect(JSON.parse(response.text)).toStrictEqual(jsonResult);
});

test('It should find firstname Jango', async () => {
test('It should find firstname Din', async () => {
const jsonResult = require('./resources/people/json-name-din-en.json');
const response = await request(app).get('/api/ldap?q=Din&hl=en');
expect(response.statusCode).toBe(200);
Expand Down Expand Up @@ -93,6 +93,13 @@ describe('Test API People ("/api/ldap")', () => {
expect(JSON.parse(response.text)).toStrictEqual(jsonResult);
});

test('It should find Bo Katan Kryze', async () => {
const jsonResult = require('./resources/people/json-name-kryze-fr.json');
const response = await request(app).get('/api/ldap?q=Bo Katan Kryze');
expect(response.statusCode).toBe(200);
expect(JSON.parse(response.text)).toStrictEqual(jsonResult);
});

test('It should not find sciper 679999 without ldap server', async () => {
const mockLdapService = jest.spyOn(ldapService, 'searchAll');
mockLdapService.mockRejectedValue(new Error('LDAP is Gone'));
Expand Down
22 changes: 22 additions & 0 deletions tests/resources/ldap/directory.json
Original file line number Diff line number Diff line change
Expand Up @@ -130,5 +130,27 @@
],
"ou;lang-en": "Mandalore is a planet located in the Outer Rim territories of the galaxy."
}
},
{
"dn": "cn=Bo-Katan Kryze,ou=kalevala,ou=tv-4,ou=so,o=epfl,c=ch",
"attributes": {
"cn": ["Bo-Katan Kryze", "Kryze"],
"sn": "Kryze",
"givenName": "Bo-Katan",
"displayName": "Bo-Katan Kryze",
"uniqueIdentifier": "670006",
"mail": "[email protected]",
"objectClass": "Person",
"roomNumber": "Castle Kryze",
"telephoneNumber": "+41 21 0054323",
"EPFLAccredOrder": "1",
"description": "Princesse",
"description;lang-en": "Princess",
"ou": [
"Kalevala",
"Kalevala est une planète du système Mandalore située dans les territoires de la bordure extérieure."
],
"ou;lang-en": "Kalevala is a planet in the Mandalore system located in the Outer Rim territories."
}
}
]
21 changes: 21 additions & 0 deletions tests/resources/people/json-name-kryze-fr.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
[
{
"sciper": "670006",
"rank": 0,
"name": "Kryze",
"firstname": "Bo-Katan",
"profile": "bo-katan.kryze",
"email": "[email protected]",
"accreds": [
{
"phoneList": ["+41 21 0054323"],
"officeList": ["Castle Kryze"],
"path": "EPFL/SO/TV-4/KALEVALA",
"rank": "1",
"position": "Princesse",
"acronym": "KALEVALA",
"name": "Kalevala est une planète du système Mandalore située dans les territoires de la bordure extérieure."
}
]
}
]

0 comments on commit a8a0cd0

Please sign in to comment.