Issue #96: Migrate Node Utils to Separate Repo (#98)

* Issue #96: Migrate Node Utils to Separate Repo
* Updated gradle dependencies, retested builds
This commit is contained in:
Joshua Darnell 2021-12-20 16:46:19 -08:00 committed by GitHub
parent 9181ef192d
commit e33ff98a61
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 15 additions and 47302 deletions

View File

@ -21,12 +21,12 @@ repositories {
dependencies { dependencies {
compile 'com.google.guava:guava:30.1.1-jre' compile 'com.google.guava:guava:30.1.1-jre'
compile 'com.google.inject:guice:5.0.1' compile 'com.google.inject:guice:5.0.1'
compile 'commons-cli:commons-cli:1.4' compile 'commons-cli:commons-cli:1.5.0'
compile 'org.apache.logging.log4j:log4j-api:2.13.0' compile 'org.apache.logging.log4j:log4j-api:2.17.0'
compile 'org.apache.logging.log4j:log4j-slf4j-impl:2.13.0' compile 'org.apache.logging.log4j:log4j-slf4j-impl:2.17.0'
compile 'org.apache.logging.log4j:log4j-1.2-api:2.13.0' compile 'org.apache.logging.log4j:log4j-1.2-api:2.17.0'
compile 'org.apache.logging.log4j:log4j-core:2.13.0' compile 'org.apache.logging.log4j:log4j-core:2.17.0'
compile 'org.apache.olingo:odata-client-api:4.8.0' compile 'org.apache.olingo:odata-client-api:4.8.0'
compile 'org.apache.olingo:odata-commons-core:4.8.0' compile 'org.apache.olingo:odata-commons-core:4.8.0'
@ -35,19 +35,19 @@ dependencies {
compile 'org.apache.poi:poi:5.0.0' compile 'org.apache.poi:poi:5.0.0'
compile 'org.apache.poi:poi-ooxml:5.0.0' compile 'org.apache.poi:poi-ooxml:5.0.0'
compile 'io.rest-assured:rest-assured:4.3.3' compile 'io.rest-assured:rest-assured:4.4.0'
compile 'io.rest-assured:json-path:4.3.3' compile 'io.rest-assured:json-path:4.4.0'
compile 'io.rest-assured:json-schema-validator:4.3.3' compile 'io.rest-assured:json-schema-validator:4.4.0'
compile 'io.cucumber:cucumber-java8:6.10.2' compile 'io.cucumber:cucumber-java8:7.1.0'
compile 'io.cucumber:cucumber-java:6.10.2' compile 'io.cucumber:cucumber-java:7.1.0'
compile 'io.cucumber:cucumber-junit:6.10.2' compile 'io.cucumber:cucumber-junit:7.1.0'
compile 'io.cucumber:cucumber-guice:6.10.2' compile 'io.cucumber:cucumber-guice:7.1.0'
compile 'io.cucumber:cucumber-core:6.10.2' compile 'io.cucumber:cucumber-core:7.1.0'
//TODO: choose one schema validator between this and rest-assured //TODO: choose one schema validator between this and rest-assured
compile 'com.networknt:json-schema-validator:1.0.51' compile 'com.networknt:json-schema-validator:1.0.63'
compile 'com.google.code.gson:gson:2.8.6' compile 'com.google.code.gson:gson:2.8.9'
compile 'org.apache.commons:commons-text:1.9' compile 'org.apache.commons:commons-text:1.9'
} }
@ -107,7 +107,6 @@ task testWebApiCore_2_0_0() {
systemProperties = System.getProperties() systemProperties = System.getProperties()
args = [ args = [
'--strict',
'--plugin', '--plugin',
'pretty', 'pretty',
'--plugin', '--plugin',

View File

@ -1,118 +0,0 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
lerna-debug.log*
.pnpm-debug.log*
# Diagnostic reports (https://nodejs.org/api/report.html)
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
# Runtime data
pids
*.pid
*.seed
*.pid.lock
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
coverage
*.lcov
# nyc test coverage
.nyc_output
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# Bower dependency directory (https://bower.io/)
bower_components
# node-waf configuration
.lock-wscript
# Compiled binary addons (https://nodejs.org/api/addons.html)
build/Release
# Dependency directories
node_modules/
jspm_packages/
# Snowpack dependency directory (https://snowpack.dev/)
web_modules/
# TypeScript cache
*.tsbuildinfo
# Optional npm cache directory
.npm
# Optional eslint cache
.eslintcache
# Microbundle cache
.rpt2_cache/
.rts2_cache_cjs/
.rts2_cache_es/
.rts2_cache_umd/
# Optional REPL history
.node_repl_history
# Output of 'npm pack'
*.tgz
# Yarn Integrity file
.yarn-integrity
# dotenv environment variables file
.env
.env.test
.env.production
# parcel-bundler cache (https://parceljs.org/)
.cache
.parcel-cache
# Next.js build output
.next
out
# Nuxt.js build / generate output
.nuxt
dist
# Gatsby files
.cache/
# Comment in the public line in if your project uses Gatsby and not Next.js
# https://nextjs.org/blog/next-9-1#public-directory-support
# public
# vuepress build output
.vuepress/dist
# Serverless directories
.serverless/
# FuseBox cache
.fusebox/
# DynamoDB Local files
.dynamodb/
# TernJS port file
.tern-port
# Stores VSCode versions used for testing VSCode extensions
.vscode-test
# yarn v2
.yarn/cache
.yarn/unplugged
.yarn/build-state.yml
.yarn/install-state.gz
.pnp.*

View File

@ -1,193 +0,0 @@
const fs = require('fs');
const fse = require('fs-extra');
const { execSync } = require('child_process');
//parse command line args
const yargs = require('yargs/yargs');
const { hideBin } = require('yargs/helpers');
const argv = yargs(hideBin(process.argv)).argv;
const { processDataDictionaryResults } = require('./services/postResultsToApi.js');
const { processDataAvailabilityReport } = require('./services/processDataAvailabilityReport.js');
const { COMMANDER_PATH } = require('./batch-config.json');
const CERTIFICATION_RESULTS_PATH = `${COMMANDER_PATH}/build/certification`;
const buildRecipientPath = (providerUoi, recipientUoi) => {
if (!providerUoi) throw Error('providerUoi is required!');
if (!recipientUoi) throw Error('recipientUoi is required!');
return `${providerUoi}/${recipientUoi}`;
};
const createResoscriptBearerTokenConfig = ({uri, token} = config) => '<?xml version="1.0" encoding="utf-8" ?>' +
'<OutputScript>' +
' <ClientSettings>' +
` <WebAPIURI>${uri}</WebAPIURI>` +
' <AuthenticationType>authorization_code</AuthenticationType>' +
` <BearerToken>${token}</BearerToken>` +
' </ClientSettings>' +
'</OutputScript>';
const createResoscriptClientCredentialsConfig = ( { uri, clientCredentials } = config) => '<?xml version="1.0" encoding="utf-8" ?>' +
'<OutputScript>' +
' <ClientSettings>' +
` <WebAPIURI>${uri}</WebAPIURI>` +
' <AuthenticationType>client_credentials</AuthenticationType>' +
` <ClientIdentification>${clientCredentials.clientId}</ClientIdentification>` +
` <ClientSecret>${clientCredentials.clientSecret}</ClientSecret>` +
` <TokenURI>${clientCredentials.tokenUri}</TokenURI>` +
` ${clientCredentials.scope ? '<ClientScope>' + clientCredentials.scope + '</ClientScope>': ''}` +
' </ClientSettings>' +
'</OutputScript>';
const isClientCredentalsConfig = ( config = { clientCredentials: {} } ) => config.clientCredentials
&& config.clientCredentials.clientId
&& config.clientCredentials.clientSecret
&& config.clientCredentials.tokenUri;
const isBearerTokenConfig = ( config = { token: '' } ) => !!config.token;
const buildResoscript = (config={}) => {
if (isClientCredentalsConfig(config)) {
return createResoscriptClientCredentialsConfig(config);
} else if (isBearerTokenConfig(config)) {
return createResoscriptBearerTokenConfig(config);
}
return null;
}
const runTests = async jsonConfigPath => {
if (!jsonConfigPath) throw Error("Missing jsonConfigPath.");
try {
providerInfo = JSON.parse(fs.readFileSync(jsonConfigPath));
} catch (err) {
throw new Error('Could not read provider info!');
}
const { providerUoi, configs } = providerInfo;
if (!providerUoi) throw new Error('providerUoi is required!');
if (!configs || !configs.length) throw new Error('configs must contain valid configurations');
try {
if (fs.existsSync(providerUoi)) {
try {
fs.renameSync(providerUoi, `${providerUoi}-old-${Date.now()}`);
} catch (err) {
console.error(err);
throw new Error('Could not rename directory! Exiting!');
}
}
//create root directory
fs.mkdirSync(providerUoi);
const totalTestCount = configs.length;
let failedTestCount = 0;
configs.forEach(config => {
const
RECIPIENT_PATH = buildRecipientPath(providerUoi, config.recipientUoi),
RESOSCRIPT_CONFIG = buildResoscript(config),
CONFIG_PATH = `${COMMANDER_PATH}/${RECIPIENT_PATH}/config.xml`;
if (!RESOSCRIPT_CONFIG) throw new Error('There was a problem creating a RESOScript config for recipientUoi: ' + config.recipientUoi);
//create recipient directory
fs.mkdirSync(RECIPIENT_PATH);
fs.writeFileSync(CONFIG_PATH, RESOSCRIPT_CONFIG);
//run dd tests
const dataDictionaryResult = execSync(`${COMMANDER_PATH}/gradlew testDataDictionary_1_7 -DpathToRESOScript='${CONFIG_PATH}'`,
{ stdio: ['inherit', 'inherit', 'pipe'] });
if (dataDictionaryResult && dataDictionaryResult.stderr) {
console.error('Data Dictionary testing failed for recipientUoi: ' + config.recipientUoi);
console.error(Error(dataDictionaryResult.stderr));
//TODO, create error directory with each corresponding log
process.exitCode = 1;
}
//run data availability tests
const dataAvailabilityResult = execSync(`${COMMANDER_PATH}/gradlew testDataAvailability_1_7 -DpathToRESOScript='${CONFIG_PATH}'`,
{ stdio: ['inherit', 'inherit', 'pipe'] });
if (dataAvailabilityResult && dataAvailabilityResult.stderr) {
console.error('Data Dictionary testing failed for recipientUoi: ' + config.recipientUoi);
console.error(Error(dataAvailabilityResult.stderr));
process.exitCode = 1;
}
const paths = ['results', 'reports', 'cucumberJson'];
paths.forEach(path => {
fse.copySync(`${CERTIFICATION_RESULTS_PATH}/${path}`, RECIPIENT_PATH, { overwrite: true }, err => {
if (err) {
console.error(err);
} else {
console.log(`Copied ${path} to ${RECIPIENT_PATH}`);
}
});
});
});
console.log("Testing complete! Tests passed: " + totalTestCount);
} catch (err) {
console.error(err)
}
};
const processDDResult = async (providerUoi, recipientUoi) =>
await processDataDictionaryResults(providerUoi, recipientUoi, buildRecipientPath(providerUoi, recipientUoi));
// const cliHandler = argv => {
// argv.command({
// command: "action",
// description: "top level command",
// builder: {
// command: "bar",
// description: "child command of foo",
// builder: function() {
// console.log("builder barr!");
// },
// handler: a => {
// console.log("handler barr!");
// }
// },
// handler: args => {
// console.log("handler foo!");
// }
// })
// .demand(1, "must provide a valid command")
// .help("h")
// .alias("h", "help")
// .argv
// if (runTests) {
// const { configFilePath } = argv;
// if (!configFilePath) console.log('configFilePath is required!\nUsage: $ node . --runTests');
// } else if (processDDResult) {
// } else if (dataAvailabilityEtl) {
// } else {
// }
// };
module.exports = {
runTests,
processDDResult,
processDataAvailabilityReport
};

View File

@ -1,4 +0,0 @@
{
"API_KEY": "",
"COMMANDER_PATH": ""
}

View File

@ -1,616 +0,0 @@
{
"name": "runResoscripts",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"dependencies": {
"aws-sdk": "^2.1026.0",
"axios": "^0.24.0",
"fs-extra": "^10.0.0",
"yargs": "^17.3.0"
}
},
"node_modules/ansi-regex": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
"integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
"engines": {
"node": ">=8"
}
},
"node_modules/ansi-styles": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
"dependencies": {
"color-convert": "^2.0.1"
},
"engines": {
"node": ">=8"
},
"funding": {
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
}
},
"node_modules/aws-sdk": {
"version": "2.1027.0",
"resolved": "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.1027.0.tgz",
"integrity": "sha512-j3UjPV9hzyCvkmfcbhRscMggdmrPqlhvo8QzkXCGFfPXjZMh1OJd4HkCEH2NaunzLOyF2Y3QzxKrGOLMT7sNzg==",
"dependencies": {
"buffer": "4.9.2",
"events": "1.1.1",
"ieee754": "1.1.13",
"jmespath": "0.15.0",
"querystring": "0.2.0",
"sax": "1.2.1",
"url": "0.10.3",
"uuid": "3.3.2",
"xml2js": "0.4.19"
},
"engines": {
"node": ">= 10.0.0"
}
},
"node_modules/axios": {
"version": "0.24.0",
"resolved": "https://registry.npmjs.org/axios/-/axios-0.24.0.tgz",
"integrity": "sha512-Q6cWsys88HoPgAaFAVUb0WpPk0O8iTeisR9IMqy9G8AbO4NlpVknrnQS03zzF9PGAWgO3cgletO3VjV/P7VztA==",
"dependencies": {
"follow-redirects": "^1.14.4"
}
},
"node_modules/base64-js": {
"version": "1.5.1",
"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
"integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/feross"
},
{
"type": "patreon",
"url": "https://www.patreon.com/feross"
},
{
"type": "consulting",
"url": "https://feross.org/support"
}
]
},
"node_modules/buffer": {
"version": "4.9.2",
"resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.2.tgz",
"integrity": "sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg==",
"dependencies": {
"base64-js": "^1.0.2",
"ieee754": "^1.1.4",
"isarray": "^1.0.0"
}
},
"node_modules/cliui": {
"version": "7.0.4",
"resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz",
"integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==",
"dependencies": {
"string-width": "^4.2.0",
"strip-ansi": "^6.0.0",
"wrap-ansi": "^7.0.0"
}
},
"node_modules/color-convert": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
"dependencies": {
"color-name": "~1.1.4"
},
"engines": {
"node": ">=7.0.0"
}
},
"node_modules/color-name": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
},
"node_modules/emoji-regex": {
"version": "8.0.0",
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="
},
"node_modules/escalade": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz",
"integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==",
"engines": {
"node": ">=6"
}
},
"node_modules/events": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/events/-/events-1.1.1.tgz",
"integrity": "sha1-nr23Y1rQmccNzEwqH1AEKI6L2SQ=",
"engines": {
"node": ">=0.4.x"
}
},
"node_modules/follow-redirects": {
"version": "1.14.5",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.5.tgz",
"integrity": "sha512-wtphSXy7d4/OR+MvIFbCVBDzZ5520qV8XfPklSN5QtxuMUJZ+b0Wnst1e1lCDocfzuCkHqj8k0FpZqO+UIaKNA==",
"funding": [
{
"type": "individual",
"url": "https://github.com/sponsors/RubenVerborgh"
}
],
"engines": {
"node": ">=4.0"
},
"peerDependenciesMeta": {
"debug": {
"optional": true
}
}
},
"node_modules/fs-extra": {
"version": "10.0.0",
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.0.0.tgz",
"integrity": "sha512-C5owb14u9eJwizKGdchcDUQeFtlSHHthBk8pbX9Vc1PFZrLombudjDnNns88aYslCyF6IY5SUw3Roz6xShcEIQ==",
"dependencies": {
"graceful-fs": "^4.2.0",
"jsonfile": "^6.0.1",
"universalify": "^2.0.0"
},
"engines": {
"node": ">=12"
}
},
"node_modules/get-caller-file": {
"version": "2.0.5",
"resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
"integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
"engines": {
"node": "6.* || 8.* || >= 10.*"
}
},
"node_modules/graceful-fs": {
"version": "4.2.8",
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.8.tgz",
"integrity": "sha512-qkIilPUYcNhJpd33n0GBXTB1MMPp14TxEsEs0pTrsSVucApsYzW5V+Q8Qxhik6KU3evy+qkAAowTByymK0avdg=="
},
"node_modules/ieee754": {
"version": "1.1.13",
"resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz",
"integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg=="
},
"node_modules/is-fullwidth-code-point": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
"integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
"engines": {
"node": ">=8"
}
},
"node_modules/isarray": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
"integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE="
},
"node_modules/jmespath": {
"version": "0.15.0",
"resolved": "https://registry.npmjs.org/jmespath/-/jmespath-0.15.0.tgz",
"integrity": "sha1-o/Iiqarp+Wb10nx5ZRDigJF2Qhc=",
"engines": {
"node": ">= 0.6.0"
}
},
"node_modules/jsonfile": {
"version": "6.1.0",
"resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz",
"integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==",
"dependencies": {
"universalify": "^2.0.0"
},
"optionalDependencies": {
"graceful-fs": "^4.1.6"
}
},
"node_modules/punycode": {
"version": "1.3.2",
"resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz",
"integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0="
},
"node_modules/querystring": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz",
"integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=",
"deprecated": "The querystring API is considered Legacy. new code should use the URLSearchParams API instead.",
"engines": {
"node": ">=0.4.x"
}
},
"node_modules/require-directory": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
"integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/sax": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/sax/-/sax-1.2.1.tgz",
"integrity": "sha1-e45lYZCyKOgaZq6nSEgNgozS03o="
},
"node_modules/string-width": {
"version": "4.2.3",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
"integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
"dependencies": {
"emoji-regex": "^8.0.0",
"is-fullwidth-code-point": "^3.0.0",
"strip-ansi": "^6.0.1"
},
"engines": {
"node": ">=8"
}
},
"node_modules/strip-ansi": {
"version": "6.0.1",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
"integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
"dependencies": {
"ansi-regex": "^5.0.1"
},
"engines": {
"node": ">=8"
}
},
"node_modules/universalify": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz",
"integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==",
"engines": {
"node": ">= 10.0.0"
}
},
"node_modules/url": {
"version": "0.10.3",
"resolved": "https://registry.npmjs.org/url/-/url-0.10.3.tgz",
"integrity": "sha1-Ah5NnHcF8hu/N9A861h2dAJ3TGQ=",
"dependencies": {
"punycode": "1.3.2",
"querystring": "0.2.0"
}
},
"node_modules/uuid": {
"version": "3.3.2",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz",
"integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==",
"deprecated": "Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details.",
"bin": {
"uuid": "bin/uuid"
}
},
"node_modules/wrap-ansi": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
"integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
"dependencies": {
"ansi-styles": "^4.0.0",
"string-width": "^4.1.0",
"strip-ansi": "^6.0.0"
},
"engines": {
"node": ">=10"
},
"funding": {
"url": "https://github.com/chalk/wrap-ansi?sponsor=1"
}
},
"node_modules/xml2js": {
"version": "0.4.19",
"resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.19.tgz",
"integrity": "sha512-esZnJZJOiJR9wWKMyuvSE1y6Dq5LCuJanqhxslH2bxM6duahNZ+HMpCLhBQGZkbX6xRf8x1Y2eJlgt2q3qo49Q==",
"dependencies": {
"sax": ">=0.6.0",
"xmlbuilder": "~9.0.1"
}
},
"node_modules/xmlbuilder": {
"version": "9.0.7",
"resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-9.0.7.tgz",
"integrity": "sha1-Ey7mPS7FVlxVfiD0wi35rKaGsQ0=",
"engines": {
"node": ">=4.0"
}
},
"node_modules/y18n": {
"version": "5.0.8",
"resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",
"integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==",
"engines": {
"node": ">=10"
}
},
"node_modules/yargs": {
"version": "17.3.0",
"resolved": "https://registry.npmjs.org/yargs/-/yargs-17.3.0.tgz",
"integrity": "sha512-GQl1pWyDoGptFPJx9b9L6kmR33TGusZvXIZUT+BOz9f7X2L94oeAskFYLEg/FkhV06zZPBYLvLZRWeYId29lew==",
"dependencies": {
"cliui": "^7.0.2",
"escalade": "^3.1.1",
"get-caller-file": "^2.0.5",
"require-directory": "^2.1.1",
"string-width": "^4.2.3",
"y18n": "^5.0.5",
"yargs-parser": "^21.0.0"
},
"engines": {
"node": ">=12"
}
},
"node_modules/yargs-parser": {
"version": "21.0.0",
"resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.0.0.tgz",
"integrity": "sha512-z9kApYUOCwoeZ78rfRYYWdiU/iNL6mwwYlkkZfJoyMR1xps+NEBX5X7XmRpxkZHhXJ6+Ey00IwKxBBSW9FIjyA==",
"engines": {
"node": ">=12"
}
}
},
"dependencies": {
"ansi-regex": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
"integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="
},
"ansi-styles": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
"requires": {
"color-convert": "^2.0.1"
}
},
"aws-sdk": {
"version": "2.1027.0",
"resolved": "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.1027.0.tgz",
"integrity": "sha512-j3UjPV9hzyCvkmfcbhRscMggdmrPqlhvo8QzkXCGFfPXjZMh1OJd4HkCEH2NaunzLOyF2Y3QzxKrGOLMT7sNzg==",
"requires": {
"buffer": "4.9.2",
"events": "1.1.1",
"ieee754": "1.1.13",
"jmespath": "0.15.0",
"querystring": "0.2.0",
"sax": "1.2.1",
"url": "0.10.3",
"uuid": "3.3.2",
"xml2js": "0.4.19"
}
},
"axios": {
"version": "0.24.0",
"resolved": "https://registry.npmjs.org/axios/-/axios-0.24.0.tgz",
"integrity": "sha512-Q6cWsys88HoPgAaFAVUb0WpPk0O8iTeisR9IMqy9G8AbO4NlpVknrnQS03zzF9PGAWgO3cgletO3VjV/P7VztA==",
"requires": {
"follow-redirects": "^1.14.4"
}
},
"base64-js": {
"version": "1.5.1",
"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
"integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA=="
},
"buffer": {
"version": "4.9.2",
"resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.2.tgz",
"integrity": "sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg==",
"requires": {
"base64-js": "^1.0.2",
"ieee754": "^1.1.4",
"isarray": "^1.0.0"
}
},
"cliui": {
"version": "7.0.4",
"resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz",
"integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==",
"requires": {
"string-width": "^4.2.0",
"strip-ansi": "^6.0.0",
"wrap-ansi": "^7.0.0"
}
},
"color-convert": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
"requires": {
"color-name": "~1.1.4"
}
},
"color-name": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
},
"emoji-regex": {
"version": "8.0.0",
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="
},
"escalade": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz",
"integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw=="
},
"events": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/events/-/events-1.1.1.tgz",
"integrity": "sha1-nr23Y1rQmccNzEwqH1AEKI6L2SQ="
},
"follow-redirects": {
"version": "1.14.5",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.5.tgz",
"integrity": "sha512-wtphSXy7d4/OR+MvIFbCVBDzZ5520qV8XfPklSN5QtxuMUJZ+b0Wnst1e1lCDocfzuCkHqj8k0FpZqO+UIaKNA=="
},
"fs-extra": {
"version": "10.0.0",
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.0.0.tgz",
"integrity": "sha512-C5owb14u9eJwizKGdchcDUQeFtlSHHthBk8pbX9Vc1PFZrLombudjDnNns88aYslCyF6IY5SUw3Roz6xShcEIQ==",
"requires": {
"graceful-fs": "^4.2.0",
"jsonfile": "^6.0.1",
"universalify": "^2.0.0"
}
},
"get-caller-file": {
"version": "2.0.5",
"resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
"integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg=="
},
"graceful-fs": {
"version": "4.2.8",
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.8.tgz",
"integrity": "sha512-qkIilPUYcNhJpd33n0GBXTB1MMPp14TxEsEs0pTrsSVucApsYzW5V+Q8Qxhik6KU3evy+qkAAowTByymK0avdg=="
},
"ieee754": {
"version": "1.1.13",
"resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz",
"integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg=="
},
"is-fullwidth-code-point": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
"integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg=="
},
"isarray": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
"integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE="
},
"jmespath": {
"version": "0.15.0",
"resolved": "https://registry.npmjs.org/jmespath/-/jmespath-0.15.0.tgz",
"integrity": "sha1-o/Iiqarp+Wb10nx5ZRDigJF2Qhc="
},
"jsonfile": {
"version": "6.1.0",
"resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz",
"integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==",
"requires": {
"graceful-fs": "^4.1.6",
"universalify": "^2.0.0"
}
},
"punycode": {
"version": "1.3.2",
"resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz",
"integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0="
},
"querystring": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz",
"integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA="
},
"require-directory": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
"integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I="
},
"sax": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/sax/-/sax-1.2.1.tgz",
"integrity": "sha1-e45lYZCyKOgaZq6nSEgNgozS03o="
},
"string-width": {
"version": "4.2.3",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
"integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
"requires": {
"emoji-regex": "^8.0.0",
"is-fullwidth-code-point": "^3.0.0",
"strip-ansi": "^6.0.1"
}
},
"strip-ansi": {
"version": "6.0.1",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
"integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
"requires": {
"ansi-regex": "^5.0.1"
}
},
"universalify": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz",
"integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ=="
},
"url": {
"version": "0.10.3",
"resolved": "https://registry.npmjs.org/url/-/url-0.10.3.tgz",
"integrity": "sha1-Ah5NnHcF8hu/N9A861h2dAJ3TGQ=",
"requires": {
"punycode": "1.3.2",
"querystring": "0.2.0"
}
},
"uuid": {
"version": "3.3.2",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz",
"integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA=="
},
"wrap-ansi": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
"integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
"requires": {
"ansi-styles": "^4.0.0",
"string-width": "^4.1.0",
"strip-ansi": "^6.0.0"
}
},
"xml2js": {
"version": "0.4.19",
"resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.19.tgz",
"integrity": "sha512-esZnJZJOiJR9wWKMyuvSE1y6Dq5LCuJanqhxslH2bxM6duahNZ+HMpCLhBQGZkbX6xRf8x1Y2eJlgt2q3qo49Q==",
"requires": {
"sax": ">=0.6.0",
"xmlbuilder": "~9.0.1"
}
},
"xmlbuilder": {
"version": "9.0.7",
"resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-9.0.7.tgz",
"integrity": "sha1-Ey7mPS7FVlxVfiD0wi35rKaGsQ0="
},
"y18n": {
"version": "5.0.8",
"resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",
"integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA=="
},
"yargs": {
"version": "17.3.0",
"resolved": "https://registry.npmjs.org/yargs/-/yargs-17.3.0.tgz",
"integrity": "sha512-GQl1pWyDoGptFPJx9b9L6kmR33TGusZvXIZUT+BOz9f7X2L94oeAskFYLEg/FkhV06zZPBYLvLZRWeYId29lew==",
"requires": {
"cliui": "^7.0.2",
"escalade": "^3.1.1",
"get-caller-file": "^2.0.5",
"require-directory": "^2.1.1",
"string-width": "^4.2.3",
"y18n": "^5.0.5",
"yargs-parser": "^21.0.0"
}
},
"yargs-parser": {
"version": "21.0.0",
"resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.0.0.tgz",
"integrity": "sha512-z9kApYUOCwoeZ78rfRYYWdiU/iNL6mwwYlkkZfJoyMR1xps+NEBX5X7XmRpxkZHhXJ6+Ey00IwKxBBSW9FIjyA=="
}
}
}

