Skip to content
This repository has been archived by the owner on Dec 9, 2024. It is now read-only.

Commit

Permalink
Merge pull request #222 from zarathustra323/omeda-idx-boolean-questions
Browse files Browse the repository at this point in the history
Sync IdentityX custom boolean questions with Omeda
  • Loading branch information
zarathustra323 authored Jan 19, 2022
2 parents 37ed52c + 8bd00d4 commit 069a735
Show file tree
Hide file tree
Showing 13 changed files with 197 additions and 48 deletions.
2 changes: 2 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -342,6 +342,8 @@ services:
IDENTITYX_APP_ID: ${EXAMPLE_IDENTITYX_APP_ID-60774dbe3dac5936323ae121}
IDENTITYX_API_TOKEN: ${EXAMPLE_IDENTITYX_API_TOKEN-}

OMEDA_CLIENT_KEY: ${EXAMPLE_OMEDA_CLIENT_KEY-client_allu}
OMEDA_GRAPHQL_URI: ${EXAMPLE_OMEDA_GRAPHQL_URI-}
OMEDA_BRAND_KEY: ${EXAMPLE_OMEDA_BRAND_KEY-allucd}
OMEDA_APP_ID: ${EXAMPLE_OMEDA_APP_ID}
OMEDA_INPUT_ID: ${EXAMPLE_OMEDA_INPUT_ID}
Expand Down
1 change: 1 addition & 0 deletions packages/marko-web-identity-x/api/fragment-types.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"__schema":{"types":[{"kind":"INTERFACE","name":"FieldInterface","possibleTypes":[{"name":"SelectField"},{"name":"BooleanField"}]}]}}
4 changes: 3 additions & 1 deletion packages/marko-web-identity-x/api/fragments/active-user.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@ fragment ActiveUserFragment on AppUser {
sort: { field: name, order: asc }
}) {
id
hasAnswered
answer
value
field {
id
label
Expand All @@ -38,7 +41,6 @@ fragment ActiveUserFragment on AppUser {
identifier { value type }
}
}
value
}
customSelectFieldAnswers(input: {
onlyActive: true
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,10 +47,10 @@ export default {
computed: {
given: {
get() {
return this.value;
return Boolean(this.value);
},
set(given) {
this.$emit('input', given);
this.$emit('input', Boolean(given));
},
},
},
Expand Down
6 changes: 3 additions & 3 deletions packages/marko-web-identity-x/browser/profile.vue
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@
:id="fieldAnswer.id"
:message="fieldAnswer.field.label"
:required="fieldAnswer.field.required"
:value="fieldAnswer.value"
:value="fieldAnswer.answer"
@input="onCustomBooleanChange(fieldAnswer.id)"
/>
</div>
Expand Down Expand Up @@ -373,8 +373,8 @@ export default {
onCustomBooleanChange(id) {
const objIndex = this.customBooleanFieldAnswers.findIndex((obj => obj.id === id));
const value = !this.customBooleanFieldAnswers[objIndex].value;
this.customBooleanFieldAnswers[objIndex].value = value;
const answer = !this.customBooleanFieldAnswers[objIndex].answer;
this.customBooleanFieldAnswers[objIndex].answer = answer;
this.user.customBooleanFieldAnswers = this.customBooleanFieldAnswers;
},
Expand Down
4 changes: 3 additions & 1 deletion packages/marko-web-identity-x/routes/profile.js
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,9 @@ module.exports = asyncRoute(async (req, res) => {
// only update custom questions when there some :)
const customBooleanFieldsInput = customBooleanFieldAnswers.map(fieldAnswer => ({
fieldId: fieldAnswer.field.id,
value: fieldAnswer.value,
// can either be true, false or null. convert null to false.
// the form submit is effectively answers the question.
value: Boolean(fieldAnswer.answer),
}));
await identityX.client.mutate({
mutation: customBooleanFieldsMutation,
Expand Down
7 changes: 5 additions & 2 deletions packages/marko-web-identity-x/utils/create-client.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
const fetch = require('node-fetch');
const { ApolloClient } = require('apollo-client');
const { InMemoryCache } = require('apollo-cache-inmemory');
const { InMemoryCache, IntrospectionFragmentMatcher } = require('apollo-cache-inmemory');
const { createHttpLink } = require('apollo-link-http');
const { setContext } = require('apollo-link-context');
const introspectionQueryResultData = require('../api/fragment-types.json');

const rootConfig = {
connectToDevTools: false,
Expand Down Expand Up @@ -40,6 +41,8 @@ module.exports = ({
...config,
...rootConfig,
link: contextLink.concat(httpLink),
cache: new InMemoryCache(),
cache: new InMemoryCache({
fragmentMatcher: new IntrospectionFragmentMatcher({ introspectionQueryResultData }),
}),
});
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
const isOmedaNamespace = require('./is-omeda-namespace');

module.exports = ({ externalId, brandKey } = {}) => isOmedaNamespace({
externalId,
brandKey,
type: 'deploymentType',
valueMatcher: id => parseInt(id, 10),
});
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
const gql = require('graphql-tag');
const { get, getAsArray } = require('@parameter1/base-cms-object-path');
const isOmedaDeploymentTypeId = require('../external-id/is-deployment-type-id');
const isOmedaDemographicId = require('../external-id/is-demographic-id');

const FIELD_QUERY = gql`
Expand All @@ -9,6 +10,7 @@ const FIELD_QUERY = gql`
node {
id
name
type
active
externalId {
id
Expand All @@ -22,6 +24,11 @@ const FIELD_QUERY = gql`
externalIdentifier
}
}
... on BooleanField {
whenTrue { type value }
whenFalse { type value }
}
}
}
}
Expand All @@ -41,6 +48,9 @@ const CUSTOMER_QUERY = gql`
demographic { id description }
value { id description }
}
primaryEmailAddress {
optInStatus { deploymentTypeId status { id } }
}
}
}
`;
Expand All @@ -51,8 +61,14 @@ const SET_OMEDA_DATA = gql`
}
`;

const SET_OMEDA_DEMOGRAPHIC_DATA = gql`
mutation SetOmedaDemographicData($input: UpdateAppUserCustomSelectAnswersMutationInput!) {
const SET_OMEDA_BOOLEAN_FIELD_ANSWERS = gql`
mutation SetOmedaBooleanFieldAnswers($input: UpdateAppUserCustomBooleanAnswersMutationInput!) {
updateAppUserCustomBooleanAnswers(input: $input) { id }
}
`;

const SET_OMEDA_SELECT_FIELD_ANSWERS = gql`
mutation SetOmedaSelectFieldAnswers($input: UpdateAppUserCustomSelectAnswersMutationInput!) {
updateAppUserCustomSelectAnswers(input: $input) { id }
}
`;
Expand Down Expand Up @@ -98,31 +114,95 @@ const setOmedaDemographics = async ({
return map;
}, new Map());

const answerMap = new Map();
const booleanAnswerMap = new Map();
const selectAnswerMap = new Map();
omedaLinkedFields.forEach((field) => {
if (answeredQuestionMap.has(field.id)) return;
const { value: demoId } = field.externalId.identifier;
const valueIdSet = omedaCustomerDemoValuesMap.get(demoId);
if (!valueIdSet) return;
field.options.forEach((option) => {
const { externalIdentifier } = option;
if (!externalIdentifier || !valueIdSet.has(externalIdentifier)) return;
if (!answerMap.has(field.id)) answerMap.set(field.id, new Set());
answerMap.get(field.id).add(option.id);
});

if (field.type === 'select') {
field.options.forEach((option) => {
const { externalIdentifier } = option;
if (!externalIdentifier || !valueIdSet.has(externalIdentifier)) return;
if (!selectAnswerMap.has(field.id)) selectAnswerMap.set(field.id, new Set());
selectAnswerMap.get(field.id).add(option.id);
});
}

if (field.type === 'boolean') {
const { whenTrue, whenFalse } = field;
if (whenTrue.type === 'INTEGER' && valueIdSet.has(`${whenTrue.value}`)) {
booleanAnswerMap.set(field.id, true);
return;
}
if (whenFalse.type === 'INTEGER' && valueIdSet.has(`${whenFalse.value}`)) {
booleanAnswerMap.set(field.id, false);
}
}
});

if (answerMap.size) {
const answers = [];
answerMap.forEach((optionIdSet, fieldId) => {
answers.push({ fieldId, optionIds: [...optionIdSet] });
});
await identityX.client.mutate({
mutation: SET_OMEDA_DEMOGRAPHIC_DATA,
variables: { input: { id: user.id, answers } },
context: { apiToken: identityX.config.getApiToken() },
});
}
await Promise.all([
(async () => {
if (!selectAnswerMap.size) return;
const answers = [];
selectAnswerMap.forEach((optionIdSet, fieldId) => {
answers.push({ fieldId, optionIds: [...optionIdSet] });
});
await identityX.client.mutate({
mutation: SET_OMEDA_SELECT_FIELD_ANSWERS,
variables: { input: { id: user.id, answers } },
context: { apiToken: identityX.config.getApiToken() },
});
})(),
(async () => {
if (!booleanAnswerMap.size) return;
const answers = [];
booleanAnswerMap.forEach((value, fieldId) => {
answers.push({ fieldId, value });
});
await identityX.client.mutate({
mutation: SET_OMEDA_BOOLEAN_FIELD_ANSWERS,
variables: { input: { id: user.id, answers } },
context: { apiToken: identityX.config.getApiToken() },
});
})(),
]);
};

const setOmedaDeploymentTypes = async ({
identityX,
user,
omedaCustomer,
omedaLinkedFields,
answeredQuestionMap,
}) => {
const omedaDeploymentOptInMap = getAsArray(omedaCustomer, 'primaryEmailAddress.optInStatus').reduce((map, { deploymentTypeId, status }) => {
const optedIn = status.id === 'IN';
map.set(`${deploymentTypeId}`, optedIn);
return map;
}, new Map());

const answerMap = new Map();
omedaLinkedFields.forEach((field) => {
if (answeredQuestionMap.has(field.id)) return;
const { value: deploymentTypeId } = field.externalId.identifier;
const optedIn = omedaDeploymentOptInMap.get(deploymentTypeId);
if (optedIn == null) return;
answerMap.set(field.id, optedIn);
});
if (!answerMap.size) return;

const answers = [];
answerMap.forEach((value, fieldId) => {
answers.push({ fieldId, value });
});
await identityX.client.mutate({
mutation: SET_OMEDA_BOOLEAN_FIELD_ANSWERS,
variables: { input: { id: user.id, answers } },
context: { apiToken: identityX.config.getApiToken() },
});
};

module.exports = async ({
Expand All @@ -147,21 +227,37 @@ module.exports = async ({
}),
]);

const omedaLinkedFields = getAsArray(data, 'fields.edges')
.map(edge => edge.node)
.filter((field) => {
if (!field.active || !field.externalId) return false;
return isOmedaDemographicId({ externalId: field.externalId, brandKey });
});
const omedaLinkedFields = {
demographic: [],
deploymentType: [],
};
getAsArray(data, 'fields.edges').forEach((edge) => {
const { node: field } = edge;
const { externalId } = field;
if (!field.active || !externalId) return;
if (isOmedaDemographicId({ externalId, brandKey })) {
omedaLinkedFields.demographic.push(field);
}
if (field.type === 'boolean' && isOmedaDeploymentTypeId({ externalId, brandKey })) {
omedaLinkedFields.deploymentType.push(field);
}
});

const answeredQuestionMap = user.customSelectFieldAnswers.reduce((map, select) => {
if (!select.hasAnswered) return map;
map.set(select.field.id, true);
return map;
}, new Map());
const answeredQuestionMap = new Map();
user.customSelectFieldAnswers.forEach((select) => {
if (!select.hasAnswered) return;
answeredQuestionMap.set(select.field.id, true);
});
user.customBooleanFieldAnswers.forEach((boolean) => {
if (!boolean.hasAnswered) return;
answeredQuestionMap.set(boolean.field.id, true);
});

const hasAnsweredAllOmedaQuestions = omedaLinkedFields
.every(field => answeredQuestionMap.has(field.id));
.demographic.every(field => answeredQuestionMap.has(field.id))
&& omedaLinkedFields
.deploymentType.every(field => answeredQuestionMap.has(field.id));

if (user.verified && user.hasAnsweredAllOmedaQuestions) {
return;
}
Expand All @@ -171,13 +267,22 @@ module.exports = async ({
const promises = [];
if (!user.verified) promises.push(setOmedaData({ identityX, user, omedaCustomer }));
if (!hasAnsweredAllOmedaQuestions) {
promises.push(setOmedaDemographics({
identityX,
user,
omedaCustomer,
omedaLinkedFields,
answeredQuestionMap,
}));
promises.push((async () => {
await setOmedaDemographics({
identityX,
user,
omedaCustomer,
omedaLinkedFields: omedaLinkedFields.demographic,
answeredQuestionMap,
});
await setOmedaDeploymentTypes({
identityX,
user,
omedaCustomer,
omedaLinkedFields: omedaLinkedFields.deploymentType,
answeredQuestionMap,
});
})());
}
await Promise.all(promises);
};
20 changes: 20 additions & 0 deletions packages/marko-web-omeda-identity-x/rapid-identify.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
const gql = require('graphql-tag');
const { get, getAsArray } = require('@parameter1/base-cms-object-path');
const isOmedaDemographicId = require('./external-id/is-demographic-id');
const isDeploymentTypeId = require('./external-id/is-deployment-type-id');

const ALPHA3_CODE = gql`
query GetAlpha3Code($alpha2: String!) {
Expand Down Expand Up @@ -67,6 +68,24 @@ module.exports = async ({
};
});

const deploymentTypes = [];
getAsArray(appUser, 'customBooleanFieldAnswers').forEach((boolean) => {
const { field, hasAnswered } = boolean;
const { externalId } = field;
if (!field.active || !externalId || !hasAnswered) return;

const { identifier } = field.externalId;
const id = parseInt(identifier.value, 10);

if (isOmedaDemographicId({ externalId, brandKey })) {
demographics.push({ id, values: [`${boolean.value}`] });
}

if (isDeploymentTypeId({ externalId, brandKey })) {
deploymentTypes.push({ id, optedIn: boolean.answer });
}
});

const { id, encryptedCustomerId } = await omedaRapidIdentify({
email: appUser.email,
productId,
Expand All @@ -78,6 +97,7 @@ module.exports = async ({
...(regionCode && { regionCode }),
...(postalCode && { postalCode }),
...(demographics.length && { demographics }),
...(deploymentTypes.length && { deploymentTypes }),
...(promoCode && { promoCode }),
});

Expand Down
Loading

0 comments on commit 069a735

Please sign in to comment.