@verii/data-loader 1.0.0-pre.1752076816
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +201 -0
- package/jest.config.js +20 -0
- package/package.json +45 -0
- package/src/batch-issuing/README.md +97 -0
- package/src/batch-issuing/constants.js +33 -0
- package/src/batch-issuing/fetchers.js +114 -0
- package/src/batch-issuing/file-readers.js +15 -0
- package/src/batch-issuing/file-writers.js +34 -0
- package/src/batch-issuing/index.js +124 -0
- package/src/batch-issuing/orchestrators.js +332 -0
- package/src/batch-issuing/prepare-data.js +167 -0
- package/src/batch-issuing/prompts.js +47 -0
- package/src/batch-issuing/validate-directory-exists.js +11 -0
- package/src/data-loader.js +14 -0
- package/src/helpers/common.js +30 -0
- package/src/helpers/compute-activation-date.js +12 -0
- package/src/helpers/index.js +8 -0
- package/src/helpers/load-csv.js +34 -0
- package/src/helpers/load-handlebars-template.js +9 -0
- package/src/helpers/parse-column.js +8 -0
- package/src/helpers/prepare-variable-sets.js +23 -0
- package/src/index.js +3 -0
- package/src/vendor-credentials/README.md +72 -0
- package/src/vendor-credentials/execute-update.js +32 -0
- package/src/vendor-credentials/index.js +49 -0
- package/src/vendor-credentials/orchestrator.js +22 -0
- package/src/vendor-credentials/prepare-data.js +36 -0
- package/test/batch-issuing.test.js +1523 -0
- package/test/data/badge-offer.template.json +22 -0
- package/test/data/batch-vars-offerids.csv +3 -0
- package/test/data/batch-vars.csv +3 -0
- package/test/data/batchissuing-variables.csv +3 -0
- package/test/data/driver-license-offer.template.json +10 -0
- package/test/data/driver-license-variables.csv +3 -0
- package/test/data/email-offer.template.json +10 -0
- package/test/data/email-variables.csv +3 -0
- package/test/data/id-document-offer.template.json +10 -0
- package/test/data/id-document-variables.csv +3 -0
- package/test/data/national-id-card-offer.template.json +10 -0
- package/test/data/national-id-card-variables.csv +3 -0
- package/test/data/offer.template.json +22 -0
- package/test/data/passport-offer.template.json +10 -0
- package/test/data/passport-variables.csv +3 -0
- package/test/data/person.template.json +15 -0
- package/test/data/phone-offer.template.json +10 -0
- package/test/data/phone-variables.csv +2 -0
- package/test/data/phones-batch-vars-offerids.csv +3 -0
- package/test/data/proof-of-age-offer.template.json +10 -0
- package/test/data/proof-of-age-variables.csv +3 -0
- package/test/data/resident-permit-offer.template.json +10 -0
- package/test/data/resident-permit-variables.csv +3 -0
- package/test/data/variables-no-did.csv +3 -0
- package/test/data/variables.csv +3 -0
- package/test/data/with-bom.csv +3 -0
- package/test/vendor-credentials.test.js +227 -0
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
const console = require('console');
|
|
2
|
+
const chalk = require('chalk');
|
|
3
|
+
const fs = require('fs');
|
|
4
|
+
const path = require('path');
|
|
5
|
+
|
|
6
|
+
const writeFile = (filePath, fileContent) => {
|
|
7
|
+
const fileBasename = path.basename(filePath, '.*');
|
|
8
|
+
|
|
9
|
+
console.info(`${chalk.green('Writing:')} ${chalk.whiteBright(fileBasename)}`);
|
|
10
|
+
|
|
11
|
+
fs.writeFileSync(filePath, fileContent, 'utf8');
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
const readFile = (filePath, missingError) => {
|
|
15
|
+
if (!fs.existsSync(filePath)) {
|
|
16
|
+
throw new Error(missingError);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
return fs.readFileSync(filePath, 'utf8');
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
const printError = (ex) => console.error(ex);
|
|
23
|
+
const printInfo = (data) => console.info(data);
|
|
24
|
+
|
|
25
|
+
module.exports = {
|
|
26
|
+
printInfo,
|
|
27
|
+
writeFile,
|
|
28
|
+
readFile,
|
|
29
|
+
printError,
|
|
30
|
+
};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
const { flow } = require('lodash/fp');
|
|
2
|
+
const { addHours } = require('date-fns/fp');
|
|
3
|
+
|
|
4
|
+
const computeActivationDate = ({
|
|
5
|
+
activatesInHours = 0 /* by default activate immediately */,
|
|
6
|
+
}) => {
|
|
7
|
+
return flow(addHours(activatesInHours))(new Date());
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
module.exports = {
|
|
11
|
+
computeActivationDate,
|
|
12
|
+
};
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
const fs = require('fs');
|
|
2
|
+
const csv = require('csv-parser');
|
|
3
|
+
const stripBom = require('strip-bom-stream');
|
|
4
|
+
const { isString, indexOf } = require('lodash/fp');
|
|
5
|
+
|
|
6
|
+
const loadCsv = (fileName) => {
|
|
7
|
+
return new Promise((resolve, reject) => {
|
|
8
|
+
const csvRows = [];
|
|
9
|
+
let csvHeaders;
|
|
10
|
+
fs.createReadStream(fileName)
|
|
11
|
+
.pipe(stripBom())
|
|
12
|
+
.pipe(csv())
|
|
13
|
+
.on('headers', (headers) => {
|
|
14
|
+
// eslint-disable-next-line better-mutation/no-mutation
|
|
15
|
+
csvHeaders = headers;
|
|
16
|
+
})
|
|
17
|
+
.on('data', (data) => {
|
|
18
|
+
// eslint-disable-next-line better-mutation/no-mutating-methods
|
|
19
|
+
csvRows.push(data);
|
|
20
|
+
})
|
|
21
|
+
.on('err', (err) => reject(err))
|
|
22
|
+
.on('end', () => {
|
|
23
|
+
resolve([csvHeaders, csvRows]);
|
|
24
|
+
});
|
|
25
|
+
});
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
const getColIndex = (csvHeaders, column) =>
|
|
29
|
+
isString(column) ? indexOf(column, csvHeaders) : column;
|
|
30
|
+
|
|
31
|
+
const getColName = (csvHeaders, column) =>
|
|
32
|
+
isString(column) ? column : csvHeaders[column];
|
|
33
|
+
|
|
34
|
+
module.exports = { loadCsv, getColIndex, getColName };
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
const Handlebars = require('handlebars');
|
|
2
|
+
const { readFile } = require('./common');
|
|
3
|
+
|
|
4
|
+
const loadHandlebarsTemplate = (filename) => {
|
|
5
|
+
const templateString = readFile(filename);
|
|
6
|
+
return Handlebars.compile(templateString);
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
module.exports = { loadHandlebarsTemplate };
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
const { map } = require('lodash/fp');
|
|
2
|
+
const { getColName } = require('./load-csv');
|
|
3
|
+
|
|
4
|
+
const prepareVariableSets = async (
|
|
5
|
+
csvHeaders,
|
|
6
|
+
csvRows,
|
|
7
|
+
{ vendorUseridColumn, vars, did }
|
|
8
|
+
) => {
|
|
9
|
+
const overrideVars = { ...vars };
|
|
10
|
+
if (did != null) {
|
|
11
|
+
overrideVars.did = did;
|
|
12
|
+
}
|
|
13
|
+
return map(
|
|
14
|
+
(csvRow) => ({
|
|
15
|
+
...csvRow,
|
|
16
|
+
...overrideVars,
|
|
17
|
+
vendorUserId: csvRow[getColName(csvHeaders, vendorUseridColumn)],
|
|
18
|
+
}),
|
|
19
|
+
csvRows
|
|
20
|
+
);
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
module.exports = { prepareVariableSets };
|
package/src/index.js
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
# vendor-credentials command
|
|
2
|
+
|
|
3
|
+
Used for creating users and then attaching them to specific credentials.
|
|
4
|
+
|
|
5
|
+
- Supports new users only, not matching to existing users
|
|
6
|
+
- Credentials & People must be specified as a handlebars templates. The data is populated from two sources
|
|
7
|
+
- the csv specified with variable names the same as the headers
|
|
8
|
+
- any `-v` arg parsed in
|
|
9
|
+
|
|
10
|
+
Checkout the [test data](../test/data).
|
|
11
|
+
|
|
12
|
+
## How to Use
|
|
13
|
+
`data-loader vendorcreds [options]`
|
|
14
|
+
|
|
15
|
+
### Config
|
|
16
|
+
|
|
17
|
+
#### General config
|
|
18
|
+
|
|
19
|
+
`-c [CSV_FILENAME]` **required** parameter containing the csv file
|
|
20
|
+
|
|
21
|
+
`-p [OUTPUT_PATH]` **required** the output directory to use where QR codes and output state files are stored
|
|
22
|
+
|
|
23
|
+
`-l [LABEL]` A label to attach to the records added to the agent
|
|
24
|
+
|
|
25
|
+
`--dry-run` Run a test that parses the CSV and creates offers, but does not attempt to write the data to an agent. great for testing!
|
|
26
|
+
|
|
27
|
+
#### Issuing config
|
|
28
|
+
|
|
29
|
+
`--new` to create a new issuing configuration ("disclosure")
|
|
30
|
+
|
|
31
|
+
`-i [DISCLOSURE_ID]` an existing disclosure to use for the batch issuing
|
|
32
|
+
|
|
33
|
+
`-d [DID]` **required** the issuer's DID
|
|
34
|
+
|
|
35
|
+
`-t [URL]` **required** the url to the T&Cs that holder must consent to
|
|
36
|
+
|
|
37
|
+
`--purpose` The purpose to display to the user. Use a maximum for 64 chars. Default is "Career Credential Issuing"
|
|
38
|
+
|
|
39
|
+
`--authTokenExpiresIn` The number of minutes that the offer will be available for after activation. Default is 365 days.
|
|
40
|
+
|
|
41
|
+
#### User authentication and matching config
|
|
42
|
+
|
|
43
|
+
`-y [ID_CREDENTIAL_TYPE]` the credential type used for identifying the user. Default is Email.
|
|
44
|
+
|
|
45
|
+
`-u [COLUMN]` the column from the CSV for the user id. Value is made available as "vendorUserId" in the offer template. Default is the first column
|
|
46
|
+
|
|
47
|
+
`-m [COLUMN]` the column from the CSV for the user to be matched against the ID credential's "identifier" property. Default is the first column
|
|
48
|
+
|
|
49
|
+
#### Offer generation config
|
|
50
|
+
|
|
51
|
+
`-o [OFFER_TEMPLATE_FILENAME]` **required** offer handlebars template. Use moustaches around variables such as `{{did}}`
|
|
52
|
+
|
|
53
|
+
`--var=[VAR_NAME]=[VAR_VALUE]` variables used in the templates can be specified on the command line. They override any csv values
|
|
54
|
+
|
|
55
|
+
#### Credential Agent config
|
|
56
|
+
|
|
57
|
+
`-e [URL]` **required if not a dryrun** the endpoint of the mockvendor server
|
|
58
|
+
|
|
59
|
+
`-t [AUTH_TOKEN]` **required if not a dryrun** the bearer token to use when calling the mockvendor server
|
|
60
|
+
|
|
61
|
+
`--legacy` the target credential agent is running in the "LEGACY" offer type mode. Default is false
|
|
62
|
+
|
|
63
|
+
#### Output config
|
|
64
|
+
|
|
65
|
+
`-x` if passed an output csv is generated including the vendor's user id as the first column and the generated qrcode filename and deeplink
|
|
66
|
+
|
|
67
|
+
`--x-name [OUTPUT_CSV_NAME]` The file name for the output CSV. Default is "output"
|
|
68
|
+
|
|
69
|
+
### Dry Run Example
|
|
70
|
+
Dry runs that print out what updates will be issued should omit the `-e` prop
|
|
71
|
+
|
|
72
|
+
`./data-loader vendorcreds -c ./test/data/variables.csv -o ./test/data/offer.template.json -d did:ion:sap456`
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
const got = require('got');
|
|
2
|
+
const { printInfo } = require('../helpers/common');
|
|
3
|
+
|
|
4
|
+
const setupGot = ({ endpoint, authToken }) => {
|
|
5
|
+
const options = {
|
|
6
|
+
prefixUrl: endpoint,
|
|
7
|
+
};
|
|
8
|
+
if (authToken != null) {
|
|
9
|
+
options.headers = { Authorization: `Bearer ${authToken}` };
|
|
10
|
+
}
|
|
11
|
+
return got.extend(options);
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
const initExecuteUpdate = (options) => {
|
|
15
|
+
const vendorGot = setupGot(options);
|
|
16
|
+
return async ({ person, offer }) => {
|
|
17
|
+
if (person) {
|
|
18
|
+
printInfo({
|
|
19
|
+
createdPerson: await vendorGot
|
|
20
|
+
.post('api/users', { json: person })
|
|
21
|
+
.json(),
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
printInfo({
|
|
25
|
+
createdOffer: await vendorGot.post('api/offers', { json: offer }).json(),
|
|
26
|
+
});
|
|
27
|
+
};
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
module.exports = {
|
|
31
|
+
initExecuteUpdate,
|
|
32
|
+
};
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
const { program } = require('commander');
|
|
2
|
+
const { reduce } = require('lodash/fp');
|
|
3
|
+
const { printInfo, parseColumn } = require('../helpers');
|
|
4
|
+
const { executeVendorCredentials } = require('./orchestrator');
|
|
5
|
+
|
|
6
|
+
const parseVar = reduce((acc, pair) => {
|
|
7
|
+
const [key, value] = pair.split('=');
|
|
8
|
+
acc[key] = value;
|
|
9
|
+
return acc;
|
|
10
|
+
}, {});
|
|
11
|
+
|
|
12
|
+
program
|
|
13
|
+
.name('data-loader vendorcreds')
|
|
14
|
+
.description('Loads data into a db')
|
|
15
|
+
.usage('[options]')
|
|
16
|
+
.requiredOption(
|
|
17
|
+
'-c, --csv-filename <filename>',
|
|
18
|
+
'File name containing variables'
|
|
19
|
+
)
|
|
20
|
+
.requiredOption(
|
|
21
|
+
'-o, --offer-template-filename <filename>',
|
|
22
|
+
'File name containing the credential template file'
|
|
23
|
+
)
|
|
24
|
+
.option(
|
|
25
|
+
'-p, --person-template-filename <filename>',
|
|
26
|
+
'File name containing the credential template file'
|
|
27
|
+
)
|
|
28
|
+
.option(
|
|
29
|
+
'-e, --endpoint <url>',
|
|
30
|
+
'Endpoint to call to upload the people and credentials to'
|
|
31
|
+
)
|
|
32
|
+
.option(
|
|
33
|
+
'-u --vendor-userid-column <vendorUseridColumn>',
|
|
34
|
+
`the column from the CSV that is users id. Value is made available as "vendorUserId" in the offer template. Accepts
|
|
35
|
+
header name or index. Default is 0.`,
|
|
36
|
+
parseColumn,
|
|
37
|
+
'0'
|
|
38
|
+
)
|
|
39
|
+
.option('-t, --auth-token <url>', 'Bearer Auth Token to use')
|
|
40
|
+
.option('-l, --label <label>', 'A label to attach to the documents inserted')
|
|
41
|
+
.option('-v, --var <var...>', 'A variable to add. use name=value')
|
|
42
|
+
.action(async () => {
|
|
43
|
+
const options = program.opts();
|
|
44
|
+
// eslint-disable-next-line better-mutation/no-mutation
|
|
45
|
+
options.vars = parseVar(options.var);
|
|
46
|
+
printInfo(options);
|
|
47
|
+
await executeVendorCredentials(options);
|
|
48
|
+
})
|
|
49
|
+
.parseAsync(process.argv);
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
const { initExecuteUpdate } = require('./execute-update');
|
|
2
|
+
const { prepareData } = require('./prepare-data');
|
|
3
|
+
const { loadCsv, printInfo } = require('../helpers');
|
|
4
|
+
|
|
5
|
+
// eslint-disable-next-line consistent-return
|
|
6
|
+
const executeVendorCredentials = async (options) => {
|
|
7
|
+
const [csvHeaders, csvRows] = await loadCsv(options.csvFilename);
|
|
8
|
+
const updates = await prepareData(csvHeaders, csvRows, options);
|
|
9
|
+
|
|
10
|
+
if (options.endpoint == null) {
|
|
11
|
+
printInfo(JSON.stringify({ updates }));
|
|
12
|
+
return updates;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const executeUpdate = initExecuteUpdate(options);
|
|
16
|
+
for (const update of updates) {
|
|
17
|
+
// eslint-disable-next-line no-await-in-loop
|
|
18
|
+
await executeUpdate(update);
|
|
19
|
+
}
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
module.exports = { executeVendorCredentials };
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
const { map } = require('lodash/fp');
|
|
2
|
+
const {
|
|
3
|
+
loadHandlebarsTemplate,
|
|
4
|
+
} = require('../helpers/load-handlebars-template');
|
|
5
|
+
const { prepareVariableSets } = require('../helpers');
|
|
6
|
+
|
|
7
|
+
const prepareData = async (csvHeaders, csvRows, options) => {
|
|
8
|
+
const variableSets = await prepareVariableSets(csvHeaders, csvRows, options);
|
|
9
|
+
|
|
10
|
+
const { offerTemplateFilename, personTemplateFilename, label } = options;
|
|
11
|
+
const offerTemplate = loadHandlebarsTemplate(offerTemplateFilename);
|
|
12
|
+
const personTemplate = personTemplateFilename
|
|
13
|
+
? loadHandlebarsTemplate(personTemplateFilename)
|
|
14
|
+
: undefined;
|
|
15
|
+
return map((variableSet) => {
|
|
16
|
+
return {
|
|
17
|
+
offer: prepareDocument({ template: offerTemplate, variableSet, label }),
|
|
18
|
+
person: personTemplate
|
|
19
|
+
? prepareDocument({ template: personTemplate, variableSet, label })
|
|
20
|
+
: undefined,
|
|
21
|
+
};
|
|
22
|
+
}, variableSets);
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
const prepareDocument = ({ template, variableSet, label }) => {
|
|
26
|
+
const offerString = template(variableSet);
|
|
27
|
+
const json = JSON.parse(offerString);
|
|
28
|
+
return {
|
|
29
|
+
...json,
|
|
30
|
+
label,
|
|
31
|
+
};
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
module.exports = {
|
|
35
|
+
prepareData,
|
|
36
|
+
};
|