View File

@ -1,8 +0,0 @@
{
"dependencies": {
"aws-sdk": "^2.1026.0",
"axios": "^0.24.0",
"fs-extra": "^10.0.0",
"yargs": "^17.3.0"
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,110 +0,0 @@
const { Axios } = require('axios');
const fs = require('fs');
const { API_KEY } = require('../batch-config.json');
const getDataDictionaryOptions = (providerUoi, recipientUoi, data) => {
if (!providerUoi) throw new Error('providerUoi is required!');
if (!recipientUoi) throw new Error('recipientUoi is required!');
if (!data) throw new Error('data is required!');
return {
'method': "post",
'baseURL': 'https://certification.reso.org',
'url': `/api/v1/certification_reports/data_dictionary/${providerUoi}`,
'headers': {
'Authorization': `ApiKey ${API_KEY}`,
'recipientUoi': recipientUoi,
'Content-Type': "application/json",
'User-Agent': "CommanderBatchProcess/0.1",
'Accept': "*/*",
'Cache-Control': "no-cache",
'Host': 'certification.reso.org',
'Accept-Encoding': 'gzip, deflate',
'Connection': 'keep-alive'
},
data
};
};
const getDataAvailabilityOptions = (metadataReportId, data) => {
if (!metadataReportId) throw new Error('metadataReportId is required!');
if (!data) throw new Error('data is required!');
return {
'method': "post",
'baseURL': 'https://certification.reso.org',
'url': `/api/v1/payload/data_availability/${metadataReportId}`,
'headers': {
'Authorization': `ApiKey ${API_KEY}`,
'Content-Type': 'application/json',
'User-Agent': 'CommanderBatchProcess/0.1',
'Accept': '*/*',
'Cache-Control': 'no-cache',
'Host': 'certification.reso.org',
'Accept-Encoding': 'gzip, deflate',
'Connection': 'keep-alive'
},
data
};
};
const buildDataDictionaryResultsPath = (providerUoi, recipientUoi) => `${providerUoi}/${recipientUoi}/metadata-report.json`;
const buildDataAvailabilityResultsPath = (providerUoi, recipientUoi) => `${providerUoi}/${recipientUoi}/data-availability-report.json`;
const postDataDictionaryResultsToApi = async (providerUoi, recipientUoi) => {
if (!providerUoi) throw new Error('providerUoi is required!');
if (!recipientUoi) throw new Error('recipientUoi is required!');
try {
const data = await fs.readFileAsync(buildDataDictionaryResultsPath(providerUoi, recipientUoi), 'utf8');
const response = await Axios.post(getDataDictionaryOptions(providerUoi, recipientUoi, data));
if (!response.id) throw new Error('Did not receive the required id parameter from the response!');
return response.id;
} catch (err) {
throw new Error('Could not post data dictionary results to API!' + '\n' + err);
}
}
const postDataAvailabilityResultsToApi = async (metadataReportId, providerUoi, recipientUoi) => {
try {
const data = await fs.readFileAsync(buildDataAvailabilityResultsPath(providerUoi, recipientUoi), 'utf8');
const response = await Axios.post(getDataAvailabilityOptions(metadataReportId, data));
if (!response || !response.success) throw new Error('Api did not report a successful response! ');
return response.id;
} catch (err) {
throw new Error('Could not post data availability results to API!' + '\n' + err);
}
};
const snooze = ms => new Promise(resolve => setTimeout(resolve, ms));
const processDataDictionaryResults = async (providerUoi, recipientUoi) => {
try {
await snooze(5 * 1000); //wait 5s for the dust to settle to avoid thrashing the server
console.log('Posting Data Dictionary results...');
const reportId = await postDataDictionaryResultsToApi(providerUoi, recipientUoi);
console.log('Results posted, reportId: ' + reportId);
await snooze(5 * 1000); //wait 5s for the dust to settle to avoid thrashing the server
if (reportId) {
console.log('Posting data availability results for reportId')
return await postDataAvailabilityResultsToApi(reportId, providerUoi, recipientUoi);
}
} catch (err) {
throw new Error('Could not process data dictionary results! \nError:' + err);
}
return null;
};
module.exports = {
processDataDictionaryResults
}

View File

@ -1,324 +0,0 @@
const fs = require('fs').promises;
const { standardMeta } = require('../references/standardMeta');
const { lookupMap } = require('../references/lookupMap.js');
/**
* Defines the bins template for stats.
* @returns bins template with all bins initialized to 0.
*/
const getBinsTemplate = () => {
return {
eq0: 0,
gt0: 0,
gte25: 0,
gte50: 0,
gte75: 0,
eq100: 0
};
};
/**
* Defines the totals template for stats.
* @returns totals template with all bins initialized to 0.
*/
const getTotalsTemplate = () => {
return {
total: getBinsTemplate(),
reso: getBinsTemplate(),
idx: getBinsTemplate(),
local: getBinsTemplate()
};
};
/**
* Defines the availability template for stats. This is the structure of the processed results.
* @returns availability template with all totals and bins initialized to 0.
*/
const getAvailabilityTemplate = () => {
return {
fields: [],
lookups: [],
lookupValues: [],
resources: [],
availability: {
fields: getTotalsTemplate(),
lookups: getTotalsTemplate(),
resources: {},
resourcesBinary: {}
}
};
};
/**
* Builds a standard field cache from a list of standard fields.
* @param {Array} fields an array of standard fields.
* @returns map of all standard fields addressable by cache[resourceName][fieldName]
* or an empty map if there are none.
*/
const createStandardFieldCache = (fields = []) => {
const resourceFieldCache = {};
fields.forEach(field => {
if (!resourceFieldCache[field.resourceName]) {
resourceFieldCache[field.resourceName] = {};
}
resourceFieldCache[field.resourceName][field.fieldName] = field;
});
return resourceFieldCache;
};
/**
* Builds a lookup value cache from a list of individual lookup value items.
* @param {Array} lookupValues the lookup values to create the cache from.
* @returns map of all lookups addressable by cache[resourceName][fieldName][lookupValue]
* or an empty map if there are none.
*/
const createLookupValueCache = (lookupValues = []) => {
const resourceFieldLookupCache = {};
lookupValues.forEach(lookupValue => {
if (!resourceFieldLookupCache[lookupValue.resourceName]) {
resourceFieldLookupCache[lookupValue.resourceName] = {};
}
if (!resourceFieldLookupCache[lookupValue.resourceName][lookupValue.fieldName]) {
resourceFieldLookupCache[lookupValue.resourceName][lookupValue.fieldName] = {};
}
resourceFieldLookupCache[lookupValue.resourceName][lookupValue.fieldName][lookupValue.lookupValue] = lookupValue;
});
return resourceFieldLookupCache;
}
/**
* Determines whether a given field is an IDX field.
* TODO: The performance could be improved here in that there's a filter being done on each payloads array.
* There's potential speedup if each payload were turned into a nested property rather than an array.
* @param {String} resourceName the name of the resource for the field.
* @param {String} fieldName the name of the field.
* @param {Object} standardFieldCache a field cache created by createStandardFieldCache().
* @returns true if the given field is an IDX field, false otherwise.
*/
const isIdxField = (resourceName, fieldName, standardFieldCache = {}) => resourceName && fieldName
&& isResoField(resourceName, fieldName, standardFieldCache)
&& !!standardFieldCache[resourceName][fieldName].payloads.filter(x => x === "IDX").length > 0;
/**
* Determines whether a given field is a RESO field.
* @param {String} resourceName the name of the resource for the field.
* @param {String} fieldName the name of the field.
* @param {Object} standardFieldCache a field cache created by createStandardFieldCache().
* @returns true if the given field is a RESO field, false otherwise.
*/
const isResoField = (resourceName, fieldName, standardFieldCache = {}) => resourceName && fieldName
&& standardFieldCache[resourceName] && !!standardFieldCache[resourceName][fieldName];
/**
* Determines if a given lookup is a RESO lookup.
* @param {*} resourceName the name of the resource for the field.
* @param {*} fieldName the name of the field.
* @param {*} lookupValue the name of the lookup to test.
* @param {*} standardFieldCache a field cache created by createStandardFieldCache().
* @returns the RESO lookup, if found, otherwise null.
*/
const findResoLookup = (resourceName, fieldName, lookupValue, standardFieldCache = {}) => {
if (resourceName && fieldName && standardFieldCache[resourceName] && standardFieldCache[resourceName][fieldName]) {
const field = standardFieldCache[resourceName][fieldName];
if (field && field.simpleDataType.includes('String List') && field.type.includes('.')) {
const lookupName = field.type.substring(field.type.lastIndexOf('.') + 1, field.type.length);
const lookup = lookupMap[lookupName] && lookupMap[lookupName].find(x => x.lookupValue === lookupValue || x.lookupDisplayName === lookupValue);
//TODO: turn the lookup map into its own inverted hash by lookup values and display names
return lookup ? { lookupName, lookup } : null;
}
}
return null;
};
/**
* Computes availability from existing bins.
* @param {Number} availability the current availability value.
* @param {Object} bins existing bins containing past availability values.
* @returns a new object following the getBinsTemplate structure that contains updated availabilities for each bin.
*/
const computeBins = (availability, bins) => {
if (!bins) return getBinsTemplate();
return {
eq0: availability === 0 ? bins.eq0 + 1 : bins.eq0 || 0,
gt0: availability > 0 ? bins.gt0 + 1 : bins.gt0 || 0,
gte25: availability >= 0.25 ? bins.gte25 + 1 : bins.gte25 || 0,
gte50: availability >= 0.5 ? bins.gte50 + 1 : bins.gte50 || 0,
gte75: availability >= 0.75 ? bins.gte75 + 1 : bins.gte75 || 0,
eq100: availability === 1 ? bins.eq100 + 1 : bins.eq100 || 0
}
};
/**
* Translates existing numeric bins into booleans.
* @param {Object} bins existing bins object.
* @returns the resulting bins object with values transformed to booleans.
*/
const computeBooleanBins = bins => {
const booleanBins = {};
Object.entries(bins).forEach( ([bin, value]) => booleanBins[bin] = !!value);
return booleanBins;
}
/**
* Computes availability from discrete bins, meaning ones with integer values (tallies).
* @param {Object} discreteBins bins using the getBinsTemplate() structure with integer availability values.
* @param {Number} resourceSampleCount the count of the number of sampled records for a given resource.
* @returns a bins object with the decimal availabilities computed.
*/
const computeAvailabilityFromDiscreteBins = (discreteBins=getBinsTemplate(), resourceSampleCount=0) => {
if (!resourceSampleCount) return discreteBins;
const availabilities = {};
Object.entries(discreteBins).forEach(([binName, value]) => availabilities[binName] = 1.0 * value / resourceSampleCount);
return availabilities;
};
/**
* Processes a RESO Data Availability Report and creates aggregates and rollups.
* TODO: individual totals calculations could be tidied up a bit.
* @param {Object} availablityReport the RESO availability report JSON to process.
* @returns a JSON availability report with the appropriate rollups and aggregates.
*/
const process = async availablityReport => {
//iterate over each field and lookup and compute their availabilities
const { resources, fields, lookups, lookupValues } = availablityReport;
const transformed = getAvailabilityTemplate();
const standardFieldCache = createStandardFieldCache(standardMeta.fields);
const resourceCounts = {};
resources.forEach(resource => resourceCounts[resource.resourceName] = resource.numRecordsFetched);
const processedFields = [], processedLookupValues = [], lookupValueCache = createLookupValueCache(lookupValues);
//binary resource availability cache
const resourcesBinary = {};
//process fields
fields.forEach(field => {
const availability = resourceCounts[field.resourceName] !== 0 ? 1.0 * field.frequency / resourceCounts[field.resourceName] : 0;
const fieldBins = computeBins(availability, getBinsTemplate());
//update field availability
transformed.availability.fields.total = computeBins(availability, transformed.availability.fields.total);
//add totals template for this resource name if it doesn't already exist
if (!resourcesBinary[field.resourceName]) {
resourcesBinary[field.resourceName] = { fields: getTotalsTemplate(), lookups: getTotalsTemplate() };
}
//update binary resource bins
resourcesBinary[field.resourceName].fields.total = computeBins(availability, resourcesBinary[field.resourceName].fields.total);
if (isResoField(field.resourceName, field.fieldName, standardFieldCache)) {
//update RESO totals
transformed.availability.fields.reso = computeBins(availability, transformed.availability.fields.reso);
resourcesBinary[field.resourceName].fields.reso = computeBins(availability, resourcesBinary[field.resourceName].fields.reso);
if (isIdxField(field.resourceName, field.fieldName, standardFieldCache)) {
//update IDX totals
transformed.availability.fields.idx = computeBins(availability, transformed.availability.fields.idx);
resourcesBinary[field.resourceName].fields.idx = computeBins(availability, resourcesBinary[field.resourceName].fields.idx);
}
} else {
//otherwise, update local totals
transformed.availability.fields.local = computeBins(availability, transformed.availability.fields.local);
resourcesBinary[field.resourceName].fields.local = computeBins(availability, resourcesBinary[field.resourceName].fields.local);
}
//only process if there are lookups for this field
const lookupsForField = lookupValueCache[field.resourceName] && lookupValueCache[field.resourceName][field.fieldName];
if (lookupsForField) {
Object.values(lookupsForField).forEach(lookupValue => {
if (lookupValue.lookupValue !== 'null' && lookupValue.lookupValue !== 'NULL_VALUE') {
const lookupAvailability = !!lookupValue.frequency && !!resourceCounts[field.resourceName]
? 1.0 * lookupValue.frequency / resourceCounts[field.resourceName] : 0;
const lookupBins = computeBins(lookupAvailability, getBinsTemplate());
transformed.availability.lookups.total = computeBins(availability, transformed.availability.lookups.total);
resourcesBinary[field.resourceName].lookups.total = computeBins(availability, resourcesBinary[field.resourceName].lookups.total);
if (isResoField(lookupValue.resourceName, lookupValue.fieldName, standardFieldCache) &&
findResoLookup(lookupValue.resourceName, lookupValue.fieldName, lookupValue.lookupValue, standardFieldCache)) {
transformed.availability.lookups.reso = computeBins(availability, transformed.availability.lookups.reso);
resourcesBinary[field.resourceName].lookups.reso = computeBins(availability, resourcesBinary[field.resourceName].lookups.reso);
if (isIdxField(lookupValue.resourceName, lookupValue.fieldName, standardFieldCache)) {
transformed.availability.lookups.idx = computeBins(availability, transformed.availability.lookups.idx);
resourcesBinary[field.resourceName].lookups.idx = computeBins(availability, resourcesBinary[field.resourceName].lookups.idx);
}
} else {
transformed.availability.lookups.local = computeBins(availability, transformed.availability.lookups.local);
resourcesBinary[field.resourceName].lookups.local = computeBins(availability, resourcesBinary[field.resourceName].lookups.local);
}
processedLookupValues.push({ ...lookupValue, lookupAvailability, ...computeBooleanBins(lookupBins) });
}
});
}
if (!!field) {
processedFields.push({ ...field, availability, ...computeBooleanBins(fieldBins) });
}
});
transformed.resources = resources;
transformed.fields = processedFields;
transformed.lookups = lookups;
transformed.lookupValues = processedLookupValues;
transformed.availability.resourcesBinary = resourcesBinary;
//compute resource availability rollups from the discrete bins
const resourceAvailability = {};
transformed.availability.resources = Object.entries(resourcesBinary).forEach(([resourceName, value]) => {
if (!resourceAvailability[resourceName]) resourceAvailability[resourceName] = {};
const { fields = getTotalsTemplate(), lookups = getTotalsTemplate() } = value;
const resourceCount = resourceCounts[resourceName] || 0;
resourceAvailability[resourceName].fields = {
total: computeAvailabilityFromDiscreteBins(fields.total, resourceCount),
reso: computeAvailabilityFromDiscreteBins(fields.reso, resourceCount),
idx: computeAvailabilityFromDiscreteBins(fields.idx, resourceCount),
local: computeAvailabilityFromDiscreteBins(fields.local, resourceCount)
};
resourceAvailability[resourceName].lookups = {
total: computeAvailabilityFromDiscreteBins(lookups.total, resourceCount),
reso: computeAvailabilityFromDiscreteBins(lookups.reso, resourceCount),
idx: computeAvailabilityFromDiscreteBins(lookups.idx, resourceCount),
local: computeAvailabilityFromDiscreteBins(lookups.local, resourceCount)
};
});
transformed.availability.resources = resourceAvailability;
return transformed;
}
/**
* Processes a RESO data availability report at the given path and writes it to a local file
* in the current path called 'availability-processed.json'.
* @param {String} pathToDataAvailabilityReport the path to the data availability report to process.
*/
const processDataAvailabilityReport = async pathToDataAvailabilityReport => {
try {
const availablityReport = JSON.parse(await fs.readFile(pathToDataAvailabilityReport, 'utf8'));
const startTime = new Date();
await fs.writeFile('./availability-processed.json', JSON.stringify(await process(availablityReport)));
console.log("Time taken: ", new Date() - startTime, "ms");
} catch (err) {
console.error(err);
}
}
module.exports = {
processDataAvailabilityReport
};