diff --git a/.c8rc b/.c8rc deleted file mode 100644 index 0a46ab9..0000000 --- a/.c8rc +++ /dev/null @@ -1,14 +0,0 @@ -{ - "all": true, - "include": [ - "index.js", - "src/**/*.js" - ], - "exclude": [ - "src/helpers/Logger.js" - ], - "reporter": [ - "lcov", - "text-summary" - ] -} diff --git a/.github/workflows/run-unit-tests.yml b/.github/workflows/run-unit-tests.yml new file mode 100644 index 0000000..5d33719 --- /dev/null +++ b/.github/workflows/run-unit-tests.yml @@ -0,0 +1,33 @@ +name: Run Python unit tests + +on: pull_request: types: [ labeled, unlabeled, opened, reopened, synchronize ] + +jobs: + changelog: + name: Updates changelog + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: dangoslen/changelog-enforcer@v3 + test: + runs-on: ubuntu-latest + steps: + - name: Checkout repo + uses: actions/checkout@v3 + + - name: Set up Python 3.12 + uses: actions/setup-python@v4 + with: + python-version: '3.12' + cache: 'pip' + + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install -r devel_requirements.txt + pip install -r requirements.txt + + - name: Run linter and test suite + run: | + make lint + make test \ No newline at end of file diff --git a/.gitignore b/.gitignore index 7f79dc3..e634aae 100644 --- a/.gitignore +++ b/.gitignore @@ -2,7 +2,15 @@ node_modules coverage +.aws-sam/ +.coverage .DS_Store +.vscode/ +__pycache__/ .terraform.lock.hcl .terraform +.pytest_cache dist.zip +*env/ +*.py[cod] +*$py.class \ No newline at end of file diff --git a/.nvmrc b/.nvmrc deleted file mode 100644 index 8351c19..0000000 --- a/.nvmrc +++ /dev/null @@ -1 +0,0 @@ -14 diff --git a/.python-version b/.python-version new file mode 100644 index 0000000..92536a9 --- /dev/null +++ b/.python-version @@ -0,0 +1 @@ +3.12.0 diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 19faf66..0000000 --- a/.travis.yml +++ /dev/null @@ -1,34 +0,0 @@ -language: node_js -install: -- npm i -before_install: -- wget https://releases.hashicorp.com/terraform/"$TF_VERSION"/terraform_"$TF_VERSION"_linux_amd64.zip -- unzip terraform_"$TF_VERSION"_linux_amd64.zip -- sudo mv terraform /usr/local/bin/ -- rm terraform_"$TF_VERSION"_linux_amd64.zip -jobs: - include: - - stage: test - script: npm test - - stage: deploy qa - if: type IN (push) and branch = qa - env: - - AWS_ACCESS_KEY_ID=$AWS_ACCESS_KEY_ID_QA - - AWS_SECRET_ACCESS_KEY=$AWS_SECRET_ACCESS_KEY_QA - script: - - terraform -chdir=provisioning/qa init -input=false - - echo "Deploying to qa" - - terraform -chdir=provisioning/qa apply -auto-approve -input=false - - stage: deploy production - if: type IN (push) and branch = production - env: - - AWS_ACCESS_KEY_ID=$AWS_ACCESS_KEY_ID_PRODUCTION - - AWS_SECRET_ACCESS_KEY=$AWS_SECRET_ACCESS_KEY_PRODUCTION - script: - - terraform -chdir=provisioning/production init -input=false - - echo "Deploying to production" - - terraform -chdir=provisioning/production apply -auto-approve -input=false -env: - global: - - TF_VERSION=1.0.11 - - secure: TWXvmnn9+683dFupAVoH9YKtj3AttCayIvPtUs075zN7pa+t/+1tDYPLrSzUk/Iy4TmsmOw2PJ4juuP3F7Nv+wqfkN2yij3p3IAZ2CNk2FsEdADGptn8Z2unvwS9k1kivTBPNR1/x6PruZdPC5t/209gd+oIlUEd728JyU7dtkWqftcZeUBGJXKbN/1igZ0NZgC+9BVomSBGe9fzJal+tzG+mVfBlDaXllBTFUz6ZpOWSx0Ixy4ydFZT06z/pM0AUWA163MExL8jAakZJuiWlAT8x7JM8fv6d0RfgxZuFzK0fMp6HEpiVipI6Kqj1bNs75/w1OxEuhcdJET6SIufcMhgPl1R2lMJMy4X0L2/f7r2hzVnPK1zNBIX/MOp2l4Qm+OpK1DtHTke963oKcI6cHTlQxgtsV30V+UZ3wjlMQG06ho61FoeKFAHpoXi5THvogheS4+Q1U6AgTv/qEu6AxAvyK6tZxvmU0hdTH5VaQhwnewNQDBqP0hXmIj1zv+hbrn0Ab2VyLRO7usVwTeZ3gzmP+ydj59CaWkKZIIzwFjbGKy5u1jaWr5uzM00k6Ge0K6DmGZj+c2/2rMRziG8pjCEV0VusY9ny5cZmjMFarzgTDcPMBiWj/nESDQScU+nMxxWxuRZJ7C/Mh2OqNBZUhuEs3pRrVLx8p6F6Blksbg= diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..a4e48e3 --- /dev/null +++ b/Makefile @@ -0,0 +1,21 @@ +.DEFAULT: help + +help: + @echo "make help" + @echo " display this help statement" + @echo "make run" + @echo " run the application in devel" + @echo "make test" + @echo " run associated test suite with pytest" + @echo "make lint" + @echo " lint project files using the black linter" + +run: + export ENVIRONMENT=devel; \ + python -c 'import lambda_function; lambda_function.lambda_handler(None, None)' + +test: + pytest tests -W ignore::DeprecationWarning + +lint: + black ./ --check --exclude="(env/)|(tests/)" \ No newline at end of file diff --git a/README.md b/README.md index df2837a..aef045c 100644 --- a/README.md +++ b/README.md @@ -1,68 +1,48 @@ # Kinesis Firehose Avro to Json Transformer Lambda [![Build Status](https://travis-ci.org/NYPL/firehose-avro-to-json-transformer.svg?branch=main)](https://travis-ci.org/NYPL/firehose-avro-to-json-transformer) -This app reads from Firehose Kinesis streams, decodes the records using the appropriate Avro schema based on the stream name, and returns the resulting records as either JSON or CSV (base64 encoded). This app is responsible for decoding records immediately before ingest into the [BIC](https://github.com/NYPL/BIC). - -## Version -> v1.0.1 - -## Installation - -Install all Node dependencies via NPM - -```console -nvm use -npm install -``` +This Python application is responsible for Avro-decoding events immediately before ingestion into the [BIC](https://github.com/NYPL/BIC). Originally developed for the Data Warehouse, this is deployed as an AWS Lambda (["AvroToJsonTransformer-qa"](https://console.aws.amazon.com/lambda/home?region=us-east-1#/functions/AvroToJsonTransformer-qa?tab=configuration) and ["AvroToJsonTransformer-production"](https://console.aws.amazon.com/lambda/home?region=us-east-1#/functions/AvroToJsonTransformer-production?tab=configuration)). In essence, the code does the following: + - Decodes the incoming batch of records using the corresponding Avro schema, which is determined based on the name of the incoming Kinesis stream + - Converts said records into a hash with `recordId`, `result: 'Ok'`, and `data` containing a JSON or CSV serialization of the record, which is also base64 encoded + - Returns processed records in this format: `{ records: [ { recordId: '[record id]', result: 'Ok', data: 'eyJmb28iOiJiYXIifQ....' }, ... ] }` ## Running Locally -Use the [sam cli](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-cli-install.html) to run the lambda on arbitrary firehose events. To process a firehose event containing 3 CircTrans records and print out the result: +Use the [sam cli](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-cli-install.html) to run the Lambda on arbitrary Firehose events. To process a Firehose event containing 3 CircTrans records and print out the result: ``` -sam local invoke --profile nypl-digital-dev -t sam.qa.yml -e sample/firehose-CircTrans-3-records-encoded.json +sam local invoke --profile nypl-digital-dev -t config/sam.qa.yml -e sample/firehose-CircTrans-3-records-encoded.json ``` -## Contributing +The [sample](./sample) folder contains sample Firehose events and their expected outcomes after Lambda event handling, so you can test the efficacy of your code with various schemas. + +With Python, you also have the option of using the [python-lambda-local](https://pypi.org/project/python-lambda-local/) package for local development! You will need to create a JSON file with env variables to use said package. + +## Contributing / Deployment This repo uses the ["PRs Target Main, Merge to Deployment Branches" git workflow](https://github.com/NYPL/engineering-general/blob/main/standards/git-workflow.md#prs-target-main-merge-to-deployment-branches): - Cut PRs from `main` - Merge `main` > `qa` - Merge `main` > `production` -## Deployment - -This app is deployed via Travis-CI using terraform. Code in `qa` is pushed to AvroToJsonTransformer-qa. Code in `production` is pushed to AvroToJsonTransformer-production. - -## Tests +This app is deployed via Travis-CI using Terraform. Code in `qa` is pushed to ["AvroToJsonTransformer-qa"](https://console.aws.amazon.com/lambda/home?region=us-east-1#/functions/AvroToJsonTransformer-qa?tab=configuration). Code in `production` is pushed to ["AvroToJsonTransformer-production"](https://console.aws.amazon.com/lambda/home?region=us-east-1#/functions/AvroToJsonTransformer-production?tab=configuration). -To run all tests found in `./test/`: - -```console -npm run test +## Test Coverage +Use the Python [coverage package](https://coverage.readthedocs.io/en/7.6.0/) to measure test coverage: ``` - -To run a specific test for the given filename: - -```console -npm run test [filename].test.js +coverage run -m pytest ``` -### Test Coverage - -This repo uses c8 to compute test coverage (because [Istanbul](https://github.com/istanbuljs/nyc) doesn't appear to support ESM at writing). Coverage reports are included at the end of `npm test`. For a detailed line-by-line breakdown, view the HTML report: - -```console -npm run coverage-report -open coverage/index.html +To see what exactly which lines are missing testing: +``` +coverage report -m ``` -### Linting - -This codebase uses [Standard JS](https://www.npmjs.com/package/standard) as the JavaScript linter. +## Linting -To check for linting errors: +This codebase uses [Black](https://github.com/psf/black) as the Python linter. -```console -npm run lint +To format the codebase as a whole: +``` +make lint ``` diff --git a/config/devel.yaml b/config/devel.yaml new file mode 100644 index 0000000..27d0912 --- /dev/null +++ b/config/devel.yaml @@ -0,0 +1,5 @@ +--- +PLAINTEXT_VARIABLES: + ENVIRONMENT: devel + NYPL_DATA_API_BASE_URL: https://qa-platform.nypl.org/api/v0.1/current-schemas/ +... \ No newline at end of file diff --git a/config/production.env b/config/production.env deleted file mode 100644 index 7801ee6..0000000 --- a/config/production.env +++ /dev/null @@ -1,4 +0,0 @@ -NODE_ENV=production -NYPL_DATA_API_BASE_URL=https://platform.nypl.org/api/v0.1/ -SCHEMA_NAME=CircTrans -SCHEMA_PATH=current-schemas/ diff --git a/config/qa.env b/config/qa.env deleted file mode 100644 index 6807a8e..0000000 --- a/config/qa.env +++ /dev/null @@ -1,4 +0,0 @@ -NODE_ENV=production -NYPL_DATA_API_BASE_URL=https://qa-platform.nypl.org/api/v0.1/ -SCHEMA_NAME=CircTrans -SCHEMA_PATH=current-schemas/ diff --git a/sam.qa.yml b/config/sam.qa.yml similarity index 74% rename from sam.qa.yml rename to config/sam.qa.yml index 128e0dd..ff89550 100644 --- a/sam.qa.yml +++ b/config/sam.qa.yml @@ -6,12 +6,11 @@ Resources: AvroToJsonTransformer: Type: AWS::Serverless::Function Properties: - Handler: index.handler - Runtime: nodejs14.x + CodeUri: . + Handler: lambda_function.lambda_handler + Runtime: python3.12 Timeout: 10 Environment: Variables: NYPL_DATA_API_BASE_URL: https://qa-platform.nypl.org/api/v0.1/ - SCHEMA_NAME: CircTrans - SCHEMA_PATH: current-schemas/ LOG_LEVEL: debug diff --git a/context.json b/context.json deleted file mode 100644 index 0967ef4..0000000 --- a/context.json +++ /dev/null @@ -1 +0,0 @@ -{} diff --git a/deployment_script.sh b/deployment_script.sh new file mode 100755 index 0000000..432d072 --- /dev/null +++ b/deployment_script.sh @@ -0,0 +1,10 @@ +#!/bin/zsh + +rm -f -r ./package +rm -f deployment-package.zip +pip3.9 install --target ./package -r requirements.txt +cd package +zip -r ../deployment-package.zip . +cd .. +zip deployment-package.zip lambda_function.py +zip deployment-package.zip record_processor.py \ No newline at end of file diff --git a/devel_requirements.txt b/devel_requirements.txt new file mode 100644 index 0000000..5e20838 --- /dev/null +++ b/devel_requirements.txt @@ -0,0 +1,5 @@ +black +nypl-py-utils[avro-client,config-helper]==1.2.0 +pybase64 +python-csv +python-io \ No newline at end of file diff --git a/event_sources.json b/event_sources.json deleted file mode 100644 index 0967ef4..0000000 --- a/event_sources.json +++ /dev/null @@ -1 +0,0 @@ -{} diff --git a/index.js b/index.js deleted file mode 100644 index 0d11a7b..0000000 --- a/index.js +++ /dev/null @@ -1,120 +0,0 @@ -const cache = require('./src/cache/CacheFactory.js') -const logger = require('./src/helpers/Logger.js') -const TransformerError = require('./src/helpers/ErrorHelper.js') -const { schemaHandler, fetchSchema } = require('./src/helpers/SchemaHandler.js') -const { processRecords } = require('./src/helpers/RecordsProcessor.js') - -const recordsHandler = async function (records, opts, context, callback) { - try { - const schemaResponse = await schemaHandler(cache.getSchema(opts.schemaName), fetchSchema(opts.nyplDataApiBaseUrl, opts.schemaPath, opts.schemaName)) - - if (schemaResponse.schemaType === 'fresh-schema') { - cache.setSchema(opts.schemaName, schemaResponse.schema) - } - - if (cache.getSchema(opts.schemaName)) { - const output = processRecords(cache.getSchema(opts.schemaName), records, opts.outputFormat) - return callback(null, { records: output }) - } - } catch (e) { - logger.error('Schema not properly cached') - return callback(e, null) - } -} - -const configHandler = (records, opts, context, callback) => { - try { - if (!opts || Object.keys(opts).length === 0) { - throw new TransformerError( - 'missing/undefined opts object configuration parameter', - { type: 'function-parameter-error' } - ) - } - if (!opts.nyplDataApiBaseUrl || typeof opts.nyplDataApiBaseUrl !== 'string' || opts.nyplDataApiBaseUrl === '') { - throw new TransformerError( - 'missing/undefined nyplDataApiBaseUrl config parameter', - { type: 'function-parameter-error' } - ) - } - if (!opts.schemaPath || typeof opts.schemaPath !== 'string' || opts.schemaPath === '') { - throw new TransformerError( - 'missing/undefined schemaPath config parameter', - { type: 'function-parameter-error' } - ) - } - if (!opts.schemaName || typeof opts.schemaName !== 'string' || opts.schemaName === '') { - throw new TransformerError( - 'missing/undefined schemaName config parameter', - { type: 'function-parameter-error' } - ) - } - if (!opts.outputFormat || typeof opts.outputFormat !== 'string' || (opts.outputFormat !== 'json' && opts.outputFormat !== 'csv')) { - throw new TransformerError( - 'missing/unsupported outputFormat config parameter', - { type: 'function-parameter-error' } - ) - } - return recordsHandler(records, opts, context, callback) - } catch (e) { - logger.error(`transformer-configuration-error: ${e.message}`) - return callback(e, null) - } -} - -/** - * Given a firehose event, returns the schema name records are encoded using - * based on stream ARN - * - * For example, given an event with: - * "deliverySteamArn": "arn:aws:kinesis:PcReserve-production" - * .. Returns: - * "PcReserve" - */ -const schemaNameFromEvent = (event) => { - // Incoming sourceKinesisStreamArn look like: - // "arn:aws:kinesis:us-east-1:946183545209:stream/PcReserve-production" - return event.sourceKinesisStreamArn.split(':') - .pop() - .replace(/^stream\//, '') - .replace(/-[a-z]+$/, '') - // Against convention, the "CircTransAnon" stream contains "CircTrans" - // encoded records, so ensure that schema name is chosen: - .replace(/^CircTransAnon$/, 'CircTrans') -} - -const handler = (event, context, callback) => { - if (event && Array.isArray(event.records) && event.records.length > 0) { - const record = event.records[0] - - const schemaName = schemaNameFromEvent(event) - let outputFormat = 'json' - if (schemaName === 'LocationHours') { - outputFormat = 'csv' - } - if (record.data) { - return configHandler( - event.records, - { - nyplDataApiBaseUrl: process.env.NYPL_DATA_API_BASE_URL, - schemaPath: process.env.SCHEMA_PATH, - schemaName, - outputFormat - }, - context, - callback - ) - } - logger.error('event.records array is empty') - return callback(new Error('event.records array is empty')) - } - - logger.error('event.records is undefined') - return callback(new Error('event is undefined')) -} - -module.exports = { - schemaNameFromEvent, - recordsHandler, - configHandler, - handler -} diff --git a/lambda_function.py b/lambda_function.py new file mode 100755 index 0000000..7eb5ddd --- /dev/null +++ b/lambda_function.py @@ -0,0 +1,78 @@ +import os +import re + +from nypl_py_utils.functions.config_helper import load_env_file +from nypl_py_utils.functions.log_helper import create_log +from record_processor import RecordProcessor + + +def lambda_handler(event, context): + logger = create_log("lambda_function") + if os.environ["ENVIRONMENT"] == "devel": + load_env_file(os.environ["ENVIRONMENT"], "config/{}.yaml") + + logger.info("Starting event processing...") + + if event is None: + logger.error("Event is undefined.") + raise RecordParsingError("No event found.") + else: + # All records under one event will have the same schema + schema_name = _pull_schema_name( + event["sourceKinesisStreamArn"]) + schema_url = ( + os.environ["NYPL_DATA_API_BASE_URL"] + f"{schema_name}" + ) + output_format = "json" if schema_name != "LocationHours" else "csv" + + processor = RecordProcessor(schema_url) + successes, failures = 0, 0 + processed_records = [] + + try: + # In case of key capitalization + lowercase_event = {k.lower(): v for k, v in event.items()} + + for record in lowercase_event["records"]: + if "data" in record: + result = processor.process_record(record, output_format) + if "ProcessingFailed" in result["result"]: + logger.error( + f"Error processing record data: {result}") + failures += 1 + else: + successes += 1 + processed_records.append(result) + except Exception as e: + # Catch any errors in the case the event has no records, etc + raise RecordParsingError(e) + + logger.info( + f"Processing complete. Successful transformations - {successes}. Failed transformations - {failures}." + ) + + logger.info("Finished lambda processing.") + return {"records": processed_records} + + +def _pull_schema_name(stream_arn): + """ + Given a Firehose event's stream ARN, pulls encoded schema type. + Example input -- "arn:aws:kinesis:us-east-1:946183545209:stream/PcReserve-production" + Example output -- "PcReserve" + """ + filtered_for_stream_name = stream_arn.split(":").pop() + replacements = [ + ("^stream/", ""), + ("-[a-z]+$", "") + ] + + for old, new in replacements: + filtered_for_stream_name = re.sub( + old, new, filtered_for_stream_name) + return filtered_for_stream_name + + +class RecordParsingError(Exception): + def __init__(self, message=None): + self.message = message diff --git a/package-lock.json b/package-lock.json deleted file mode 100644 index 5fc684a..0000000 --- a/package-lock.json +++ /dev/null @@ -1,3735 +0,0 @@ -{ - "name": "firehose-avro-to-json-transformer", - "version": "1.0.0", - "lockfileVersion": 1, - "requires": true, - "dependencies": { - "@babel/code-frame": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.16.0.tgz", - "integrity": "sha1-DfyAMJvuyEEeZecGRhxAiwu5tDE=", - "dev": true, - "requires": { - "@babel/highlight": "^7.16.0" - } - }, - "@babel/helper-validator-identifier": { - "version": "7.15.7", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.15.7.tgz", - "integrity": "sha1-Ig35k7/pBKSmsCq08zhaXr9uI4k=", - "dev": true - }, - "@babel/highlight": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.16.0.tgz", - "integrity": "sha1-bOsysspLj182H7f9gh4/3fShclo=", - "dev": true, - "requires": { - "@babel/helper-validator-identifier": "^7.15.7", - "chalk": "^2.0.0", - "js-tokens": "^4.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha1-QfuyAkPlCxK+DwS43tvwdSDOhB0=", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha1-zUJUFnelQzPPVBpJEIwUMrRMlCQ=", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha1-GSA/tZmR35jjoocFDUZHzerzJJk=", - "dev": true - }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha1-4uaaRKyHcveKHsCzW2id9lMO/I8=", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "@bcoe/v8-coverage": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", - "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", - "dev": true - }, - "@eslint/eslintrc": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.3.0.tgz", - "integrity": "sha512-1JTKgrOKAHVivSvOYw+sJOunkBjUOvjqWk1DPja7ZFhIS2mX/4EgTT8M7eTK9jrKhL/FvXXEbQwIs3pg1xp3dg==", - "dev": true, - "requires": { - "ajv": "^6.12.4", - "debug": "^4.1.1", - "espree": "^7.3.0", - "globals": "^12.1.0", - "ignore": "^4.0.6", - "import-fresh": "^3.2.1", - "js-yaml": "^3.13.1", - "lodash": "^4.17.20", - "minimatch": "^3.0.4", - "strip-json-comments": "^3.1.1" - }, - "dependencies": { - "debug": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz", - "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==", - "dev": true, - "requires": { - "ms": "2.1.2" - } - }, - "globals": { - "version": "12.4.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-12.4.0.tgz", - "integrity": "sha512-BWICuzzDvDoH54NHKCseDanAhE3CeDorgDL5MT6LMXXj2WCnd9UC2szdk4AWLfjdgNBCXLUanXYcpBBKOSWGwg==", - "dev": true, - "requires": { - "type-fest": "^0.8.1" - } - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - } - } - }, - "@istanbuljs/schema": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", - "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", - "dev": true - }, - "@sinonjs/commons": { - "version": "1.8.3", - "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.3.tgz", - "integrity": "sha1-OALd0hpQqUm2ch3dcto25n5/Gy0=", - "dev": true, - "requires": { - "type-detect": "4.0.8" - } - }, - "@sinonjs/formatio": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/@sinonjs/formatio/-/formatio-3.2.2.tgz", - "integrity": "sha1-dxxg36dep/LWjjuUx+iIp4eBNyw=", - "dev": true, - "requires": { - "@sinonjs/commons": "^1", - "@sinonjs/samsam": "^3.1.0" - } - }, - "@sinonjs/samsam": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/@sinonjs/samsam/-/samsam-3.3.3.tgz", - "integrity": "sha1-Rmgu/Zlnslm4ETa58SD9VFhf60o=", - "dev": true, - "requires": { - "@sinonjs/commons": "^1.3.0", - "array-from": "^2.1.1", - "lodash": "^4.17.15" - } - }, - "@sinonjs/text-encoding": { - "version": "0.7.1", - "resolved": "https://registry.npmjs.org/@sinonjs/text-encoding/-/text-encoding-0.7.1.tgz", - "integrity": "sha1-jaXGUwkVZT86Hzj9XxAdjD+AecU=", - "dev": true - }, - "@types/istanbul-lib-coverage": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.3.tgz", - "integrity": "sha512-sz7iLqvVUg1gIedBOvlkxPlc8/uVzyS5OwGz1cKjXzkl3FpL3al0crU8YGU1WoHkxn0Wxbw5tyi6hvzJKNzFsw==", - "dev": true - }, - "@types/json5": { - "version": "0.0.29", - "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", - "integrity": "sha1-7ihweulOEdK4J7y+UnC86n8+ce4=", - "dev": true - }, - "@ungap/promise-all-settled": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@ungap/promise-all-settled/-/promise-all-settled-1.1.2.tgz", - "integrity": "sha512-sL/cEvJWAnClXw0wHk85/2L0G6Sj8UB0Ctc1TEMbKSsmpRosqhwj9gWgFRZSrBr2f9tiXISwNhCPmlfqUqyb9Q==", - "dev": true - }, - "acorn": { - "version": "7.4.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", - "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", - "dev": true - }, - "acorn-jsx": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", - "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", - "dev": true - }, - "ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha1-uvWmLoArB9l3A0WG+MO69a3ybfQ=", - "dev": true, - "requires": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - } - }, - "ansi-colors": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", - "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", - "dev": true - }, - "argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha1-vNZ5HqWuCXJeF+WtmIE0zUCz2RE=", - "dev": true, - "requires": { - "sprintf-js": "~1.0.2" - } - }, - "array-from": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/array-from/-/array-from-2.1.1.tgz", - "integrity": "sha1-z+nYwmYoudxa7MYqn12PHzUsEZU=", - "dev": true - }, - "array-includes": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.4.tgz", - "integrity": "sha512-ZTNSQkmWumEbiHO2GF4GmWxYVTiQyJy2XOTa15sdQSrvKn7l+180egQMqlrMOUMCyLMD7pmyQe4mMDUT6Behrw==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3", - "es-abstract": "^1.19.1", - "get-intrinsic": "^1.1.1", - "is-string": "^1.0.7" - } - }, - "array.prototype.flat": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.2.5.tgz", - "integrity": "sha512-KaYU+S+ndVqyUnignHftkwc58o3uVU1jzczILJ1tN2YaIZpFIKBiP/x/j97E5MVPsaCloPbqWLB/8qCTVvT2qg==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3", - "es-abstract": "^1.19.0" - } - }, - "array.prototype.flatmap": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.2.5.tgz", - "integrity": "sha512-08u6rVyi1Lj7oqWbS9nUxliETrtIROT4XGTA4D/LWGten6E3ocm7cy9SIrmNHOL5XVbVuckUp3X6Xyg8/zpvHA==", - "dev": true, - "requires": { - "call-bind": "^1.0.0", - "define-properties": "^1.1.3", - "es-abstract": "^1.19.0" - } - }, - "asn1": { - "version": "0.2.6", - "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz", - "integrity": "sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==", - "dev": true, - "requires": { - "safer-buffer": "~2.1.0" - } - }, - "assert-plus": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", - "dev": true - }, - "assertion-error": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", - "integrity": "sha1-5gtrDo8wG9l+U3UhW9pAbIURjAs=", - "dev": true - }, - "astral-regex": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", - "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", - "dev": true - }, - "asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", - "dev": true - }, - "avsc": { - "version": "5.0.7", - "resolved": "https://registry.npmjs.org/avsc/-/avsc-5.0.7.tgz", - "integrity": "sha1-zVEL6R/qnc30I/vpG1wXeWR9w+8=" - }, - "aws-sign2": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", - "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=", - "dev": true - }, - "aws4": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.11.0.tgz", - "integrity": "sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA==", - "dev": true - }, - "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" - } - }, - "axios-mock-adapter": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/axios-mock-adapter/-/axios-mock-adapter-1.9.0.tgz", - "integrity": "sha1-Djh2Zvzq8/kSYhCaOVb1iLxq8tE=", - "dev": true, - "requires": { - "deep-equal": "^1.0.1" - } - }, - "balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha1-6D46fj8wCzTLnYf2FfoMvzV2kO4=", - "dev": true - }, - "bcrypt-pbkdf": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", - "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=", - "dev": true, - "requires": { - "tweetnacl": "^0.14.3" - } - }, - "brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha1-PH/L9SnYcibz0vUrlm/1Jx60Qd0=", - "dev": true, - "requires": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "browser-stdout": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", - "integrity": "sha1-uqVZ7hTO1zRSIputcyZGfGH6vWA=", - "dev": true - }, - "c8": { - "version": "7.10.0", - "resolved": "https://registry.npmjs.org/c8/-/c8-7.10.0.tgz", - "integrity": "sha512-OAwfC5+emvA6R7pkYFVBTOtI5ruf9DahffGmIqUc9l6wEh0h7iAFP6dt/V9Ioqlr2zW5avX9U9/w1I4alTRHkA==", - "dev": true, - "requires": { - "@bcoe/v8-coverage": "^0.2.3", - "@istanbuljs/schema": "^0.1.2", - "find-up": "^5.0.0", - "foreground-child": "^2.0.0", - "istanbul-lib-coverage": "^3.0.1", - "istanbul-lib-report": "^3.0.0", - "istanbul-reports": "^3.0.2", - "rimraf": "^3.0.0", - "test-exclude": "^6.0.0", - "v8-to-istanbul": "^8.0.0", - "yargs": "^16.2.0", - "yargs-parser": "^20.2.7" - }, - "dependencies": { - "find-up": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", - "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", - "dev": true, - "requires": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" - } - }, - "locate-path": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", - "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", - "dev": true, - "requires": { - "p-locate": "^5.0.0" - } - }, - "p-limit": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", - "dev": true, - "requires": { - "yocto-queue": "^0.1.0" - } - }, - "p-locate": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", - "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", - "dev": true, - "requires": { - "p-limit": "^3.0.2" - } - }, - "path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true - }, - "rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "dev": true, - "requires": { - "glob": "^7.1.3" - } - }, - "yargs-parser": { - "version": "20.2.9", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", - "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", - "dev": true - } - } - }, - "call-bind": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", - "integrity": "sha1-sdTonmiBGcPJqQOtMKuy9qkZvjw=", - "dev": true, - "requires": { - "function-bind": "^1.1.1", - "get-intrinsic": "^1.0.2" - } - }, - "callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "dev": true - }, - "camelcase": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.2.1.tgz", - "integrity": "sha512-tVI4q5jjFV5CavAU8DXfza/TJcZutVKo/5Foskmsqcm0MsL91moHvwiGNnqaa2o6PF/7yT5ikDRcVcl8Rj6LCA==", - "dev": true - }, - "caseless": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", - "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=", - "dev": true - }, - "chai": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/chai/-/chai-4.3.4.tgz", - "integrity": "sha512-yS5H68VYOCtN1cjfwumDSuzn/9c+yza4f3reKXlE5rUg7SFcCEy90gJvydNgOYtblyf4Zi6jIWRnXOgErta0KA==", - "dev": true, - "requires": { - "assertion-error": "^1.1.0", - "check-error": "^1.0.2", - "deep-eql": "^3.0.1", - "get-func-name": "^2.0.0", - "pathval": "^1.1.1", - "type-detect": "^4.0.5" - } - }, - "chai-as-promised": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/chai-as-promised/-/chai-as-promised-7.1.1.tgz", - "integrity": "sha1-CGRdgl3rhpbuYXJdv1kMAS6wDKA=", - "dev": true, - "requires": { - "check-error": "^1.0.2" - } - }, - "check-error": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz", - "integrity": "sha1-V00xLt2Iu13YkS6Sht1sCu1KrII=", - "dev": true - }, - "cliui": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", - "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", - "dev": true, - "requires": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^7.0.0" - }, - "dependencies": { - "ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true - }, - "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==", - "dev": true - }, - "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==", - "dev": true, - "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==", - "dev": true, - "requires": { - "ansi-regex": "^5.0.1" - } - } - } - }, - "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha1-u3GFBpDh8TZWfeYp0tVHHe2kweg=", - "dev": true, - "requires": { - "color-name": "1.1.3" - } - }, - "color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", - "dev": true - }, - "colors": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/colors/-/colors-1.0.3.tgz", - "integrity": "sha1-BDP0TYCWgP3rYO0mDxsMJi6CpAs=" - }, - "combined-stream": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "dev": true, - "requires": { - "delayed-stream": "~1.0.0" - } - }, - "concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", - "dev": true - }, - "convert-source-map": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.8.0.tgz", - "integrity": "sha512-+OQdjP49zViI/6i7nIJpA8rAl4sV/JdPfU9nZs3VqOwGIgizICvuN2ru6fMd+4llL0tar18UYJXfZ/TWtmhUjA==", - "dev": true, - "requires": { - "safe-buffer": "~5.1.1" - } - }, - "coveralls": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/coveralls/-/coveralls-3.1.1.tgz", - "integrity": "sha512-+dxnG2NHncSD1NrqbSM3dn/lE57O6Qf/koe9+I7c+wzkqRmEvcp0kgJdxKInzYzkICKkFMZsX3Vct3++tsF9ww==", - "dev": true, - "requires": { - "js-yaml": "^3.13.1", - "lcov-parse": "^1.0.0", - "log-driver": "^1.2.7", - "minimist": "^1.2.5", - "request": "^2.88.2" - } - }, - "cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", - "dev": true, - "requires": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - } - }, - "cycle": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/cycle/-/cycle-1.0.3.tgz", - "integrity": "sha1-IegLK+hYD5i0aPN5QwZisEbDStI=" - }, - "dashdash": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", - "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", - "dev": true, - "requires": { - "assert-plus": "^1.0.0" - } - }, - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha1-XRKFFd8TT/Mn6QpMk/Tgd6U2NB8=", - "dev": true, - "requires": { - "ms": "2.0.0" - } - }, - "decamelize": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", - "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", - "dev": true - }, - "deep-eql": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-3.0.1.tgz", - "integrity": "sha1-38lARACtHI/gI+faHfHBR8S0RN8=", - "dev": true, - "requires": { - "type-detect": "^4.0.0" - } - }, - "deep-equal": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.1.1.tgz", - "integrity": "sha1-tcmMlCzv+vfLBR4k4UNKJaLmB2o=", - "dev": true, - "requires": { - "is-arguments": "^1.0.4", - "is-date-object": "^1.0.1", - "is-regex": "^1.0.4", - "object-is": "^1.0.1", - "object-keys": "^1.1.1", - "regexp.prototype.flags": "^1.2.0" - } - }, - "deep-is": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", - "integrity": "sha1-pvLc5hL63S7x9Rm3NVHxfoUZmDE=", - "dev": true - }, - "define-properties": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", - "integrity": "sha1-z4jabL7ib+bbcJT2HYcMvYTO6fE=", - "dev": true, - "requires": { - "object-keys": "^1.0.12" - } - }, - "delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", - "dev": true - }, - "diff": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz", - "integrity": "sha1-gAwN0eCov7yVg1wgKtIg/jF+WhI=", - "dev": true - }, - "doctrine": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", - "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", - "dev": true, - "requires": { - "esutils": "^2.0.2" - } - }, - "ecc-jsbn": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", - "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=", - "dev": true, - "requires": { - "jsbn": "~0.1.0", - "safer-buffer": "^2.1.0" - } - }, - "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==", - "dev": true - }, - "enquirer": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz", - "integrity": "sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==", - "dev": true, - "requires": { - "ansi-colors": "^4.1.1" - } - }, - "error-ex": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", - "integrity": "sha1-tKxAZIEH/c3PriQvQovqihTU8b8=", - "dev": true, - "requires": { - "is-arrayish": "^0.2.1" - } - }, - "es-abstract": { - "version": "1.19.1", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.19.1.tgz", - "integrity": "sha1-1IhXlodpFpWd547aoN9FZicRXsM=", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "es-to-primitive": "^1.2.1", - "function-bind": "^1.1.1", - "get-intrinsic": "^1.1.1", - "get-symbol-description": "^1.0.0", - "has": "^1.0.3", - "has-symbols": "^1.0.2", - "internal-slot": "^1.0.3", - "is-callable": "^1.2.4", - "is-negative-zero": "^2.0.1", - "is-regex": "^1.1.4", - "is-shared-array-buffer": "^1.0.1", - "is-string": "^1.0.7", - "is-weakref": "^1.0.1", - "object-inspect": "^1.11.0", - "object-keys": "^1.1.1", - "object.assign": "^4.1.2", - "string.prototype.trimend": "^1.0.4", - "string.prototype.trimstart": "^1.0.4", - "unbox-primitive": "^1.0.1" - } - }, - "es-to-primitive": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", - "integrity": "sha1-5VzUyc3BiLzvsDs2bHNjI/xciYo=", - "dev": true, - "requires": { - "is-callable": "^1.1.4", - "is-date-object": "^1.0.1", - "is-symbol": "^1.0.2" - } - }, - "escalade": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", - "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", - "dev": true - }, - "escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", - "dev": true - }, - "eslint": { - "version": "7.18.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.18.0.tgz", - "integrity": "sha512-fbgTiE8BfUJZuBeq2Yi7J3RB3WGUQ9PNuNbmgi6jt9Iv8qrkxfy19Ds3OpL1Pm7zg3BtTVhvcUZbIRQ0wmSjAQ==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.0.0", - "@eslint/eslintrc": "^0.3.0", - "ajv": "^6.10.0", - "chalk": "^4.0.0", - "cross-spawn": "^7.0.2", - "debug": "^4.0.1", - "doctrine": "^3.0.0", - "enquirer": "^2.3.5", - "eslint-scope": "^5.1.1", - "eslint-utils": "^2.1.0", - "eslint-visitor-keys": "^2.0.0", - "espree": "^7.3.1", - "esquery": "^1.2.0", - "esutils": "^2.0.2", - "file-entry-cache": "^6.0.0", - "functional-red-black-tree": "^1.0.1", - "glob-parent": "^5.0.0", - "globals": "^12.1.0", - "ignore": "^4.0.6", - "import-fresh": "^3.0.0", - "imurmurhash": "^0.1.4", - "is-glob": "^4.0.0", - "js-yaml": "^3.13.1", - "json-stable-stringify-without-jsonify": "^1.0.1", - "levn": "^0.4.1", - "lodash": "^4.17.20", - "minimatch": "^3.0.4", - "natural-compare": "^1.4.0", - "optionator": "^0.9.1", - "progress": "^2.0.0", - "regexpp": "^3.1.0", - "semver": "^7.2.1", - "strip-ansi": "^6.0.0", - "strip-json-comments": "^3.1.0", - "table": "^6.0.4", - "text-table": "^0.2.0", - "v8-compile-cache": "^2.0.3" - }, - "dependencies": { - "ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true - }, - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.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==", - "dev": true, - "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==", - "dev": true - }, - "debug": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz", - "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==", - "dev": true, - "requires": { - "ms": "2.1.2" - } - }, - "glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "requires": { - "is-glob": "^4.0.1" - } - }, - "globals": { - "version": "12.4.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-12.4.0.tgz", - "integrity": "sha512-BWICuzzDvDoH54NHKCseDanAhE3CeDorgDL5MT6LMXXj2WCnd9UC2szdk4AWLfjdgNBCXLUanXYcpBBKOSWGwg==", - "dev": true, - "requires": { - "type-fest": "^0.8.1" - } - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", - "dev": true - }, - "is-glob": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "dev": true, - "requires": { - "is-extglob": "^2.1.1" - } - }, - "levn": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", - "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", - "dev": true, - "requires": { - "prelude-ls": "^1.2.1", - "type-check": "~0.4.0" - } - }, - "lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, - "requires": { - "yallist": "^4.0.0" - } - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, - "optionator": { - "version": "0.9.1", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", - "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", - "dev": true, - "requires": { - "deep-is": "^0.1.3", - "fast-levenshtein": "^2.0.6", - "levn": "^0.4.1", - "prelude-ls": "^1.2.1", - "type-check": "^0.4.0", - "word-wrap": "^1.2.3" - } - }, - "prelude-ls": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", - "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", - "dev": true - }, - "semver": { - "version": "7.3.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", - "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", - "dev": true, - "requires": { - "lru-cache": "^6.0.0" - } - }, - "strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "requires": { - "ansi-regex": "^5.0.1" - } - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - }, - "type-check": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", - "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", - "dev": true, - "requires": { - "prelude-ls": "^1.2.1" - } - }, - "yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - } - } - }, - "eslint-config-standard": { - "version": "16.0.3", - "resolved": "https://registry.npmjs.org/eslint-config-standard/-/eslint-config-standard-16.0.3.tgz", - "integrity": "sha512-x4fmJL5hGqNJKGHSjnLdgA6U6h1YW/G2dW9fA+cyVur4SK6lyue8+UgNKWlZtUDTXvgKDD/Oa3GQjmB5kjtVvg==", - "dev": true - }, - "eslint-config-standard-jsx": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/eslint-config-standard-jsx/-/eslint-config-standard-jsx-10.0.0.tgz", - "integrity": "sha512-hLeA2f5e06W1xyr/93/QJulN/rLbUVUmqTlexv9PRKHFwEC9ffJcH2LvJhMoEqYQBEYafedgGZXH2W8NUpt5lA==", - "dev": true - }, - "eslint-import-resolver-node": { - "version": "0.3.6", - "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.6.tgz", - "integrity": "sha512-0En0w03NRVMn9Uiyn8YRPDKvWjxCWkslUEhGNTdGx15RvPJYQ+lbOlqrlNI2vEAs4pDYK4f/HN2TbDmk5TP0iw==", - "dev": true, - "requires": { - "debug": "^3.2.7", - "resolve": "^1.20.0" - }, - "dependencies": { - "debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - }, - "ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true - } - } - }, - "eslint-module-utils": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.7.1.tgz", - "integrity": "sha1-tDUAHJ+N1Kt/bQ78rkuWltTCS3w=", - "dev": true, - "requires": { - "debug": "^3.2.7", - "find-up": "^2.1.0", - "pkg-dir": "^2.0.0" - }, - "dependencies": { - "debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha1-clgLfpFF+zm2Z2+cXl+xALk0F5o=", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - }, - "find-up": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", - "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", - "dev": true, - "requires": { - "locate-path": "^2.0.0" - } - }, - "locate-path": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", - "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", - "dev": true, - "requires": { - "p-locate": "^2.0.0", - "path-exists": "^3.0.0" - } - }, - "ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha1-V0yBOM4dK1hh8LRFedut1gxmFbI=", - "dev": true - }, - "p-limit": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", - "integrity": "sha1-uGvV8MJWkJEcdZD8v8IBDVSzzLg=", - "dev": true, - "requires": { - "p-try": "^1.0.0" - } - }, - "p-locate": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", - "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", - "dev": true, - "requires": { - "p-limit": "^1.1.0" - } - }, - "p-try": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", - "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=", - "dev": true - } - } - }, - "eslint-plugin-es": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-es/-/eslint-plugin-es-3.0.1.tgz", - "integrity": "sha512-GUmAsJaN4Fc7Gbtl8uOBlayo2DqhwWvEzykMHSCZHU3XdJ+NSzzZcVhXh3VxX5icqQ+oQdIEawXX8xkR3mIFmQ==", - "dev": true, - "requires": { - "eslint-utils": "^2.0.0", - "regexpp": "^3.0.0" - } - }, - "eslint-plugin-import": { - "version": "2.24.2", - "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.24.2.tgz", - "integrity": "sha512-hNVtyhiEtZmpsabL4neEj+6M5DCLgpYyG9nzJY8lZQeQXEn5UPW1DpUdsMHMXsq98dbNm7nt1w9ZMSVpfJdi8Q==", - "dev": true, - "requires": { - "array-includes": "^3.1.3", - "array.prototype.flat": "^1.2.4", - "debug": "^2.6.9", - "doctrine": "^2.1.0", - "eslint-import-resolver-node": "^0.3.6", - "eslint-module-utils": "^2.6.2", - "find-up": "^2.0.0", - "has": "^1.0.3", - "is-core-module": "^2.6.0", - "minimatch": "^3.0.4", - "object.values": "^1.1.4", - "pkg-up": "^2.0.0", - "read-pkg-up": "^3.0.0", - "resolve": "^1.20.0", - "tsconfig-paths": "^3.11.0" - }, - "dependencies": { - "doctrine": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", - "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", - "dev": true, - "requires": { - "esutils": "^2.0.2" - } - }, - "find-up": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", - "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", - "dev": true, - "requires": { - "locate-path": "^2.0.0" - } - }, - "locate-path": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", - "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", - "dev": true, - "requires": { - "p-locate": "^2.0.0", - "path-exists": "^3.0.0" - } - }, - "p-limit": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", - "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", - "dev": true, - "requires": { - "p-try": "^1.0.0" - } - }, - "p-locate": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", - "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", - "dev": true, - "requires": { - "p-limit": "^1.1.0" - } - }, - "p-try": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", - "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=", - "dev": true - }, - "read-pkg-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-3.0.0.tgz", - "integrity": "sha1-PtSWaF26D4/hGNBpHcUfSh/5bwc=", - "dev": true, - "requires": { - "find-up": "^2.0.0", - "read-pkg": "^3.0.0" - } - } - } - }, - "eslint-plugin-node": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-node/-/eslint-plugin-node-11.1.0.tgz", - "integrity": "sha512-oUwtPJ1W0SKD0Tr+wqu92c5xuCeQqB3hSCHasn/ZgjFdA9iDGNkNf2Zi9ztY7X+hNuMib23LNGRm6+uN+KLE3g==", - "dev": true, - "requires": { - "eslint-plugin-es": "^3.0.0", - "eslint-utils": "^2.0.0", - "ignore": "^5.1.1", - "minimatch": "^3.0.4", - "resolve": "^1.10.1", - "semver": "^6.1.0" - }, - "dependencies": { - "ignore": { - "version": "5.1.9", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.9.tgz", - "integrity": "sha512-2zeMQpbKz5dhZ9IwL0gbxSW5w0NK/MSAMtNuhgIHEPmaU3vPdKPL0UdvUCXs5SS4JAwsBxysK5sFMW8ocFiVjQ==", - "dev": true - } - } - }, - "eslint-plugin-promise": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-promise/-/eslint-plugin-promise-5.1.1.tgz", - "integrity": "sha512-XgdcdyNzHfmlQyweOPTxmc7pIsS6dE4MvwhXWMQ2Dxs1XAL2GJDilUsjWen6TWik0aSI+zD/PqocZBblcm9rdA==", - "dev": true - }, - "eslint-plugin-react": { - "version": "7.25.3", - "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.25.3.tgz", - "integrity": "sha512-ZMbFvZ1WAYSZKY662MBVEWR45VaBT6KSJCiupjrNlcdakB90juaZeDCbJq19e73JZQubqFtgETohwgAt8u5P6w==", - "dev": true, - "requires": { - "array-includes": "^3.1.3", - "array.prototype.flatmap": "^1.2.4", - "doctrine": "^2.1.0", - "estraverse": "^5.2.0", - "jsx-ast-utils": "^2.4.1 || ^3.0.0", - "minimatch": "^3.0.4", - "object.entries": "^1.1.4", - "object.fromentries": "^2.0.4", - "object.hasown": "^1.0.0", - "object.values": "^1.1.4", - "prop-types": "^15.7.2", - "resolve": "^2.0.0-next.3", - "string.prototype.matchall": "^4.0.5" - }, - "dependencies": { - "doctrine": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", - "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", - "dev": true, - "requires": { - "esutils": "^2.0.2" - } - }, - "estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true - }, - "resolve": { - "version": "2.0.0-next.3", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.3.tgz", - "integrity": "sha512-W8LucSynKUIDu9ylraa7ueVZ7hc0uAgJBxVsQSKOXOyle8a93qXhcz+XAXZ8bIq2d6i4Ehddn6Evt+0/UwKk6Q==", - "dev": true, - "requires": { - "is-core-module": "^2.2.0", - "path-parse": "^1.0.6" - } - } - } - }, - "eslint-scope": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", - "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", - "dev": true, - "requires": { - "esrecurse": "^4.3.0", - "estraverse": "^4.1.1" - } - }, - "eslint-utils": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz", - "integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==", - "dev": true, - "requires": { - "eslint-visitor-keys": "^1.1.0" - }, - "dependencies": { - "eslint-visitor-keys": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", - "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", - "dev": true - } - } - }, - "eslint-visitor-keys": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", - "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", - "dev": true - }, - "espree": { - "version": "7.3.1", - "resolved": "https://registry.npmjs.org/espree/-/espree-7.3.1.tgz", - "integrity": "sha512-v3JCNCE64umkFpmkFGqzVKsOT0tN1Zr+ueqLZfpV1Ob8e+CEgPWa+OxCoGH3tnhimMKIaBm4m/vaRpJ/krRz2g==", - "dev": true, - "requires": { - "acorn": "^7.4.0", - "acorn-jsx": "^5.3.1", - "eslint-visitor-keys": "^1.3.0" - }, - "dependencies": { - "eslint-visitor-keys": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", - "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", - "dev": true - } - } - }, - "esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha1-E7BM2z5sXRnfkatph6hpVhmwqnE=", - "dev": true - }, - "esquery": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz", - "integrity": "sha1-IUj/w4uC6McFff7UhCWz5h8PJKU=", - "dev": true, - "requires": { - "estraverse": "^5.1.0" - }, - "dependencies": { - "estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha1-LupSkHAvJquP5TcDcP+GyWXSESM=", - "dev": true - } - } - }, - "esrecurse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", - "integrity": "sha1-eteWTWeauyi+5yzsY3WLHF0smSE=", - "dev": true, - "requires": { - "estraverse": "^5.2.0" - }, - "dependencies": { - "estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha1-LupSkHAvJquP5TcDcP+GyWXSESM=", - "dev": true - } - } - }, - "estraverse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", - "integrity": "sha1-OYrT88WiSUi+dyXoPRGn3ijNvR0=", - "dev": true - }, - "esutils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha1-dNLrTeC42hKTcRkQ1Qd1ubcQ72Q=", - "dev": true - }, - "extend": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", - "integrity": "sha1-+LETa0Bx+9jrFAr/hYsQGewpFfo=", - "dev": true - }, - "extsprintf": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", - "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=", - "dev": true - }, - "eyes": { - "version": "0.1.8", - "resolved": "https://registry.npmjs.org/eyes/-/eyes-0.1.8.tgz", - "integrity": "sha1-Ys8SAjTGg3hdkCNIqADvPgzCC8A=" - }, - "fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha1-On1WtVnWy8PrUSMlJE5hmmXGxSU=", - "dev": true - }, - "fast-json-stable-stringify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha1-h0v2nG9ATCtdmcSBNBOZ/VWJJjM=", - "dev": true - }, - "fast-levenshtein": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", - "dev": true - }, - "file-entry-cache": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", - "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", - "dev": true, - "requires": { - "flat-cache": "^3.0.4" - } - }, - "find-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", - "integrity": "sha1-SRafHXmTQwZG2mHsxa41XCHJe3M=", - "dev": true, - "requires": { - "locate-path": "^3.0.0" - } - }, - "flat": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", - "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", - "dev": true - }, - "flat-cache": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", - "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", - "dev": true, - "requires": { - "flatted": "^3.1.0", - "rimraf": "^3.0.2" - }, - "dependencies": { - "rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "dev": true, - "requires": { - "glob": "^7.1.3" - } - } - } - }, - "flatted": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.4.tgz", - "integrity": "sha512-8/sOawo8tJ4QOBX8YlQBMxL8+RLZfxMQOif9o0KUKTNTjMYElWPE0r/m5VNFxTRd0NSw8qSy8dajrwX4RYI1Hw==", - "dev": true - }, - "follow-redirects": { - "version": "1.14.6", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.6.tgz", - "integrity": "sha1-jPsoG7wDWzwGfWzZdbD2reboVc0=" - }, - "foreground-child": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-2.0.0.tgz", - "integrity": "sha512-dCIq9FpEcyQyXKCkyzmlPTFNgrCzPudOe+mhvJU5zAtlBnGVy2yKxtfsxK2tQBThwq225jcvBjpw1Gr40uzZCA==", - "dev": true, - "requires": { - "cross-spawn": "^7.0.0", - "signal-exit": "^3.0.2" - } - }, - "forever-agent": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", - "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=", - "dev": true - }, - "form-data": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", - "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", - "dev": true, - "requires": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.6", - "mime-types": "^2.1.12" - } - }, - "formatio": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/formatio/-/formatio-1.2.0.tgz", - "integrity": "sha1-87IWfZBoxGmKjVH092CjmlTYGOs=", - "dev": true, - "requires": { - "samsam": "1.x" - } - }, - "fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", - "dev": true - }, - "function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha1-pWiZ0+o8m6uHS7l3O3xe3pL0iV0=", - "dev": true - }, - "functional-red-black-tree": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", - "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", - "dev": true - }, - "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==", - "dev": true - }, - "get-func-name": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz", - "integrity": "sha1-6td0q+5y4gQJQzoGY2YCPdaIekE=", - "dev": true - }, - "get-intrinsic": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz", - "integrity": "sha1-FfWfN2+FXERpY5SPDSTNNje0q8Y=", - "dev": true, - "requires": { - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-symbols": "^1.0.1" - } - }, - "get-stdin": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-8.0.0.tgz", - "integrity": "sha512-sY22aA6xchAzprjyqmSEQv4UbAAzRN0L2dQB0NlN5acTTK9Don6nhoc3eAbUnpZiCANAMfd/+40kVdKfFygohg==", - "dev": true - }, - "get-symbol-description": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz", - "integrity": "sha1-f9uByQAQH71WTdXxowr1qtweWNY=", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.1.1" - } - }, - "getpass": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", - "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", - "dev": true, - "requires": { - "assert-plus": "^1.0.0" - } - }, - "glob": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", - "integrity": "sha1-0VU1r3cy4C6Uj0xBYovZECk/YCM=", - "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "graceful-fs": { - "version": "4.2.8", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.8.tgz", - "integrity": "sha1-5BK40z9eAGWTy9PO5t+fLOu+gCo=", - "dev": true - }, - "growl": { - "version": "1.10.5", - "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz", - "integrity": "sha1-8nNdwig2dPpnR4sQGBBZNVw2nl4=", - "dev": true - }, - "har-schema": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", - "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=", - "dev": true - }, - "har-validator": { - "version": "5.1.5", - "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz", - "integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==", - "dev": true, - "requires": { - "ajv": "^6.12.3", - "har-schema": "^2.0.0" - } - }, - "has": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", - "integrity": "sha1-ci18v8H2qoJB8W3YFOAR4fQeh5Y=", - "dev": true, - "requires": { - "function-bind": "^1.1.1" - } - }, - "has-bigints": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.1.tgz", - "integrity": "sha1-ZP5qywIGc+O3jbA1pa9pqp0HsRM=", - "dev": true - }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", - "dev": true - }, - "has-symbols": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.2.tgz", - "integrity": "sha1-Fl0wcMADCXUqEjakeTMeOsVvFCM=", - "dev": true - }, - "has-tostringtag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", - "integrity": "sha1-fhM4GKfTlHNPlB5zw9P5KR5liyU=", - "dev": true, - "requires": { - "has-symbols": "^1.0.2" - } - }, - "he": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", - "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", - "dev": true - }, - "hosted-git-info": { - "version": "2.8.9", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", - "integrity": "sha1-3/wL+aIcAiCQkPKqaUKeFBTa8/k=", - "dev": true - }, - "html-escaper": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", - "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", - "dev": true - }, - "http-signature": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", - "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", - "dev": true, - "requires": { - "assert-plus": "^1.0.0", - "jsprim": "^1.2.2", - "sshpk": "^1.7.0" - } - }, - "ignore": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", - "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", - "dev": true - }, - "import-fresh": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", - "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", - "dev": true, - "requires": { - "parent-module": "^1.0.0", - "resolve-from": "^4.0.0" - } - }, - "imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", - "dev": true - }, - "inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", - "dev": true, - "requires": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha1-D6LGT5MpF8NDOg3tVTY6rjdBa3w=", - "dev": true - }, - "internal-slot": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.3.tgz", - "integrity": "sha1-c0fjB97uovqsKsYgXUvH00ln9Zw=", - "dev": true, - "requires": { - "get-intrinsic": "^1.1.0", - "has": "^1.0.3", - "side-channel": "^1.0.4" - } - }, - "is-arguments": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz", - "integrity": "sha1-FbP4j9oB8ql/7ITKdhpWDxI++ps=", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" - } - }, - "is-arrayish": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", - "dev": true - }, - "is-bigint": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", - "integrity": "sha1-CBR6GHW8KzIAXUHM2Ckd/8ZpHfM=", - "dev": true, - "requires": { - "has-bigints": "^1.0.1" - } - }, - "is-boolean-object": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", - "integrity": "sha1-XG3CACRt2TIa5LiFoRS7H3X2Nxk=", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" - } - }, - "is-callable": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.4.tgz", - "integrity": "sha1-RzAdWN0CWUB4ZVR4U99tYf5HGUU=", - "dev": true - }, - "is-core-module": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.8.0.tgz", - "integrity": "sha1-AyEzbD0JJeSX/Zf12VyxFKXM1Ug=", - "dev": true, - "requires": { - "has": "^1.0.3" - } - }, - "is-date-object": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", - "integrity": "sha1-CEHVU25yTCVZe/bqYuG9OCmN8x8=", - "dev": true, - "requires": { - "has-tostringtag": "^1.0.0" - } - }, - "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==", - "dev": true - }, - "is-negative-zero": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz", - "integrity": "sha1-e/bwOigAO4s5Zd46wm9mTXZfMVA=", - "dev": true - }, - "is-number-object": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.6.tgz", - "integrity": "sha1-anqvg4x/BoalC0VT9+VKlklOifA=", - "dev": true, - "requires": { - "has-tostringtag": "^1.0.0" - } - }, - "is-plain-obj": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", - "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", - "dev": true - }, - "is-regex": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", - "integrity": "sha1-7vVmPNWfpMCuM5UFMj32hUuxWVg=", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" - } - }, - "is-shared-array-buffer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.1.tgz", - "integrity": "sha1-l7DIX72stZycRG/mU7gs8rW3z+Y=", - "dev": true - }, - "is-string": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", - "integrity": "sha1-DdEr8gBvJVu1j2lREO/3SR7rwP0=", - "dev": true, - "requires": { - "has-tostringtag": "^1.0.0" - } - }, - "is-symbol": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", - "integrity": "sha1-ptrJO2NbBjymhyI23oiRClevE5w=", - "dev": true, - "requires": { - "has-symbols": "^1.0.2" - } - }, - "is-typedarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", - "dev": true - }, - "is-unicode-supported": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", - "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", - "dev": true - }, - "is-weakref": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", - "integrity": "sha1-lSnzg6kzggXol2XgOS78LxAPBvI=", - "dev": true, - "requires": { - "call-bind": "^1.0.2" - } - }, - "isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", - "dev": true - }, - "isstream": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", - "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=" - }, - "istanbul-lib-coverage": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz", - "integrity": "sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw==", - "dev": true - }, - "istanbul-lib-report": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", - "integrity": "sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw==", - "dev": true, - "requires": { - "istanbul-lib-coverage": "^3.0.0", - "make-dir": "^3.0.0", - "supports-color": "^7.1.0" - } - }, - "istanbul-reports": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.1.tgz", - "integrity": "sha512-q1kvhAXWSsXfMjCdNHNPKZZv94OlspKnoGv+R9RGbnqOOQ0VbNfLFgQDVgi7hHenKsndGq3/o0OBdzDXthWcNw==", - "dev": true, - "requires": { - "html-escaper": "^2.0.0", - "istanbul-lib-report": "^3.0.0" - } - }, - "js-tokens": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz", - "integrity": "sha1-mGbfOVECEw449/mWvOtlRDIJwls=", - "dev": true - }, - "js-yaml": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", - "integrity": "sha1-2ugS/bOCX6MGYJqHFzg8UMNqBTc=", - "dev": true, - "requires": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - } - }, - "jsbn": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", - "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=", - "dev": true - }, - "json-parse-better-errors": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", - "integrity": "sha1-u4Z8+zRQ5pEHwTHRxRS6s9yLyqk=", - "dev": true - }, - "json-schema": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz", - "integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==", - "dev": true - }, - "json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha1-afaofZUTq4u4/mO9sJecRI5oRmA=", - "dev": true - }, - "json-stable-stringify-without-jsonify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", - "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", - "dev": true - }, - "json-stringify-safe": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", - "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=", - "dev": true - }, - "jsprim": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.2.tgz", - "integrity": "sha512-P2bSOMAc/ciLz6DzgjVlGJP9+BrJWu5UDGK70C2iweC5QBIeFf0ZXRvGjEj2uYgrY2MkAAhsSWHDWlFtEroZWw==", - "dev": true, - "requires": { - "assert-plus": "1.0.0", - "extsprintf": "1.3.0", - "json-schema": "0.4.0", - "verror": "1.10.0" - } - }, - "jsx-ast-utils": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.2.1.tgz", - "integrity": "sha512-uP5vu8xfy2F9A6LGC22KO7e2/vGTS1MhP+18f++ZNlf0Ohaxbc9nIEwHAsejlJKyzfZzU5UIhe5ItYkitcZnZA==", - "dev": true, - "requires": { - "array-includes": "^3.1.3", - "object.assign": "^4.1.2" - } - }, - "just-extend": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/just-extend/-/just-extend-4.2.1.tgz", - "integrity": "sha1-715Ymvth5dZrJOynSUCaiTmox0Q=", - "dev": true - }, - "lcov-parse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/lcov-parse/-/lcov-parse-1.0.0.tgz", - "integrity": "sha1-6w1GtUER68VhrLTECO+TY73I9+A=", - "dev": true - }, - "load-json-file": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", - "integrity": "sha1-L19Fq5HjMhYjT9U62rZo607AmTs=", - "dev": true, - "requires": { - "graceful-fs": "^4.1.2", - "parse-json": "^4.0.0", - "pify": "^3.0.0", - "strip-bom": "^3.0.0" - } - }, - "locate-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", - "integrity": "sha1-2+w7OrdZdYBxtY/ln8QYca8hQA4=", - "dev": true, - "requires": { - "p-locate": "^3.0.0", - "path-exists": "^3.0.0" - } - }, - "lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha1-Z5WRxWTDv/quhFTPCz3zcMPWkRw=", - "dev": true - }, - "lodash.truncate": { - "version": "4.4.2", - "resolved": "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz", - "integrity": "sha1-WjUNoLERO4N+z//VgSy+WNbq4ZM=", - "dev": true - }, - "log-driver": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/log-driver/-/log-driver-1.2.7.tgz", - "integrity": "sha512-U7KCmLdqsGHBLeWqYlFA0V0Sl6P08EE1ZrmA9cxjUE0WVqT9qnyVDPz1kzpFEP0jdJuFnasWIfSd7fsaNXkpbg==", - "dev": true - }, - "log-symbols": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", - "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", - "dev": true, - "requires": { - "chalk": "^4.1.0", - "is-unicode-supported": "^0.1.0" - }, - "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.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==", - "dev": true, - "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==", - "dev": true - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "lolex": { - "version": "2.7.5", - "resolved": "https://registry.npmjs.org/lolex/-/lolex-2.7.5.tgz", - "integrity": "sha1-ETAB1Wv8fgLVbjYpHMXEE9GqBzM=", - "dev": true - }, - "loose-envify": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", - "integrity": "sha1-ce5R+nvkyuwaY4OffmgtgTLTDK8=", - "dev": true, - "requires": { - "js-tokens": "^3.0.0 || ^4.0.0" - } - }, - "make-dir": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", - "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", - "dev": true, - "requires": { - "semver": "^6.0.0" - } - }, - "mime-db": { - "version": "1.51.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.51.0.tgz", - "integrity": "sha512-5y8A56jg7XVQx2mbv1lu49NR4dokRnhZYTtL+KGfaa27uq4pSTXkwQkFJl4pkRMyNFz/EtYDSkiiEHx3F7UN6g==", - "dev": true - }, - "mime-types": { - "version": "2.1.34", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.34.tgz", - "integrity": "sha512-6cP692WwGIs9XXdOO4++N+7qjqv0rqxxVvJ3VHPh/Sc9mVZcQP+ZGhkKiTvWMQRr2tbHkJP/Yn7Y0npb3ZBs4A==", - "dev": true, - "requires": { - "mime-db": "1.51.0" - } - }, - "minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha1-UWbihkV/AzBgZL5Ul+jbsMPTIIM=", - "dev": true, - "requires": { - "brace-expansion": "^1.1.7" - } - }, - "minimist": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", - "integrity": "sha1-Z9ZgFLZqaoqqDAg8X9WN9OTpdgI=", - "dev": true - }, - "mocha": { - "version": "9.1.3", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-9.1.3.tgz", - "integrity": "sha512-Xcpl9FqXOAYqI3j79pEtHBBnQgVXIhpULjGQa7DVb0Po+VzmSIK9kanAiWLHoRR/dbZ2qpdPshuXr8l1VaHCzw==", - "dev": true, - "requires": { - "@ungap/promise-all-settled": "1.1.2", - "ansi-colors": "4.1.1", - "browser-stdout": "1.3.1", - "chokidar": "3.5.2", - "debug": "4.3.2", - "diff": "5.0.0", - "escape-string-regexp": "4.0.0", - "find-up": "5.0.0", - "glob": "7.1.7", - "growl": "1.10.5", - "he": "1.2.0", - "js-yaml": "4.1.0", - "log-symbols": "4.1.0", - "minimatch": "3.0.4", - "ms": "2.1.3", - "nanoid": "3.1.25", - "serialize-javascript": "6.0.0", - "strip-json-comments": "3.1.1", - "supports-color": "8.1.1", - "which": "2.0.2", - "workerpool": "6.1.5", - "yargs": "16.2.0", - "yargs-parser": "20.2.4", - "yargs-unparser": "2.0.0" - }, - "dependencies": { - "anymatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", - "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", - "dev": true, - "requires": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - } - }, - "argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true - }, - "binary-extensions": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", - "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", - "dev": true - }, - "braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "dev": true, - "requires": { - "fill-range": "^7.0.1" - } - }, - "chokidar": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.2.tgz", - "integrity": "sha512-ekGhOnNVPgT77r4K/U3GDhu+FQ2S8TnK/s2KbIGXi0SZWuwkZ2QNyfWdZW+TVfn84DpEP7rLeCt2UI6bJ8GwbQ==", - "dev": true, - "requires": { - "anymatch": "~3.1.2", - "braces": "~3.0.2", - "fsevents": "~2.3.2", - "glob-parent": "~5.1.2", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.6.0" - } - }, - "debug": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", - "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", - "dev": true, - "requires": { - "ms": "2.1.2" - }, - "dependencies": { - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - } - } - }, - "diff": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz", - "integrity": "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==", - "dev": true - }, - "escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "dev": true - }, - "fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "dev": true, - "requires": { - "to-regex-range": "^5.0.1" - } - }, - "find-up": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", - "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", - "dev": true, - "requires": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" - } - }, - "fsevents": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", - "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", - "dev": true, - "optional": true - }, - "glob": { - "version": "7.1.7", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz", - "integrity": "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==", - "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "requires": { - "is-glob": "^4.0.1" - } - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "is-binary-path": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", - "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", - "dev": true, - "requires": { - "binary-extensions": "^2.0.0" - } - }, - "is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", - "dev": true - }, - "is-glob": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "dev": true, - "requires": { - "is-extglob": "^2.1.1" - } - }, - "is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true - }, - "js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "dev": true, - "requires": { - "argparse": "^2.0.1" - } - }, - "locate-path": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", - "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", - "dev": true, - "requires": { - "p-locate": "^5.0.0" - } - }, - "ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true - }, - "normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "dev": true - }, - "p-limit": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", - "dev": true, - "requires": { - "yocto-queue": "^0.1.0" - } - }, - "p-locate": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", - "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", - "dev": true, - "requires": { - "p-limit": "^3.0.2" - } - }, - "path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true - }, - "readdirp": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", - "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", - "dev": true, - "requires": { - "picomatch": "^2.2.1" - } - }, - "strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", - "dev": true - }, - "supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - }, - "to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, - "requires": { - "is-number": "^7.0.0" - } - } - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true - }, - "nanoid": { - "version": "3.1.25", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.25.tgz", - "integrity": "sha512-rdwtIXaXCLFAQbnfqDRnI6jaRHp9fTcYBjtFKE8eezcZ7LuLjhUaQGNeMXf1HmRoCH32CLz6XwX0TtxEOS/A3Q==", - "dev": true - }, - "native-promise-only": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/native-promise-only/-/native-promise-only-0.8.1.tgz", - "integrity": "sha1-IKMYwwy0X3H+et+/eyHJnBRy7xE=", - "dev": true - }, - "natural-compare": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", - "dev": true - }, - "nise": { - "version": "1.5.3", - "resolved": "https://registry.npmjs.org/nise/-/nise-1.5.3.tgz", - "integrity": "sha1-nSz+N9RPVzF3ZsbpQIo1nF06wfc=", - "dev": true, - "requires": { - "@sinonjs/formatio": "^3.2.1", - "@sinonjs/text-encoding": "^0.7.1", - "just-extend": "^4.0.2", - "lolex": "^5.0.1", - "path-to-regexp": "^1.7.0" - }, - "dependencies": { - "lolex": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/lolex/-/lolex-5.1.2.tgz", - "integrity": "sha1-lTaU0JjOfAe8XtbQ5CvGwMbVo2c=", - "dev": true, - "requires": { - "@sinonjs/commons": "^1.7.0" - } - } - } - }, - "normalize-package-data": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", - "integrity": "sha1-5m2xg4sgDB38IzIl0SyzZSDiNKg=", - "dev": true, - "requires": { - "hosted-git-info": "^2.1.4", - "resolve": "^1.10.0", - "semver": "2 || 3 || 4 || 5", - "validate-npm-package-license": "^3.0.1" - }, - "dependencies": { - "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha1-qVT5Ma66UI0we78Gnv8MAclhFvc=", - "dev": true - } - } - }, - "oauth-sign": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", - "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==", - "dev": true - }, - "object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", - "dev": true - }, - "object-inspect": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.11.1.tgz", - "integrity": "sha1-1L19feVLmnVZn1mgC9aYwfHGVJs=", - "dev": true - }, - "object-is": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.5.tgz", - "integrity": "sha1-ud7qpfx/GEag+uzc7sE45XePU6w=", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3" - } - }, - "object-keys": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", - "integrity": "sha1-HEfyct8nfzsdrwYWd9nILiMixg4=", - "dev": true - }, - "object.assign": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz", - "integrity": "sha1-DtVKNC7Os3s4/3brgxoOeIy2OUA=", - "dev": true, - "requires": { - "call-bind": "^1.0.0", - "define-properties": "^1.1.3", - "has-symbols": "^1.0.1", - "object-keys": "^1.1.1" - } - }, - "object.entries": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.5.tgz", - "integrity": "sha512-TyxmjUoZggd4OrrU1W66FMDG6CuqJxsFvymeyXI51+vQLN67zYfZseptRge703kKQdo4uccgAKebXFcRCzk4+g==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3", - "es-abstract": "^1.19.1" - } - }, - "object.fromentries": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.5.tgz", - "integrity": "sha512-CAyG5mWQRRiBU57Re4FKoTBjXfDoNwdFVH2Y1tS9PqCsfUTymAohOkEMSG3aRNKmv4lV3O7p1et7c187q6bynw==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3", - "es-abstract": "^1.19.1" - } - }, - "object.hasown": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/object.hasown/-/object.hasown-1.1.0.tgz", - "integrity": "sha512-MhjYRfj3GBlhSkDHo6QmvgjRLXQ2zndabdf3nX0yTyZK9rPfxb6uRpAac8HXNLy1GpqWtZ81Qh4v3uOls2sRAg==", - "dev": true, - "requires": { - "define-properties": "^1.1.3", - "es-abstract": "^1.19.1" - } - }, - "object.values": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.5.tgz", - "integrity": "sha512-QUZRW0ilQ3PnPpbNtgdNV1PDbEqLIiSFB3l+EnGtBQ/8SUTLj1PZwtQHABZtLgwpJZTSZhuGLOGk57Drx2IvYg==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3", - "es-abstract": "^1.19.1" - } - }, - "once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "dev": true, - "requires": { - "wrappy": "1" - } - }, - "p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha1-PdM8ZHohT9//2DWTPrCG2g3CHbE=", - "dev": true, - "requires": { - "p-try": "^2.0.0" - } - }, - "p-locate": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", - "integrity": "sha1-Mi1poFwCZLJZl9n0DNiokasAZKQ=", - "dev": true, - "requires": { - "p-limit": "^2.0.0" - } - }, - "p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha1-yyhoVA4xPWHeWPr741zpAE1VQOY=", - "dev": true - }, - "parent-module": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", - "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", - "dev": true, - "requires": { - "callsites": "^3.0.0" - } - }, - "parse-json": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", - "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=", - "dev": true, - "requires": { - "error-ex": "^1.3.1", - "json-parse-better-errors": "^1.0.1" - } - }, - "path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", - "dev": true - }, - "path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", - "dev": true - }, - "path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true - }, - "path-parse": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha1-+8EUtgykKzDZ2vWFjkvWi77bZzU=", - "dev": true - }, - "path-to-regexp": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.8.0.tgz", - "integrity": "sha1-iHs7qdhDk+h6CgufTLdWGYtTVIo=", - "dev": true, - "requires": { - "isarray": "0.0.1" - }, - "dependencies": { - "isarray": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", - "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", - "dev": true - } - } - }, - "path-type": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", - "integrity": "sha1-zvMdyOCho7sNEFwM2Xzzv0f0428=", - "dev": true, - "requires": { - "pify": "^3.0.0" - } - }, - "pathval": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz", - "integrity": "sha1-hTTnenfOesWiUS6iHg/bj89sPY0=", - "dev": true - }, - "performance-now": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", - "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=", - "dev": true - }, - "picomatch": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.0.tgz", - "integrity": "sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw==", - "dev": true - }, - "pify": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", - "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", - "dev": true - }, - "pkg-conf": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/pkg-conf/-/pkg-conf-3.1.0.tgz", - "integrity": "sha512-m0OTbR/5VPNPqO1ph6Fqbj7Hv6QU7gR/tQW40ZqrL1rjgCU85W6C1bJn0BItuJqnR98PWzw7Z8hHeChD1WrgdQ==", - "dev": true, - "requires": { - "find-up": "^3.0.0", - "load-json-file": "^5.2.0" - }, - "dependencies": { - "load-json-file": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-5.3.0.tgz", - "integrity": "sha512-cJGP40Jc/VXUsp8/OrnyKyTZ1y6v/dphm3bioS+RrKXjK2BB6wHUd6JptZEFDGgGahMT+InnZO5i1Ei9mpC8Bw==", - "dev": true, - "requires": { - "graceful-fs": "^4.1.15", - "parse-json": "^4.0.0", - "pify": "^4.0.1", - "strip-bom": "^3.0.0", - "type-fest": "^0.3.0" - } - }, - "pify": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", - "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", - "dev": true - }, - "type-fest": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.3.1.tgz", - "integrity": "sha512-cUGJnCdr4STbePCgqNFbpVNCepa+kAVohJs1sLhxzdH+gnEoOd8VhbYa7pD3zZYGiURWM2xzEII3fQcRizDkYQ==", - "dev": true - } - } - }, - "pkg-dir": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-2.0.0.tgz", - "integrity": "sha1-9tXREJ4Z1j7fQo4L1X4Sd3YVM0s=", - "dev": true, - "requires": { - "find-up": "^2.1.0" - }, - "dependencies": { - "find-up": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", - "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", - "dev": true, - "requires": { - "locate-path": "^2.0.0" - } - }, - "locate-path": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", - "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", - "dev": true, - "requires": { - "p-locate": "^2.0.0", - "path-exists": "^3.0.0" - } - }, - "p-limit": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", - "integrity": "sha1-uGvV8MJWkJEcdZD8v8IBDVSzzLg=", - "dev": true, - "requires": { - "p-try": "^1.0.0" - } - }, - "p-locate": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", - "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", - "dev": true, - "requires": { - "p-limit": "^1.1.0" - } - }, - "p-try": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", - "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=", - "dev": true - } - } - }, - "pkg-up": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/pkg-up/-/pkg-up-2.0.0.tgz", - "integrity": "sha1-yBmscoBZpGHKscOImivjxJoATX8=", - "dev": true, - "requires": { - "find-up": "^2.1.0" - }, - "dependencies": { - "find-up": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", - "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", - "dev": true, - "requires": { - "locate-path": "^2.0.0" - } - }, - "locate-path": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", - "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", - "dev": true, - "requires": { - "p-locate": "^2.0.0", - "path-exists": "^3.0.0" - } - }, - "p-limit": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", - "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", - "dev": true, - "requires": { - "p-try": "^1.0.0" - } - }, - "p-locate": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", - "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", - "dev": true, - "requires": { - "p-limit": "^1.1.0" - } - }, - "p-try": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", - "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=", - "dev": true - } - } - }, - "progress": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", - "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", - "dev": true - }, - "prop-types": { - "version": "15.7.2", - "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.7.2.tgz", - "integrity": "sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==", - "dev": true, - "requires": { - "loose-envify": "^1.4.0", - "object-assign": "^4.1.1", - "react-is": "^16.8.1" - } - }, - "psl": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz", - "integrity": "sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ==", - "dev": true - }, - "punycode": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", - "integrity": "sha1-tYsBCsQMIsVldhbI0sLALHv0eew=", - "dev": true - }, - "qs": { - "version": "6.5.2", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", - "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==", - "dev": true - }, - "randombytes": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", - "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", - "dev": true, - "requires": { - "safe-buffer": "^5.1.0" - } - }, - "react-is": { - "version": "16.13.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", - "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", - "dev": true - }, - "read-pkg": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", - "integrity": "sha1-nLxoaXj+5l0WwA4rGcI3/Pbjg4k=", - "dev": true, - "requires": { - "load-json-file": "^4.0.0", - "normalize-package-data": "^2.3.2", - "path-type": "^3.0.0" - } - }, - "regexp.prototype.flags": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.3.1.tgz", - "integrity": "sha1-fvNSro0VnnWMDq3Kb4/LTu8HviY=", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3" - } - }, - "regexpp": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", - "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", - "dev": true - }, - "request": { - "version": "2.88.2", - "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz", - "integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==", - "dev": true, - "requires": { - "aws-sign2": "~0.7.0", - "aws4": "^1.8.0", - "caseless": "~0.12.0", - "combined-stream": "~1.0.6", - "extend": "~3.0.2", - "forever-agent": "~0.6.1", - "form-data": "~2.3.2", - "har-validator": "~5.1.3", - "http-signature": "~1.2.0", - "is-typedarray": "~1.0.0", - "isstream": "~0.1.2", - "json-stringify-safe": "~5.0.1", - "mime-types": "~2.1.19", - "oauth-sign": "~0.9.0", - "performance-now": "^2.1.0", - "qs": "~6.5.2", - "safe-buffer": "^5.1.2", - "tough-cookie": "~2.5.0", - "tunnel-agent": "^0.6.0", - "uuid": "^3.3.2" - } - }, - "require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", - "dev": true - }, - "require-from-string": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", - "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", - "dev": true - }, - "resolve": { - "version": "1.20.0", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.20.0.tgz", - "integrity": "sha1-YpoBP7P3B1XW8LeTXMHCxTeLGXU=", - "dev": true, - "requires": { - "is-core-module": "^2.2.0", - "path-parse": "^1.0.6" - } - }, - "resolve-from": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", - "dev": true - }, - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha1-mR7GnSluAxN0fVm9/St0XDX4go0=", - "dev": true - }, - "safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha1-RPoWGwGHuVSd2Eu5GAL5vYOFzWo=", - "dev": true - }, - "samsam": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/samsam/-/samsam-1.3.0.tgz", - "integrity": "sha1-jR2TUOJWItow3j5EumkrUiGrfFA=", - "dev": true - }, - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha1-7gpkyK9ejO6mdoexM3YeG+y9HT0=", - "dev": true - }, - "serialize-javascript": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", - "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==", - "dev": true, - "requires": { - "randombytes": "^2.1.0" - } - }, - "shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, - "requires": { - "shebang-regex": "^3.0.0" - } - }, - "shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true - }, - "side-channel": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", - "integrity": "sha1-785cj9wQTudRslxY1CkAEfpeos8=", - "dev": true, - "requires": { - "call-bind": "^1.0.0", - "get-intrinsic": "^1.0.2", - "object-inspect": "^1.9.0" - } - }, - "signal-exit": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.6.tgz", - "integrity": "sha512-sDl4qMFpijcGw22U5w63KmD3cZJfBuFlVNbVMKje2keoKML7X2UzWbc4XrmEbDwg0NXJc3yv4/ox7b+JWb57kQ==", - "dev": true - }, - "sinon": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/sinon/-/sinon-3.2.1.tgz", - "integrity": "sha1-2K2r2QBzD9SXeIoCcEnGSwi+kcI=", - "dev": true, - "requires": { - "diff": "^3.1.0", - "formatio": "1.2.0", - "lolex": "^2.1.2", - "native-promise-only": "^0.8.1", - "nise": "^1.0.1", - "path-to-regexp": "^1.7.0", - "samsam": "^1.1.3", - "text-encoding": "0.6.4", - "type-detect": "^4.0.0" - } - }, - "sinon-chai": { - "version": "2.13.0", - "resolved": "https://registry.npmjs.org/sinon-chai/-/sinon-chai-2.13.0.tgz", - "integrity": "sha1-uaQugBwgI0v8L0Oynm9PYbYJkMQ=", - "dev": true - }, - "slice-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", - "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", - "dev": true, - "requires": { - "ansi-styles": "^4.0.0", - "astral-regex": "^2.0.0", - "is-fullwidth-code-point": "^3.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "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==", - "dev": true, - "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==", - "dev": true - } - } - }, - "spdx-correct": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz", - "integrity": "sha1-3s6BrJweZxPl99G28X1Gj6U9iak=", - "dev": true, - "requires": { - "spdx-expression-parse": "^3.0.0", - "spdx-license-ids": "^3.0.0" - } - }, - "spdx-exceptions": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz", - "integrity": "sha1-PyjOGnegA3JoPq3kpDMYNSeiFj0=", - "dev": true - }, - "spdx-expression-parse": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", - "integrity": "sha1-z3D1BILu/cmOPOCmgz5KU87rpnk=", - "dev": true, - "requires": { - "spdx-exceptions": "^2.1.0", - "spdx-license-ids": "^3.0.0" - } - }, - "spdx-license-ids": { - "version": "3.0.11", - "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.11.tgz", - "integrity": "sha1-UMDYxAoU7Bv0Sbrmmg6kaFqdn5U=", - "dev": true - }, - "sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", - "dev": true - }, - "sshpk": { - "version": "1.16.1", - "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz", - "integrity": "sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==", - "dev": true, - "requires": { - "asn1": "~0.2.3", - "assert-plus": "^1.0.0", - "bcrypt-pbkdf": "^1.0.0", - "dashdash": "^1.12.0", - "ecc-jsbn": "~0.1.1", - "getpass": "^0.1.1", - "jsbn": "~0.1.0", - "safer-buffer": "^2.0.2", - "tweetnacl": "~0.14.0" - } - }, - "stack-trace": { - "version": "0.0.10", - "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", - "integrity": "sha1-VHxws0fo0ytOEI6hoqFZ5f3eGcA=" - }, - "standard": { - "version": "16.0.4", - "resolved": "https://registry.npmjs.org/standard/-/standard-16.0.4.tgz", - "integrity": "sha512-2AGI874RNClW4xUdM+bg1LRXVlYLzTNEkHmTG5mhyn45OhbgwA+6znowkOGYy+WMb5HRyELvtNy39kcdMQMcYQ==", - "dev": true, - "requires": { - "eslint": "~7.18.0", - "eslint-config-standard": "16.0.3", - "eslint-config-standard-jsx": "10.0.0", - "eslint-plugin-import": "~2.24.2", - "eslint-plugin-node": "~11.1.0", - "eslint-plugin-promise": "~5.1.0", - "eslint-plugin-react": "~7.25.1", - "standard-engine": "^14.0.1" - } - }, - "standard-engine": { - "version": "14.0.1", - "resolved": "https://registry.npmjs.org/standard-engine/-/standard-engine-14.0.1.tgz", - "integrity": "sha512-7FEzDwmHDOGva7r9ifOzD3BGdTbA7ujJ50afLVdW/tK14zQEptJjbFuUfn50irqdHDcTbNh0DTIoMPynMCXb0Q==", - "dev": true, - "requires": { - "get-stdin": "^8.0.0", - "minimist": "^1.2.5", - "pkg-conf": "^3.1.0", - "xdg-basedir": "^4.0.0" - } - }, - "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==", - "dev": true, - "requires": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "dependencies": { - "ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true - }, - "strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "requires": { - "ansi-regex": "^5.0.1" - } - } - } - }, - "string.prototype.matchall": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.6.tgz", - "integrity": "sha512-6WgDX8HmQqvEd7J+G6VtAahhsQIssiZ8zl7zKh1VDMFyL3hRTJP4FTNA3RbIp2TOQ9AYNDcc7e3fH0Qbup+DBg==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3", - "es-abstract": "^1.19.1", - "get-intrinsic": "^1.1.1", - "has-symbols": "^1.0.2", - "internal-slot": "^1.0.3", - "regexp.prototype.flags": "^1.3.1", - "side-channel": "^1.0.4" - } - }, - "string.prototype.trimend": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.4.tgz", - "integrity": "sha1-51rpDClCxjUEaGwYsoe0oLGkX4A=", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3" - } - }, - "string.prototype.trimstart": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.4.tgz", - "integrity": "sha1-s2OZr0qymZtMnGSL16P7K7Jv7u0=", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3" - } - }, - "strip-bom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", - "dev": true - }, - "strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", - "dev": true - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - }, - "dependencies": { - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - } - } - }, - "table": { - "version": "6.7.5", - "resolved": "https://registry.npmjs.org/table/-/table-6.7.5.tgz", - "integrity": "sha512-LFNeryOqiQHqCVKzhkymKwt6ozeRhlm8IL1mE8rNUurkir4heF6PzMyRgaTa4tlyPTGGgXuvVOF/OLWiH09Lqw==", - "dev": true, - "requires": { - "ajv": "^8.0.1", - "lodash.truncate": "^4.4.2", - "slice-ansi": "^4.0.0", - "string-width": "^4.2.3", - "strip-ansi": "^6.0.1" - }, - "dependencies": { - "ajv": { - "version": "8.8.2", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.8.2.tgz", - "integrity": "sha512-x9VuX+R/jcFj1DHo/fCp99esgGDWiHENrKxaCENuCxpoMCmAt/COCGVDwA7kleEpEzJjDnvh3yGoOuLu0Dtllw==", - "dev": true, - "requires": { - "fast-deep-equal": "^3.1.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.2.2" - } - }, - "ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true - }, - "json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", - "dev": true - }, - "strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "requires": { - "ansi-regex": "^5.0.1" - } - } - } - }, - "test-exclude": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", - "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", - "dev": true, - "requires": { - "@istanbuljs/schema": "^0.1.2", - "glob": "^7.1.4", - "minimatch": "^3.0.4" - } - }, - "text-encoding": { - "version": "0.6.4", - "resolved": "https://registry.npmjs.org/text-encoding/-/text-encoding-0.6.4.tgz", - "integrity": "sha1-45mpgiV6J22uQou5KEXLcb3CbRk=", - "dev": true - }, - "text-table": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", - "dev": true - }, - "tough-cookie": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", - "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", - "dev": true, - "requires": { - "psl": "^1.1.28", - "punycode": "^2.1.1" - } - }, - "tsconfig-paths": { - "version": "3.12.0", - "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.12.0.tgz", - "integrity": "sha512-e5adrnOYT6zqVnWqZu7i/BQ3BnhzvGbjEjejFXO20lKIKpwTaupkCPgEfv4GZK1IBciJUEhYs3J3p75FdaTFVg==", - "dev": true, - "requires": { - "@types/json5": "^0.0.29", - "json5": "^1.0.1", - "minimist": "^1.2.0", - "strip-bom": "^3.0.0" - }, - "dependencies": { - "json5": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", - "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", - "dev": true, - "requires": { - "minimist": "^1.2.0" - } - } - } - }, - "tunnel-agent": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", - "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", - "dev": true, - "requires": { - "safe-buffer": "^5.0.1" - } - }, - "tweetnacl": { - "version": "0.14.5", - "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", - "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=", - "dev": true - }, - "type-detect": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", - "integrity": "sha1-dkb7XxiHHPu3dJ5pvTmmOI63RQw=", - "dev": true - }, - "type-fest": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", - "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", - "dev": true - }, - "unbox-primitive": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.1.tgz", - "integrity": "sha1-CF4hViXsMWJXTciFmr7nilmxRHE=", - "dev": true, - "requires": { - "function-bind": "^1.1.1", - "has-bigints": "^1.0.1", - "has-symbols": "^1.0.2", - "which-boxed-primitive": "^1.0.2" - } - }, - "uri-js": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", - "integrity": "sha1-mxpSWVIlhZ5V9mnZKPiMbFfyp34=", - "dev": true, - "requires": { - "punycode": "^2.1.0" - } - }, - "uuid": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", - "integrity": "sha1-sj5DWK+oogL+ehAK8fX4g/AgB+4=", - "dev": true - }, - "v8-compile-cache": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz", - "integrity": "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==", - "dev": true - }, - "v8-to-istanbul": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-8.1.0.tgz", - "integrity": "sha512-/PRhfd8aTNp9Ggr62HPzXg2XasNFGy5PBt0Rp04du7/8GNNSgxFL6WBTkgMKSL9bFjH+8kKEG3f37FmxiTqUUA==", - "dev": true, - "requires": { - "@types/istanbul-lib-coverage": "^2.0.1", - "convert-source-map": "^1.6.0", - "source-map": "^0.7.3" - }, - "dependencies": { - "source-map": { - "version": "0.7.3", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz", - "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==", - "dev": true - } - } - }, - "validate-npm-package-license": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", - "integrity": "sha1-/JH2uce6FchX9MssXe/uw51PQQo=", - "dev": true, - "requires": { - "spdx-correct": "^3.0.0", - "spdx-expression-parse": "^3.0.0" - } - }, - "verror": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", - "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", - "dev": true, - "requires": { - "assert-plus": "^1.0.0", - "core-util-is": "1.0.2", - "extsprintf": "^1.2.0" - }, - "dependencies": { - "core-util-is": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", - "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", - "dev": true - } - } - }, - "which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "requires": { - "isexe": "^2.0.0" - } - }, - "which-boxed-primitive": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", - "integrity": "sha1-E3V7yJsgmwSf5dhkMOIc9AqJqOY=", - "dev": true, - "requires": { - "is-bigint": "^1.0.1", - "is-boolean-object": "^1.1.0", - "is-number-object": "^1.0.4", - "is-string": "^1.0.5", - "is-symbol": "^1.0.3" - } - }, - "winston": { - "version": "2.4.4", - "resolved": "https://registry.npmjs.org/winston/-/winston-2.4.4.tgz", - "integrity": "sha512-NBo2Pepn4hK4V01UfcWcDlmiVTs7VTB1h7bgnB0rgP146bYhMxX0ypCz3lBOfNxCO4Zuek7yeT+y/zM1OfMw4Q==", - "requires": { - "async": "~1.0.0", - "colors": "1.0.x", - "cycle": "1.0.x", - "eyes": "0.1.x", - "isstream": "0.1.x", - "stack-trace": "0.0.x" - }, - "dependencies": { - "async": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/async/-/async-1.0.0.tgz", - "integrity": "sha1-+PwEyjoTeErenhZBr5hXjPvWR6k=" - } - } - }, - "word-wrap": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", - "integrity": "sha1-YQY29rH3A4kb00dxzLF/uTtHB5w=", - "dev": true - }, - "workerpool": { - "version": "6.1.5", - "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.1.5.tgz", - "integrity": "sha512-XdKkCK0Zqc6w3iTxLckiuJ81tiD/o5rBE/m+nXpRCB+/Sq4DqkfXZ/x0jW02DG1tGsfUGXbTJyZDP+eu67haSw==", - "dev": true - }, - "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==", - "dev": true, - "requires": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "dependencies": { - "ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true - }, - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "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==", - "dev": true, - "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==", - "dev": true - }, - "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==", - "dev": true - }, - "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==", - "dev": true, - "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==", - "dev": true, - "requires": { - "ansi-regex": "^5.0.1" - } - } - } - }, - "wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", - "dev": true - }, - "xdg-basedir": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-4.0.0.tgz", - "integrity": "sha512-PSNhEJDejZYV7h50BohL09Er9VaIefr2LMAf3OEmpCkjOi34eYyQYAXUTjEQtZJTKcF0E2UKTh+osDLsgNim9Q==", - "dev": true - }, - "y18n": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "dev": true - }, - "yargs": { - "version": "16.2.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", - "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", - "dev": true, - "requires": { - "cliui": "^7.0.2", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.0", - "y18n": "^5.0.5", - "yargs-parser": "^20.2.2" - }, - "dependencies": { - "ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true - }, - "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==", - "dev": true - }, - "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==", - "dev": true, - "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==", - "dev": true, - "requires": { - "ansi-regex": "^5.0.1" - } - } - } - }, - "yargs-parser": { - "version": "20.2.4", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz", - "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==", - "dev": true - }, - "yargs-unparser": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz", - "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==", - "dev": true, - "requires": { - "camelcase": "^6.0.0", - "decamelize": "^4.0.0", - "flat": "^5.0.2", - "is-plain-obj": "^2.1.0" - } - }, - "yocto-queue": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", - "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", - "dev": true - } - } -} diff --git a/package.json b/package.json deleted file mode 100644 index e6e4a89..0000000 --- a/package.json +++ /dev/null @@ -1,56 +0,0 @@ -{ - "name": "firehose-avro-to-json-transformer", - "version": "1.0.0", - "description": "Converts Kinesis Firehose records in Avro to JSON for ingest by Amazon Redshift.", - "main": "index.js", - "scripts": { - "lint": "./node_modules/.bin/standard", - "test": "./node_modules/.bin/standard && NODE_ENV=test ./node_modules/.bin/c8 --reporter=lcov --reporter=text ./node_modules/.bin/mocha --recursive", - "coverage-report": "./node_modules/.bin/c8 report --reporter=html" - }, - "repository": { - "type": "git", - "url": "git+https://github.com/NYPL/firehose-avro-to-json-transformer.git" - }, - "keywords": [ - "aws", - "lambda", - "node", - "kinesis", - "firehose" - ], - "author": "NYPL Data Warehouse Team & Rafael Hernandez", - "license": "ISC", - "bugs": { - "url": "https://github.com/NYPL/firehose-avro-to-json-transformer/issues" - }, - "dependencies": { - "avsc": "5.0.7", - "axios": "^0.24.0", - "winston": "2.4.4" - }, - "devDependencies": { - "axios-mock-adapter": "1.9.0", - "c8": "^7.10.0", - "chai": "4.3.4", - "chai-as-promised": "7.1.1", - "coveralls": "^3.1.1", - "mocha": "9.1.3", - "sinon": "3.2.1", - "sinon-chai": "2.13.0", - "standard": "^16.0.4" - }, - "standard": { - "env": { - "mocha": true - }, - "globals": [ - "expect" - ], - "ignore": [ - "/sample/", - "/build/", - "config" - ] - } -} diff --git a/provisioning/base/resources.tf b/provisioning/base/resources.tf deleted file mode 100644 index b23f4a1..0000000 --- a/provisioning/base/resources.tf +++ /dev/null @@ -1,54 +0,0 @@ -provider "aws" { - region = "us-east-1" -} - -variable "environment" { - type = string - default = "qa" - description = "The name of the environnment (qa, production). This controls the name of lambda and the env vars loaded." - - validation { - condition = contains(["qa", "production"], var.environment) - error_message = "The environmnet must be 'qa' or 'production'." - } -} - -# Package the app as a zip: -data "archive_file" "lambda_zip" { - type = "zip" - output_path = "${path.module}/dist.zip" - source_dir = "../../" - excludes = [".git", ".terraform", "provisioning"] -} - -# Upload the zipped app to S3: -resource "aws_s3_bucket_object" "uploaded_zip" { - bucket = "nypl-travis-builds-${var.environment}" - key = "avro-to-json-transofmer-${var.environment}-dist.zip" - acl = "private" - source = data.archive_file.lambda_zip.output_path - etag = filemd5(data.archive_file.lambda_zip.output_path) -} - -# Create the lambda: -resource "aws_lambda_function" "lambda_instance" { - description = "Converts Kinesis Firehose records in Avro to JSON for ingest by Amazon Redshift." - function_name = "AvroToJsonTransformer-${var.environment}" - handler = "index.handler" - memory_size = 128 - role = "arn:aws:iam::946183545209:role/lambda-full-access" - runtime = "nodejs14.x" - timeout = 60 - - # Location of the zipped code in S3: - s3_bucket = aws_s3_bucket_object.uploaded_zip.bucket - s3_key = aws_s3_bucket_object.uploaded_zip.key - - # Trigger pulling code from S3 when the zip has changed: - source_code_hash = data.archive_file.lambda_zip.output_base64sha256 - - # Load ENV vars from ./config/{environment}.env - environment { - variables = { for tuple in regexall("(.*?)=(.*)", file("../../config/${var.environment}.env")) : tuple[0] => tuple[1] } - } -} diff --git a/provisioning/production/resources.tf b/provisioning/production/resources.tf deleted file mode 100644 index 036b490..0000000 --- a/provisioning/production/resources.tf +++ /dev/null @@ -1,18 +0,0 @@ -provider "aws" { - region = "us-east-1" -} - -terraform { - # Use s3 to store terraform state - backend "s3" { - bucket = "nypl-travis-builds-production" - key = "avro-to-json-transformer-terraform-state" - region = "us-east-1" - } -} - -module "base" { - source = "../base" - - environment = "production" -} diff --git a/provisioning/qa/resources.tf b/provisioning/qa/resources.tf deleted file mode 100644 index 186bea5..0000000 --- a/provisioning/qa/resources.tf +++ /dev/null @@ -1,18 +0,0 @@ -provider "aws" { - region = "us-east-1" -} - -terraform { - # Use s3 to store terraform state - backend "s3" { - bucket = "nypl-travis-builds-qa" - key = "avro-to-json-transformer-terraform-state" - region = "us-east-1" - } -} - -module "base" { - source = "../base" - - environment = "qa" -} diff --git a/record_processor.py b/record_processor.py new file mode 100755 index 0000000..f051f45 --- /dev/null +++ b/record_processor.py @@ -0,0 +1,58 @@ +import base64 +import csv +import io +import json + +from nypl_py_utils.classes.avro_client import AvroDecoder +from nypl_py_utils.functions.log_helper import create_log + + +class RecordProcessor: + def __init__(self, schema_url): + self.logger = create_log("record_processor") + self.avro_decoder = AvroDecoder(schema_url) + self.schema = self.avro_decoder.get_json_schema(schema_url) + + def process_record(self, record, output_format): + """ + Maps records to decoded Avro. Method returns data in + desired output format for Firehose (JSON or CSV string) + """ + binary_record_data = base64.b64decode(record["data"]) + decoded_record_data = self.avro_decoder.decode_record( + binary_record_data) + result_string = self._format_result_string( + output_format, decoded_record_data + ) + + return { + "recordId": record["recordId"], + "result": "Ok", + "data": result_string, # needed for JSON conversion + } + + def _format_result_string(self, output_format, data): + if output_format == "csv" and isinstance(data, dict): + data = self._transform_dictionary_to_csv_string(data) + else: + data = json.dumps(data) + # We need to take original string ("data" ) and convert to hex, + # which requires using bytes as an intermediate type + to_bytes = data.encode("utf-8") + return (base64.b64encode(to_bytes)).decode("utf-8") + + def _transform_dictionary_to_csv_string(self, data): + # replace vertical bar within data + output = io.StringIO() + writer = csv.DictWriter( + f=output, + fieldnames=data.keys(), + delimiter="|", + quoting=csv.QUOTE_NONE, + escapechar="\\", + ) + writer.writerow(data) + + # remove carriage returns + csv_string = output.getvalue() + return csv_string.replace("\r", "") diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..ee5d73f --- /dev/null +++ b/requirements.txt @@ -0,0 +1,4 @@ +nypl-py-utils[avro-client]==1.2.0 +pybase64 +python-csv +python-io \ No newline at end of file diff --git a/sample/deployment.env.sample b/sample/deployment.env.sample deleted file mode 100644 index 26c46f1..0000000 --- a/sample/deployment.env.sample +++ /dev/null @@ -1,4 +0,0 @@ -NYPL_DATA_API_BASE_URL=XXX -SCHEMA_PATH=XXX -SCHEMA_NAME=XXX -NODE_ENV=development diff --git a/sample/firehose-LocationHours-3-records-encoded.json b/sample/firehose-LocationHours-3-records-encoded.json index 9117d26..229061e 100644 --- a/sample/firehose-LocationHours-3-records-encoded.json +++ b/sample/firehose-LocationHours-3-records-encoded.json @@ -14,7 +14,7 @@ "shardId": "shardId-000000000000", "approximateArrivalTimestamp": 1495072949453 }, - "data": "AARhYQASTGlicmFyeSBBAAZNb24CCjA5OjAwAgoxNzowMAAUMjAyMy0wMS0wMQ==" + "data": "AARhYQISTGlicmFyeSBBAAZNb24CCjA5OjAwAgoxNzowMAIUMjAyMy0wMS0wMQ==" }, { "recordId": "49546986683135544286507457936321625675700192471156785154", @@ -26,7 +26,7 @@ "shardId": "shardId-000000000000", "approximateArrivalTimestamp": 1495072949453 }, - "data": "AARiYgASTGlicmFyeSBCAAZUdWUAAAAUMjAyMy0wMi0wMg==" + "data": "AARiYgISTGlicmFyeSBCAAZUdWUAAAIUMjAyMy0wMi0wMg==" }, { "recordId": "49546986683135544286507457936321625675700192471156785154", @@ -38,7 +38,7 @@ "shardId": "shardId-000000000000", "approximateArrivalTimestamp": 1495072949453 }, - "data": "AARjYwAeTGlicmFyeSB8IEMgISw9AAZXZWQCCjAxOjAwAgoyMzowMAAUMjAyMy0wMy0wMw==" + "data": "AARjYwIeTGlicmFyeSB8IEMgISw9AAZXZWQCCjAxOjAwAgoyMzowMAIUMjAyMy0wMy0wMw==" } ] } diff --git a/src/cache/CacheFactory.js b/src/cache/CacheFactory.js deleted file mode 100644 index 673ed13..0000000 --- a/src/cache/CacheFactory.js +++ /dev/null @@ -1,18 +0,0 @@ -const cache = { - schemas: {}, - nodeEnv: process.env.NODE_ENV, - getSchema (name) { - return this.schemas[name] - }, - setSchema (name, schema) { - this.schemas[name] = schema - }, - getNodeEnv () { - return this.nodeEnv - }, - setNodeEnv (env) { - this.nodeEnv = env - } -} - -module.exports = cache diff --git a/src/helpers/ErrorHelper.js b/src/helpers/ErrorHelper.js deleted file mode 100644 index 412531e..0000000 --- a/src/helpers/ErrorHelper.js +++ /dev/null @@ -1,31 +0,0 @@ -module.exports = class TransformerError extends Error { - constructor (message, opts = {}) { - if (!message || typeof message !== 'string' || message.trim() === '') { - throw new Error('an error message is required') - } - - super(message) - - // Capturing stack trace, excluding constructor call from it. - if (typeof Error.captureStackTrace === 'function') { - Error.captureStackTrace(this, this.constructor) - } else { - this.stack = (new Error(message)).stack - } - - // Saving class name in the property of our custom error as a shortcut. - this.name = this.constructor.name - - if (opts.type) { - this.type = opts.type - } - - if (opts.statusCode) { - this.statusCode = opts.statusCode - } - - if (opts.debugInfo) { - this.debugInfo = opts.debugInfo - } - } -} diff --git a/src/helpers/Logger.js b/src/helpers/Logger.js deleted file mode 100644 index 3349ee4..0000000 --- a/src/helpers/Logger.js +++ /dev/null @@ -1,79 +0,0 @@ -const winston = require('winston') - -// Set default NYPL agreed upon log levels -const levels = { - emergency: 0, - alert: 1, - critical: 2, - error: 3, - warning: 4, - notice: 5, - info: 6, - debug: 7 -} - -const getLogLevelCode = (levelString) => { - switch (levelString) { - case 'emergency': - return 0 - case 'alert': - return 1 - case 'critical': - return 2 - case 'error': - return 3 - case 'warning': - return 4 - case 'notice': - return 5 - case 'info': - return 6 - case 'debug': - return 7 - default: - return 'n/a' - } -} - -const loggerTransports = [] - -if (process.env.NODE_ENV !== 'test') { - loggerTransports.push( - new winston.transports.Console({ - timestamp: () => { - return new Date().toISOString() - }, - formatter: (options) => { - const result = { - timestamp: options.timestamp(), - levelCode: getLogLevelCode(options.level), - level: options.level.toUpperCase() - } - - if (process.pid) { - result.pid = process.pid.toString() - } - - if (options.message) { - result.message = options.message - } - - if (options.meta && Object.keys(options.meta).length) { - if (options.meta && Object.keys(options.meta).length) { - result.meta = JSON.stringify(options.meta) - } - } - - return JSON.stringify(result) - } - }) - ) -} - -const logger = new (winston.Logger)({ - levels: levels, - transports: loggerTransports, - exitOnError: false -}) - -module.exports = logger diff --git a/src/helpers/RecordsProcessor.js b/src/helpers/RecordsProcessor.js deleted file mode 100644 index d13826b..0000000 --- a/src/helpers/RecordsProcessor.js +++ /dev/null @@ -1,60 +0,0 @@ -const avro = require('avsc') - -const logger = require('./Logger.js') - -// Map records to decode Avro and return data in desired output format to firehose. -const processRecords = function (schema, records, outputFormat) { - let success = 0 - let failure = 0 - const type = avro.Type.forSchema(schema) - const output = records.map((record) => { - const decodedData = decodeAvro(type, record.data) - if (!decodedData) { - failure++ - return { - recordId: record.recordId, - result: 'ProcessingFailed', - data: record.data - } - } - - let resultString = '' - if (outputFormat === 'csv') { - Object.values(decodedData).forEach((value) => { - if (value === null) { - value = '' - } else if (typeof value === 'string') { - value = value.replace('|', '\\|') - } - resultString += (value + '|') - }) - resultString = resultString.slice(0, -1) + '\n' - } else { - resultString = JSON.stringify(decodedData) - } - - success++ - return { - recordId: record.recordId, - result: 'Ok', - data: Buffer.from(resultString).toString('base64') - } - }) - logger.info(`Processing completed. Successful transformations - ${success}. Failed transformations - ${failure}.`) - return output -} - -const decodeAvro = function (type, record) { - const decodedRecord = Buffer.from(record, 'base64') - try { - return type.fromBuffer(decodedRecord) - } catch (e) { - logger.error(`Decoding fatal error occurred: ${e.message}`, { debugInfo: e }) - return false - } -} - -module.exports = { - processRecords, - decodeAvro -} diff --git a/src/helpers/SchemaHandler.js b/src/helpers/SchemaHandler.js deleted file mode 100644 index 34e3383..0000000 --- a/src/helpers/SchemaHandler.js +++ /dev/null @@ -1,74 +0,0 @@ -const axios = require('axios') - -const logger = require('./Logger.js') -const TransformerError = require('./ErrorHelper.js') - -const fetchSchema = function (url, path, name) { - if (!url || !path || !name) { - return Promise.reject( - new TransformerError('missing one or more URL parameters') - ) - } - - return axios.get(url + path + name) - .then(response => { - if (response.data && typeof response.data.data !== 'undefined' && typeof response.data.data.schema !== 'undefined') { - return Promise.resolve(JSON.parse(response.data.data.schema)) - } - - return Promise.reject( - new TransformerError( - 'Schema object could not be retrieved', - { type: 'invalid-schema-response' } - ) - ) - }) - .catch(error => { - let errMsg = 'An error occurred requesting the schema from the NYPL API' - errMsg += ` (${url}${path}${name})` - - if (error.response) { - const statusCode = error.response.status - const statusText = error.response.statusText - if (statusCode) { - errMsg += `; the service responded with status code: (${statusCode})` - } - if (statusText) { - errMsg += ` and status text: (${statusText})` - } - - logger.error(errMsg, { debugInfo: error.response }) - return Promise.reject( - new TransformerError( - errMsg, - { - type: 'api-service-error', - statusCode: error.response.status || null - } - ) - ) - } - logger.error(errMsg, { debugInfo: error }) - return Promise.reject(error) - }) -} - -const schemaHandler = async function (cachedSchema, getSchemaFn) { - if (cachedSchema) { - return { - schemaType: 'cached-schema', - schema: cachedSchema - } - } - - const freshSchema = await getSchemaFn - return { - schemaType: 'fresh-schema', - schema: freshSchema - } -} - -module.exports = { - fetchSchema, - schemaHandler -} diff --git a/test/cache/CacheFactory.test.js b/test/cache/CacheFactory.test.js deleted file mode 100644 index c7d9558..0000000 --- a/test/cache/CacheFactory.test.js +++ /dev/null @@ -1,31 +0,0 @@ -/* eslint-disable no-unused-expressions */ - -const Cache = require('../../src/cache/CacheFactory.js') - -describe('AvroToJsonTransformer Lambda: CacheFactory', () => { - it('should initialize the nodeEnv variable', () => { - expect(Cache.nodeEnv).to.equal(process.env.NODE_ENV) - }) - - it('should return the set value for nodeEnv variable', () => { - const testEnv = 'local' - Cache.setNodeEnv(testEnv) - expect(Cache.getNodeEnv()).to.equal(testEnv) - }) - - it('should initialize schema variable as null', () => { - expect(Cache.schemas).to.be.a('object') - expect(Cache.schemas).to.be.empty - }) - - it('should return the set value for schema variable', () => { - const testSchema = '{ "name": "circ_trans", "type": "record" }' - Cache.setSchema('circ_trans', testSchema) - expect(Cache.getSchema('circ_trans')).to.equal(testSchema) - - // Confirm it can handle another schema: - const testSchema2 = '{ "name": "circ_trans2", "type": "record" }' - Cache.setSchema('circ_trans2', testSchema2) - expect(Cache.getSchema('circ_trans2')).to.equal(testSchema2) - }) -}) diff --git a/test/handler.test.js b/test/handler.test.js deleted file mode 100644 index 036adbb..0000000 --- a/test/handler.test.js +++ /dev/null @@ -1,610 +0,0 @@ -/* eslint-env mocha */ -/* eslint-disable no-unused-expressions */ - -const fs = require('fs') -const axios = require('axios') -const sinon = require('sinon') -const MockAdapter = require('axios-mock-adapter') - -const AvroToJsonTransformer = require('../index.js') -const { setEnv, restoreEnv, decodeBase64Json } = require('./test-helper.js') - -const circTransEvent = JSON.parse(fs.readFileSync('./sample/firehose-CircTrans-3-records-encoded.json', 'utf8')) -const pcReserveEvent = JSON.parse(fs.readFileSync('./sample/firehose-PcReserve-3-records-encoded.json', 'utf8')) -const patronInfoEvent = JSON.parse(fs.readFileSync('./sample/firehose-PatronInfo-3-records-encoded.json', 'utf8')) -const locationHoursEvent = JSON.parse(fs.readFileSync('./sample/firehose-LocationHours-3-records-encoded.json', 'utf8')) - -const circTransJson = JSON.parse(fs.readFileSync('./sample/CircTrans-3-records-decoded.json', 'utf8')) -const pcReserveJson = JSON.parse(fs.readFileSync('./sample/PcReserve-3-records-decoded.json', 'utf8')) -const patronInfoJson = JSON.parse(fs.readFileSync('./sample/PatronInfo-3-records-decoded.json', 'utf8')) -const locationHoursCsv = fs.readFileSync('./sample/LocationHours-3-records-decoded.csv', 'utf8').split('\n') - -const recordsHandlerFn = AvroToJsonTransformer.recordsHandler -const configHandlerFn = AvroToJsonTransformer.configHandler - -describe('AvroToJsonTransformer Lambda: Handle Firehose Input', () => { - describe('Main Handler: exports.handler()', () => { - let mock - beforeEach(() => { - mock = new MockAdapter(axios) - - setEnv({ - NYPL_DATA_API_BASE_URL: 'https://example.com', - SCHEMA_PATH: '/schemas/' - }) - }) - - afterEach(() => { - mock.reset() - - restoreEnv() - }) - - it('should return an error callback when event.records is null or missing', () => { - const callbackSpy = sinon.spy() - - AvroToJsonTransformer.handler(null, null, callbackSpy) - - const errArg = callbackSpy.firstCall.args[0] - - expect(errArg).to.be.instanceOf(Error) - expect(errArg.message).to.equal('event is undefined') - expect(callbackSpy).to.be.called - }) - - it('should throw an error if the event.records array is empty', () => { - const callbackSpy = sinon.spy() - - AvroToJsonTransformer.handler({ - sourceKinesisStreamArn: 'arn:...:stream/NonsenseStream-production', - records: [] - }, - null, - callbackSpy - ) - - const errArg = callbackSpy.firstCall.args[0] - - expect(errArg).to.be.instanceOf(Error) - expect(errArg.message).to.equal('event is undefined') - expect(callbackSpy).to.be.called - }) - - it('should throw an error if the event.records.data array is empty', () => { - const callbackSpy = sinon.spy() - - AvroToJsonTransformer.handler({ - sourceKinesisStreamArn: 'arn:...:stream/NonsenseStream-production', - records: [ - { - data: '' - } - ] - }, - null, - callbackSpy - ) - - const errArg = callbackSpy.firstCall.args[0] - - expect(errArg).to.be.instanceOf(Error) - expect(errArg.message).to.equal('event.records array is empty') - expect(callbackSpy).to.be.called - }) - - it('should callback with decoded CircTrans records', () => { - mock.onGet().reply( - 200, - JSON.parse(fs.readFileSync('./test/stubs/CircTrans-schema-response.json', 'utf8')) - ) - - const callbackSpy = sinon.spy() - - AvroToJsonTransformer.handler( - circTransEvent, - null, - callbackSpy - ) - - // A success callback invocation is technically async, so let things resolve: - setImmediate(() => { - expect(callbackSpy).to.be.called - - const errArg = callbackSpy.firstCall.args[0] - expect(errArg).to.be.null - - const payload = callbackSpy.firstCall.args[1] - expect(payload).to.be.a('object') - expect(payload.records).to.be.a('array') - expect(payload.records).to.have.lengthOf(3) - for (let i = 0; i < 3; i++) { - const record = payload.records[i] - expect(record.data).to.be.a('string') - expect(decodeBase64Json(record.data)).to.be.a('object') - expect(decodeBase64Json(record.data)).to.be.deep.equal(circTransJson[i]) - } - }) - }) - - it('should callback with decoded PcReserve records', () => { - mock.onGet().reply( - 200, - JSON.parse(fs.readFileSync('./test/stubs/PcReserve-schema-response.json', 'utf8')) - ) - - const callbackSpy = sinon.spy() - - AvroToJsonTransformer.handler( - pcReserveEvent, - null, - callbackSpy - ) - - // A success callback invocation is technically async, so let things resolve: - setImmediate(() => { - expect(callbackSpy).to.be.called - - const errArg = callbackSpy.firstCall.args[0] - expect(errArg).to.be.null - - const payload = callbackSpy.firstCall.args[1] - expect(payload).to.be.a('object') - expect(payload.records).to.be.a('array') - expect(payload.records).to.have.lengthOf(3) - for (let i = 0; i < 3; i++) { - const record = payload.records[i] - expect(record.data).to.be.a('string') - expect(decodeBase64Json(record.data)).to.be.a('object') - expect(decodeBase64Json(record.data)).to.be.deep.equal(pcReserveJson[i]) - } - }) - }) - - it('should callback with decoded PatronInfo records', () => { - mock.onGet().reply( - 200, - JSON.parse(fs.readFileSync('./test/stubs/PatronInfo-schema-response.json', 'utf8')) - ) - - const callbackSpy = sinon.spy() - - AvroToJsonTransformer.handler( - patronInfoEvent, - null, - callbackSpy - ) - - // A success callback invocation is technically async, so let things resolve: - setImmediate(() => { - expect(callbackSpy).to.be.called - - const errArg = callbackSpy.firstCall.args[0] - expect(errArg).to.be.null - - const payload = callbackSpy.firstCall.args[1] - expect(payload).to.be.a('object') - expect(payload.records).to.be.a('array') - expect(payload.records).to.have.lengthOf(3) - for (let i = 0; i < 3; i++) { - const record = payload.records[i] - expect(record.data).to.be.a('string') - expect(decodeBase64Json(record.data)).to.be.a('object') - expect(decodeBase64Json(record.data)).to.be.deep.equal(patronInfoJson[i]) - } - }) - }) - - it('should callback with decoded LocationHours csv records', () => { - mock.onGet().reply( - 200, - JSON.parse(fs.readFileSync('./test/stubs/LocationHours-schema-response.json', 'utf8')) - ) - - const callbackSpy = sinon.spy() - - AvroToJsonTransformer.handler( - locationHoursEvent, - null, - callbackSpy - ) - - // A success callback invocation is technically async, so let things resolve: - setImmediate(() => { - expect(callbackSpy).to.be.called - - const errArg = callbackSpy.firstCall.args[0] - expect(errArg).to.be.null - - const payload = callbackSpy.firstCall.args[1] - expect(payload).to.be.a('object') - expect(payload.records).to.be.a('array') - expect(payload.records).to.have.lengthOf(3) - for (let i = 0; i < 3; i++) { - const record = payload.records[i] - expect(record.data).to.be.a('string') - expect(Buffer.from(record.data, 'base64').toString('utf8')).to.be.equal(locationHoursCsv[i] + '\n') - } - }) - }) - - it('should callback with error if invalid schema name is identified', () => { - mock.onGet().reply(404, '') - - const callbackSpy = sinon.spy() - - const eventWithNonExistentSchema = Object.assign( - {}, - pcReserveEvent, - { sourceKinesisStreamArn: 'arn:...:stream/NonExistentSchemaName-production' } - ) - - AvroToJsonTransformer.handler( - eventWithNonExistentSchema, - null, - callbackSpy - ) - - // A success callback invocation is technically async, so let things resolve: - setImmediate(() => { - expect(callbackSpy).to.be.called - - const errArg = callbackSpy.firstCall.args[0] - expect(errArg).to.be.instanceOf(Error) - expect(errArg.message).to.equal('An error occurred requesting the schema from the NYPL API (https://example.com/schemas/NonExistentSchemaName); the service responded with status code: (404)') - expect(callbackSpy).to.be.called - - expect(callbackSpy.firstCall.args[1]).to.be.null - }) - }) - }) - - describe('Config Handler: exports.configHandler()', () => { - it('should respond with a TransformerError if parameter options are null', () => { - const callbackSpy = sinon.spy() - configHandlerFn(circTransEvent.records, null, null, callbackSpy) - - const errArg = callbackSpy.firstCall.args[0] - - expect(errArg).to.be.instanceOf(Error) - expect(errArg.message).to.equal('missing/undefined opts object configuration parameter') - }) - - it('should respond with a TransformerError if parameter options are empty', () => { - const callbackSpy = sinon.spy() - configHandlerFn(circTransEvent.records, {}, null, callbackSpy) - - const errArg = callbackSpy.firstCall.args[0] - - expect(callbackSpy.callCount).to.equal(1) - expect(errArg).to.be.instanceOf(Error) - expect(errArg.message).to.equal('missing/undefined opts object configuration parameter') - }) - - it('should respond with a TransformerError if nyplDataApiBaseUrl is missing', () => { - const callbackSpy = sinon.spy() - configHandlerFn( - circTransEvent.records, - { - schemaPath: 'path/to/schema', - schemaName: 'schema' - }, - null, - callbackSpy - ) - - const errArg = callbackSpy.firstCall.args[0] - - expect(callbackSpy.callCount).to.equal(1) - expect(errArg).to.be.instanceOf(Error) - expect(errArg.message).to.equal('missing/undefined nyplDataApiBaseUrl config parameter') - }) - - it('should respond with a TransformerError if nyplDataApiBaseUrl is not a string', () => { - const callbackSpy = sinon.spy() - configHandlerFn( - circTransEvent.records, - { - nyplDataApiBaseUrl: 192871, - schemaPath: 'path/to/schema', - schemaName: 'schema' - }, - null, - callbackSpy - ) - - const errArg = callbackSpy.firstCall.args[0] - - expect(callbackSpy.callCount).to.equal(1) - expect(errArg).to.be.instanceOf(Error) - expect(errArg.message).to.equal('missing/undefined nyplDataApiBaseUrl config parameter') - }) - - it('should respond with a TransformerError if nyplDataApiBaseUrl is empty', () => { - const callbackSpy = sinon.spy() - configHandlerFn( - circTransEvent.records, - { - nyplDataApiBaseUrl: '', - schemaPath: 'path/to/schema', - schemaName: 'schema' - }, - null, - callbackSpy - ) - - const errArg = callbackSpy.firstCall.args[0] - - expect(callbackSpy.callCount).to.equal(1) - expect(errArg).to.be.instanceOf(Error) - expect(errArg.message).to.equal('missing/undefined nyplDataApiBaseUrl config parameter') - }) - - it('should respond with a TransformerError if schemaPath is missing', () => { - const callbackSpy = sinon.spy() - configHandlerFn( - circTransEvent.records, - { - nyplDataApiBaseUrl: 'https://nypl.org/api/stuff', - schemaName: 'schema' - }, - null, - callbackSpy - ) - - const errArg = callbackSpy.firstCall.args[0] - - expect(callbackSpy.callCount).to.equal(1) - expect(errArg).to.be.instanceOf(Error) - expect(errArg.message).to.equal('missing/undefined schemaPath config parameter') - }) - - it('should respond with a TransformerError if schemaPath is not a string', () => { - const callbackSpy = sinon.spy() - configHandlerFn( - circTransEvent.records, - { - nyplDataApiBaseUrl: 'https://nypl.org/api/stuff', - schemaPath: 99817, - schemaName: 'schema' - }, - null, - callbackSpy - ) - - const errArg = callbackSpy.firstCall.args[0] - - expect(callbackSpy.callCount).to.equal(1) - expect(errArg).to.be.instanceOf(Error) - expect(errArg.message).to.equal('missing/undefined schemaPath config parameter') - }) - - it('should respond with a TransformerError if schemaPath is empty', () => { - const callbackSpy = sinon.spy() - configHandlerFn( - circTransEvent.records, - { - nyplDataApiBaseUrl: 'https://nypl.org/api/stuff', - schemaPath: '', - schemaName: 'schema' - }, - null, - callbackSpy - ) - - const errArg = callbackSpy.firstCall.args[0] - - expect(callbackSpy.callCount).to.equal(1) - expect(errArg).to.be.instanceOf(Error) - expect(errArg.message).to.equal('missing/undefined schemaPath config parameter') - }) - - it('should respond with a TransformerError if schemaName is missing', () => { - const callbackSpy = sinon.spy() - configHandlerFn( - circTransEvent.records, - { - nyplDataApiBaseUrl: 'https://nypl.org/api/stuff', - schemaPath: 'path/to/schema' - }, - null, - callbackSpy - ) - - const errArg = callbackSpy.firstCall.args[0] - - expect(callbackSpy.callCount).to.equal(1) - expect(errArg).to.be.instanceOf(Error) - expect(errArg.message).to.equal('missing/undefined schemaName config parameter') - }) - - it('should respond with a TransformerError if schemaName is not a string', () => { - const callbackSpy = sinon.spy() - configHandlerFn( - circTransEvent.records, - { - nyplDataApiBaseUrl: 'https://nypl.org/api/stuff', - schemaPath: 'path/to/schema', - schemaName: 52223 - }, - null, - callbackSpy - ) - - const errArg = callbackSpy.firstCall.args[0] - - expect(callbackSpy.callCount).to.equal(1) - expect(errArg).to.be.instanceOf(Error) - expect(errArg.message).to.equal('missing/undefined schemaName config parameter') - }) - - it('should respond with a TransformerError if schemaName is empty', () => { - const callbackSpy = sinon.spy() - configHandlerFn( - circTransEvent.records, - { - nyplDataApiBaseUrl: 'https://nypl.org/api/stuff', - schemaPath: 'path/to-schema', - schemaName: '' - }, - null, - callbackSpy - ) - - const errArg = callbackSpy.firstCall.args[0] - - expect(callbackSpy.callCount).to.equal(1) - expect(errArg).to.be.instanceOf(Error) - expect(errArg.message).to.equal('missing/undefined schemaName config parameter') - }) - - it('should respond with a TransformerError if outputFormat is not a string', () => { - const callbackSpy = sinon.spy() - configHandlerFn( - circTransEvent.records, - { - nyplDataApiBaseUrl: 'https://nypl.org/api/stuff', - schemaPath: 'path/to/schema', - schemaName: 'schema', - outputFormat: 1 - }, - null, - callbackSpy - ) - - const errArg = callbackSpy.firstCall.args[0] - - expect(callbackSpy.callCount).to.equal(1) - expect(errArg).to.be.instanceOf(Error) - expect(errArg.message).to.equal('missing/unsupported outputFormat config parameter') - }) - - it('should respond with a TransformerError if outputFormat is not json or csv', () => { - const callbackSpy = sinon.spy() - configHandlerFn( - circTransEvent.records, - { - nyplDataApiBaseUrl: 'https://nypl.org/api/stuff', - schemaPath: 'path/to/schema', - schemaName: 'schema', - outputFormat: 'bad format' - }, - null, - callbackSpy - ) - - const errArg = callbackSpy.firstCall.args[0] - - expect(callbackSpy.callCount).to.equal(1) - expect(errArg).to.be.instanceOf(Error) - expect(errArg.message).to.equal('missing/unsupported outputFormat config parameter') - }) - }) - - describe('Records Handler: exports.recordsHandler(records, opts, context, callback)', () => { - let mock - beforeEach(() => { - mock = new MockAdapter(axios) - }) - - afterEach(() => { - mock.reset() - }) - - it('should be a function', () => { - expect(recordsHandlerFn).to.be.a('function') - }) - - it('should reject an improper response from schema retrieval promise', async () => { - await expect(recordsHandlerFn( - circTransEvent.records, - { - nyplDataApiBaseUrl: 'https://nyplurl.org', - schemaPath: 'current-schemas/', - schemaName: 'circTrans' - }, - null, - null - )).to.be.rejected - }) - - it('should handle a CircTrans batch', async () => { - mock.onGet().reply( - 200, - JSON.parse(fs.readFileSync('./test/stubs/CircTrans-schema-response.json', 'utf8')) - ) - - const result = await new Promise((resolve, reject) => { - recordsHandlerFn( - circTransEvent.records, - { - nyplDataApiBaseUrl: 'https://nyplurl.org', - schemaPath: 'current-schemas/', - schemaName: 'CircTrans' - }, - null, - (e, result) => { - if (e) return reject(e) - return resolve(result) - }) - }) - - expect(result).to.be.a('object') - expect(result.records).to.be.a('array') - expect(result.records).to.have.lengthOf(3) - result.records.forEach((record) => { - expect(record).to.be.a('object') - expect(record.data).to.be.a('string') - expect(decodeBase64Json(record.data)).to.be.a('object') - expect(decodeBase64Json(record.data).op_code).to.be.a('string') - }) - }) - - it('should handle a PcReserve batch', async () => { - mock.onGet().reply( - 200, - JSON.parse(fs.readFileSync('./test/stubs/PcReserve-schema-response.json', 'utf8')) - ) - - const result = await new Promise((resolve, reject) => { - recordsHandlerFn( - pcReserveEvent.records, - { - nyplDataApiBaseUrl: 'https://nyplurl.org', - schemaPath: 'current-schemas/', - schemaName: 'PcReserve' - }, - null, - (e, result) => { - if (e) return reject(e) - return resolve(result) - }) - }) - - expect(result).to.be.a('object') - expect(result.records).to.be.a('array') - expect(result.records).to.have.lengthOf(3) - result.records.forEach((record) => { - expect(record).to.be.a('object') - expect(record.data).to.be.a('string') - expect(decodeBase64Json(record.data)).to.be.a('object') - expect(decodeBase64Json(record.data).patron_id).to.be.a('string') - }) - }) - }) - - describe('schemaNameFromEvent', () => { - it('should return CircTrans for CircTransAnon-production stream', () => { - const schemaName = AvroToJsonTransformer.schemaNameFromEvent({ - sourceKinesisStreamArn: 'arn:aws:kinesis:stream/CircTransAnon-production' - }) - expect(schemaName).to.equal('CircTrans') - }) - - it('should return PcReserve for PcReserve-production stream', () => { - const schemaName = AvroToJsonTransformer.schemaNameFromEvent({ - sourceKinesisStreamArn: 'arn:aws:kinesis:stream/PcReserve-production' - }) - expect(schemaName).to.equal('PcReserve') - }) - }) -}) diff --git a/test/helpers/ErrorHelper.test.js b/test/helpers/ErrorHelper.test.js deleted file mode 100644 index a881f39..0000000 --- a/test/helpers/ErrorHelper.test.js +++ /dev/null @@ -1,34 +0,0 @@ -const TransformerError = require('../../src/helpers/ErrorHelper.js') - -describe('AvroToJsonTransformer Lambda: TransformerError', () => { - it('should throw an error when message variable is missing', () => { - expect(() => new TransformerError()).to.throw(/an error message is required/) - expect(() => new TransformerError(null, {})).to.throw(/an error message is required/) - expect(() => new TransformerError({})).to.throw(/an error message is required/) - }) - - it('should return the error message when given', () => { - const err = new TransformerError('an error occurred') - expect(err.message).to.equal('an error occurred') - }) - - it('should return the error type when given', () => { - const err = new TransformerError('an error occurred', { type: 'internal-error' }) - expect(err.type).to.equal('internal-error') - }) - - it('should return the statusCode when given', () => { - const err = new TransformerError('an error occurred', { statusCode: 500 }) - expect(err.statusCode).to.equal(500) - }) - - it('should return the debugInfo when given', () => { - const err = new TransformerError('an error occurred', { debugInfo: 'schemaFetch is not a function' }) - expect(err.debugInfo).to.equal('schemaFetch is not a function') - }) - - it('should return the constructor name assigned', () => { - const err = new TransformerError('an error occurred') - expect(err.name).to.equal('TransformerError') - }) -}) diff --git a/test/helpers/RecordsProcessor.test.js b/test/helpers/RecordsProcessor.test.js deleted file mode 100644 index 444d902..0000000 --- a/test/helpers/RecordsProcessor.test.js +++ /dev/null @@ -1,26 +0,0 @@ -const fs = require('fs') - -const { processRecords } = require('../../src/helpers/RecordsProcessor.js') - -const schema = JSON.parse(fs.readFileSync('./test/stubs/CircTrans.json', 'utf8')) -const records = JSON.parse(fs.readFileSync('./test/stubs/records.json', 'utf8')) - -describe('Records Processor: processRecords(schema, records)', () => { - it('should return JSON array of 3 sample objects; 2 successes and 1 failure', () => { - const result = processRecords(schema, records.records) - - let jsn = false - const buf = Buffer.from(result[0].data, 'base64') - if (JSON.parse(buf)) { - jsn = true - } - - expect(Array.isArray(result)).to.equal(true) - expect(jsn).to.equal(true) - expect(result.length).to.equal(3) - expect(result[0].recordId).to.be.equal('789') - expect(result[0].result).to.equal('Ok') - expect(result[0].data).to.be.a('string') - expect(result[2].result).to.equal('ProcessingFailed') - }) -}) diff --git a/test/helpers/SchemaHandler.test.js b/test/helpers/SchemaHandler.test.js deleted file mode 100644 index 22e49d4..0000000 --- a/test/helpers/SchemaHandler.test.js +++ /dev/null @@ -1,100 +0,0 @@ -const axios = require('axios') -const MockAdapter = require('axios-mock-adapter') - -const { schemaHandler, fetchSchema } = require('../../src/helpers/SchemaHandler.js') -const TransformerError = require('../../src/helpers/ErrorHelper.js') - -describe('AvroToJsonTransformer Lambda: SchemaHandler', () => { - let mock - - beforeEach(() => { - mock = new MockAdapter(axios) - }) - - afterEach(() => { - mock.reset() - }) - - describe('schemaHandler(cachedSchema, fetchSchemaFn)', () => { - it('should not fetch a new schema if cachedSchema contains one', () => { - const result = schemaHandler('cachedSchemaJson', 'fetchSchemaFn') - return result.should.be.fulfilled.and.should.eventually.deep.equal({ - schemaType: 'cached-schema', - schema: 'cachedSchemaJson' - }) - }) - - it('should fetch a new schema when cachedSchema is null', () => { - mock.onGet().reply( - 200, - { - data: { - schema: '{ "name": "circTrans" }' - } - } - ) - - const result = schemaHandler(null, fetchSchema('http://nypltest.org', 'schema-path', 'schema')) - - return result.should.be.fulfilled.and.should.eventually.deep.equal({ - schemaType: 'fresh-schema', - schema: JSON.parse('{ "name": "circTrans" }') - }) - }) - - it('should reject the promise on 400 responses from the API', () => { - mock.onGet().reply(404) - - const result = schemaHandler(null, fetchSchema('http://nypltest.org', 'schema-path', 'schema')) - - return result.should.be.rejected.and.should.eventually.have.property('statusCode', 404) - }) - - it('should reject the promise on 500 responses from the API', () => { - mock.onGet().reply(503) - - const result = schemaHandler(null, fetchSchema('http://nypltest.org', 'schema-path', 'schema')) - - return result.should.be.rejected.and.should.eventually.have.property('statusCode', 503) - }) - }) - - describe('fetchSchema(url, path, name)', () => { - it('should reject the promise if the url parameter is undefined', () => { - const result = fetchSchema(null, 'path', 'name') - return result.should.be.rejectedWith(TransformerError, 'missing one or more URL parameters') - }) - - it('should reject the promise if the path parameter is undefined', () => { - const result = fetchSchema('url', null, 'name') - return result.should.be.rejectedWith(TransformerError, 'missing one or more URL parameters') - }) - - it('should reject the promise if the name parameter is undefined', () => { - const result = fetchSchema('url', 'path', null) - return result.should.be.rejectedWith(TransformerError, 'missing one or more URL parameters') - }) - - it('should reject the promise if the response is empty', () => { - mock.onGet().reply( - 200, - {} - ) - - const result = fetchSchema('http://nypltest.org', 'schema-path', 'schema') - return result.should.be.rejectedWith(TransformerError, 'Schema object could not be retrieved') - }) - - it('should reject the promise if the schema object is undefined', () => { - mock.onGet().reply( - 200, - { - data: {} - } - ) - - const result = fetchSchema('http://nypltest.org', 'schema-path', 'schema') - return result.should.be.rejectedWith(TransformerError, 'Schema object could not be retrieved') - }) - }) -}) diff --git a/test/stubs/CircTrans-schema-response.json b/test/stubs/CircTrans-schema-response.json deleted file mode 100644 index 156b4ea..0000000 --- a/test/stubs/CircTrans-schema-response.json +++ /dev/null @@ -1,153 +0,0 @@ -{ - "data" : { - "offsetEnd" : 0, - "id" : 3, - "offsetBegin" : 0, - "schemaObject" : { - "fields" : [ - { - "type" : [ - "string" - ], - "doc" : "uniqure processing row uuid, auto-generated and random", - "name" : "uuid" - }, - { - "name" : "transaction_checksum", - "type" : [ - "string" - ], - "doc" : "Obfuscated unique transaction checksum of concatenated transaction information" - }, - { - "name" : "patron_id", - "type" : [ - "string" - ], - "doc" : "Obfuscated patron id" - }, - { - "type" : [ - "null", - "int" - ], - "doc" : "Patron ptype code", - "name" : "ptype_code" - }, - { - "name" : "patron_home_library_code", - "type" : [ - "null", - "string" - ], - "doc" : "A five-character location code, right-padded with spaces, from the associated patron record." - }, - { - "doc" : "Patron 'home region' code", - "type" : [ - "null", - "int" - ], - "name" : "pcode3" - }, - { - "doc" : "Patron postal code", - "type" : [ - "null", - "string" - ], - "name" : "postal_code" - }, - { - "doc" : "Patron census tract", - "type" : [ - "null", - "string" - ], - "name" : "geoid" - }, - { - "type" : [ - "null", - "int" - ], - "doc" : "Item type code", - "name" : "itype_code_num" - }, - { - "name" : "item_location_code", - "doc" : "A five-character location code, right-padded with spaces, from the associated item record.", - "type" : [ - "null", - "string" - ] - }, - { - "name" : "icode1", - "doc" : "Item code 1", - "type" : [ - "null", - "int" - ] - }, - { - "name" : "op_code", - "type" : [ - "null", - "string" - ], - "doc" : "Type of transaction: `o` (checkout), `i` (checkin), `n` (hold), `nb` (bib hold), `ni` (item hold), `nv` (volume hold), `h` (hold with recall), `hb` (hold recall bib), `hi` (hold recall item), `hv` (hold recall volume), `f` (filled hold), `r` (renewal), `b` (booking), `u` (use count)" - }, - { - "name" : "transaction_et", - "type" : [ - "null", - "string" - ], - "doc" : "Transaction date (ISO 8601 date without time in ET)" - }, - { - "name" : "due_date_et", - "type" : [ - "null", - "string" - ], - "doc" : "Due date (ISO 8601 date without time in ET) The application of this date depends on the `op_code` for the transaction. The due date is not included for bookings (`op_code` `b`) or filled holds (`op_code` `f`). For `op_code` `i` (checkin), this is the original due date. For `op_code` `r` (renewal), this is the renewal due date. For `op_code` `o` (checkouts), this is the item due date. For `op_code` `n` (holds) and `h` (holds with recall), a non-zero entry indicates that the hold is for a checked-out item that is due on the specified date." - }, - { - "name" : "application_name", - "doc" : "The name of the program that generated the transaction. Valid program names are: `circ` (includes transactions made using PC Circ), `circa` (for transactions written by selfcheckwebserver and in-house use [transaction codes 'u' and 's'], which use webpac to execute transactions.) `milcirc`, ` milmyselfcheck`, `readreq`, `selfcheck`", - "type" : "string" - }, - { - "name" : "stat_group_code_num", - "type" : [ - "null", - "int" - ], - "doc" : "The number of the terminal at which the transaction occurred or the user-specified statistics group number for PC-Circ transactions. Also stores the login's statistics group number for circulation transactions performed with the following Circa applications: checkout checkin count internal use" - }, - { - "name" : "loanrule_code_num", - "type" : [ - "null", - "int" - ], - "doc" : "Indicates loan rule governing transaction" - }, - { - "name" : "source_code", - "type" : "string", - "doc" : "The transaction source. Possible values are: local INN-Reach ILL" - } - ], - "type" : "record", - "name" : "CircTrans" - }, - "stream" : "CircTrans", - "schema" : "{\"fields\":[{\"doc\":\"uniqure processing row uuid, auto-generated and random\",\"name\":\"uuid\",\"type\":[\"string\"]},{\"doc\":\"Obfuscated unique transaction checksum of concatenated transaction information\",\"name\":\"transaction_checksum\",\"type\":[\"string\"]},{\"doc\":\"Obfuscated patron id\",\"name\":\"patron_id\",\"type\":[\"string\"]},{\"doc\":\"Patron ptype code\",\"name\":\"ptype_code\",\"type\":[\"null\",\"int\"]},{\"doc\":\"A five-character location code, right-padded with spaces, from the associated patron record.\",\"name\":\"patron_home_library_code\",\"type\":[\"null\",\"string\"]},{\"doc\":\"Patron 'home region' code\",\"name\":\"pcode3\",\"type\":[\"null\",\"int\"]},{\"doc\":\"Patron postal code\",\"name\":\"postal_code\",\"type\":[\"null\",\"string\"]},{\"doc\":\"Patron census tract\",\"name\":\"geoid\",\"type\":[\"null\",\"string\"]},{\"doc\":\"Item type code\",\"name\":\"itype_code_num\",\"type\":[\"null\",\"int\"]},{\"doc\":\"A five-character location code, right-padded with spaces, from the associated item record.\",\"name\":\"item_location_code\",\"type\":[\"null\",\"string\"]},{\"doc\":\"Item code 1\",\"name\":\"icode1\",\"type\":[\"null\",\"int\"]},{\"doc\":\"Type of transaction: `o` (checkout), `i` (checkin), `n` (hold), `nb` (bib hold), `ni` (item hold), `nv` (volume hold), `h` (hold with recall), `hb` (hold recall bib), `hi` (hold recall item), `hv` (hold recall volume), `f` (filled hold), `r` (renewal), `b` (booking), `u` (use count)\",\"name\":\"op_code\",\"type\":[\"null\",\"string\"]},{\"doc\":\"Transaction date (ISO 8601 date without time in ET)\",\"name\":\"transaction_et\",\"type\":[\"null\",\"string\"]},{\"doc\":\"Due date (ISO 8601 date without time in ET) The application of this date depends on the `op_code` for the transaction. The due date is not included for bookings (`op_code` `b`) or filled holds (`op_code` `f`). For `op_code` `i` (checkin), this is the original due date. For `op_code` `r` (renewal), this is the renewal due date. For `op_code` `o` (checkouts), this is the item due date. For `op_code` `n` (holds) and `h` (holds with recall), a non-zero entry indicates that the hold is for a checked-out item that is due on the specified date.\",\"name\":\"due_date_et\",\"type\":[\"null\",\"string\"]},{\"doc\":\"The name of the program that generated the transaction. Valid program names are: `circ` (includes transactions made using PC Circ), `circa` (for transactions written by selfcheckwebserver and in-house use [transaction codes 'u' and 's'], which use webpac to execute transactions.) `milcirc`, ` milmyselfcheck`, `readreq`, `selfcheck`\",\"name\":\"application_name\",\"type\":\"string\"},{\"doc\":\"The number of the terminal at which the transaction occurred or the user-specified statistics group number for PC-Circ transactions. Also stores the login's statistics group number for circulation transactions performed with the following Circa applications: checkout checkin count internal use\",\"name\":\"stat_group_code_num\",\"type\":[\"null\",\"int\"]},{\"doc\":\"Indicates loan rule governing transaction\",\"name\":\"loanrule_code_num\",\"type\":[\"null\",\"int\"]},{\"doc\":\"The transaction source. Possible values are: local INN-Reach ILL\",\"name\":\"source_code\",\"type\":\"string\"}],\"name\":\"CircTrans\",\"type\":\"record\"}" - }, - "statusCode" : 200, - "count" : 1, - "debugInfo" : [] -} diff --git a/test/stubs/CircTrans.json b/test/stubs/CircTrans.json deleted file mode 100644 index a821666..0000000 --- a/test/stubs/CircTrans.json +++ /dev/null @@ -1,196 +0,0 @@ -{ - "name": "CircTransRecord", - "type": "record", - "fields": [ - { - "name": "id", - "type": "int", - "doc": "System-generated sequential ID." - }, - { - "name": "patron_id", - "type": [ - "null", - "int" - ], - "doc": "De-identified Patron ID for record" - }, - { - "name": "item_id", - "type": [ - "null", - "int" - ], - "doc": "Item ID for record" - }, - { - "name": "volume_id", - "type": [ - "null", - "int" - ], - "doc": "Volume ID for record" - }, - { - "name": "bib_id", - "type": [ - "null", - "int" - ], - "doc": "Bib ID for record" - }, - { - "name": "transaction_gmt", - "type": [ - "null", - "string" - ], - "doc": "Transaction date in UNIX format." - }, - { - "name": "application_name", - "type": "string", - "doc": "The name of the program that generated the transaction. Valid program names are: circ (includes transactions made using PC Circ) circa (for transactions written by selfcheckwebserver and in-house use [transaction codes 'u' and 's'], which use webpac to execute transactions.) milcirc milmyselfcheck readreq selfcheck" - }, - { - "name": "source_code", - "type": "string", - "doc": "The transaction source. Possible values are: local INN-Reach ILL" - }, - { - "name": "op_code", - "type": [ - "null", - "string" - ], - "doc": "Type of transaction: o = checkout i = checkin n = hold nb = bib hold ni = item hold nv = volume hold h = hold with recall hb = hold recall bib hi = hold recall item hv = hold recall volume f = filled hold r = renewal b = booking u = use count" - }, - { - "name": "stat_group_code_num", - "type": [ - "null", - "int" - ], - "doc": "The number of the terminal at which the transaction occurred or the user-specified statistics group number for PC-Circ transactions. Also stores the login's statistics group number for circulation transactions performed with the following Circa applications: checkout checkin count internal use" - }, - { - "name": "due_date_gmt", - "type": [ - "null", - "string" - ], - "doc": "Due date in UNIX format. The application of this date depends on the op_code for the transaction. The due date is not included for bookings (op_code b) or filled holds (op_code f). For op_code 'i' (checkin), this is the original due date. For op_code 'r' (renewal), this is the renewal due date. For op_code 'o' (checkouts), this is the item due date. For op_codes 'n' (holds) and 'h' (holds with recall), a non-zero entry indicates that the hold is for a checked-out item that is due on the specified date." - }, - { - "name": "count_type_code_num", - "type": [ - "null", - "int" - ], - "doc": "Indicates the type of use count (for op_code 'u'): Code Number Count Type 1 INTL USE (fixflds 93) 2 COPY USE (fixflds 94) 3 IUSE3 (fixflds 74) 4 PIUSE: generated by the system" - }, - { - "name": "itype_code_num", - "type": [ - "null", - "int" - ], - "doc": "Item type code. (Defined by the library.)" - }, - { - "name": "icode1", - "type": [ - "null", - "int" - ], - "doc": "Item code 1. (Defined by the library.)" - }, - { - "name": "icode2", - "type": [ - "null", - "string" - ], - "doc": "Item code 2. (Defined by the library.)" - }, - { - "name": "item_location_code", - "type": [ - "null", - "string" - ], - "doc": "A five-character location code, right-padded with spaces, from the associated item record." - }, - { - "name": "item_agency_code_num", - "type": [ - "null", - "int" - ], - "doc": "A one-character AGENCY code from the associated item record." - }, - { - "name": "ptype_code", - "type": [ - "null", - "string" - ], - "doc": "Patron type code. (Defined by the library.)" - }, - { - "name": "pcode1", - "type": [ - "null", - "string" - ], - "doc": "Patron code 1. (Defined by the library.)" - }, - { - "name": "pcode2", - "type": [ - "null", - "string" - ], - "doc": "Patron code 2. (Defined by the library.)" - }, - { - "name": "pcode3", - "type": [ - "null", - "int" - ], - "doc": "Patron code 3. (Defined by the library.)" - }, - { - "name": "pcode4", - "type": [ - "null", - "int" - ], - "doc": "Patron code 4. (Defined by the library.)" - }, - { - "name": "patron_home_library_code", - "type": [ - "null", - "string" - ], - "doc": "A five-character location code, right-padded with spaces, from the associated patron record." - }, - { - "name": "patron_agency_code_num", - "type": [ - "null", - "int" - ], - "doc": "A one-character AGENCY code from the associated patron record." - }, - { - "name": "loanrule_code_num", - "type": [ - "null", - "int" - ] - } - ] -} diff --git a/test/stubs/LocationHours-schema-response.json b/test/stubs/LocationHours-schema-response.json deleted file mode 100644 index 9ec368a..0000000 --- a/test/stubs/LocationHours-schema-response.json +++ /dev/null @@ -1,62 +0,0 @@ -{ - "data": { - "id": 12, - "offsetBegin": 0, - "offsetEnd": 0, - "schemaObject": { - "name": "LocationHours", - "type": "record", - "fields": [ - { - "name": "drupal_location_id", - "doc": "The library's id as stored in Drupal", - "type": [ - "string" - ] - }, - { - "name": "name", - "doc": "The library's full name", - "type": [ - "string" - ] - }, - { - "name": "weekday", - "doc": "Day of the week for which the hours apply", - "type": [ - "string" - ] - }, - { - "name": "open", - "doc": "Opening time (hh:mm) for the library in ET", - "type": [ - "null", - "string" - ] - }, - { - "name": "close", - "doc": "Closing time (hh:mm) for the library in ET", - "type": [ - "null", - "string" - ] - }, - { - "name": "date_of_change", - "doc": "Date (ISO 8601 date without time in ET) at which the regular hours for the given day changed", - "type": [ - "string" - ] - } - ] - }, - "stream": "LocationHours", - "schema": "{\"name\":\"LocationHours\",\"type\":\"record\",\"fields\":[{\"name\":\"drupal_location_id\",\"doc\":\"The library's id as stored in Drupal\",\"type\":[\"string\"]},{\"name\":\"name\",\"doc\":\"The library's full name\",\"type\":[\"string\"]},{\"name\":\"weekday\",\"doc\":\"Day of the week for which the hours apply\",\"type\":[\"string\"]},{\"name\":\"open\",\"doc\":\"Opening time (hh:mm) for the library in ET\",\"type\":[\"null\",\"string\"]},{\"name\":\"close\",\"doc\":\"Closing time (hh:mm) for the library in ET\",\"type\":[\"null\",\"string\"]},{\"name\":\"date_of_change\",\"doc\":\"Date (ISO 8601 date without time in ET) at which the regular hours for the given day changed\",\"type\":[\"string\"]}]}" - }, - "count": 1, - "statusCode": 200, - "debugInfo": [] -} \ No newline at end of file diff --git a/test/stubs/PatronInfo-schema-response.json b/test/stubs/PatronInfo-schema-response.json deleted file mode 100644 index 96fa640..0000000 --- a/test/stubs/PatronInfo-schema-response.json +++ /dev/null @@ -1 +0,0 @@ -{"data":{"id":11,"offsetBegin":0,"offsetEnd":0,"schemaObject":{"name":"PatronInfo","type":"record","fields":[{"name":"patron_id","doc":"Obfuscated patron record id","type":["string"]},{"name":"address_hash","doc":"Obfuscated concatenation of patron record id, address, city, region, and postal code","type":["null","string"]},{"name":"postal_code","doc":"Patron five-digit postal code","type":["null","string"]},{"name":"geoid","doc":"11-digit geoid, also known as a census tract","type":["null","string"]},{"name":"creation_date_et","doc":"Date (ISO 8601 date without time in ET) of the patron record's creation","type":["null","string"]},{"name":"deletion_date_et","doc":"Date (ISO 8601 date without time in ET) of the patron record's deletion. Null if the patron is still active.","type":["null","string"]},{"name":"circ_active_date_et","doc":"Date (ISO 8601 date without time in ET) of the patron's last circulation activity","type":["null","string"]},{"name":"ptype_code","doc":"Patron ptype code","type":["null","int"]},{"name":"pcode3","doc":"Patron 'home region' code","type":["null","int"]},{"name":"patron_home_library_code","doc":"A five-character location code, right-padded with spaces, from the associated patron record","type":["null","string"]}]},"stream":"PatronInfo","schema":"{\"name\":\"PatronInfo\",\"type\":\"record\",\"fields\":[{\"name\":\"patron_id\",\"doc\":\"Obfuscated patron record id\",\"type\":[\"string\"]},{\"name\":\"address_hash\",\"doc\":\"Obfuscated concatenation of patron record id, address, city, region, and postal code\",\"type\":[\"null\",\"string\"]},{\"name\":\"postal_code\",\"doc\":\"Patron five-digit postal code\",\"type\":[\"null\",\"string\"]},{\"name\":\"geoid\",\"doc\":\"11-digit geoid, also known as a census tract\",\"type\":[\"null\",\"string\"]},{\"name\":\"creation_date_et\",\"doc\":\"Date (ISO 8601 date without time in ET) of the patron record's creation\",\"type\":[\"null\",\"string\"]},{\"name\":\"deletion_date_et\",\"doc\":\"Date (ISO 8601 date without time in ET) of the patron record's deletion. Null if the patron is still active.\",\"type\":[\"null\",\"string\"]},{\"name\":\"circ_active_date_et\",\"doc\":\"Date (ISO 8601 date without time in ET) of the patron's last circulation activity\",\"type\":[\"null\",\"string\"]},{\"name\":\"ptype_code\",\"doc\":\"Patron ptype code\",\"type\":[\"null\",\"int\"]},{\"name\":\"pcode3\",\"doc\":\"Patron 'home region' code\",\"type\":[\"null\",\"int\"]},{\"name\":\"patron_home_library_code\",\"doc\":\"A five-character location code, right-padded with spaces, from the associated patron record\",\"type\":[\"null\",\"string\"]}]}"},"count":1,"statusCode":200,"debugInfo":[]} \ No newline at end of file diff --git a/test/stubs/PcReserve-schema-response.json b/test/stubs/PcReserve-schema-response.json deleted file mode 100644 index a1d59fa..0000000 --- a/test/stubs/PcReserve-schema-response.json +++ /dev/null @@ -1 +0,0 @@ -{"data":{"id":10,"offsetBegin":0,"offsetEnd":0,"schemaObject":{"name":"PcReserve","type":"record","fields":[{"name":"patron_id","type":["string"],"doc":"Obfuscated patron id"},{"name":"patron_retrieval_status","doc":"Status of patron record retrieval (found, missing, guest pass)","type":["null","string"]},{"name":"ptype_code","doc":"Patron ptype code","type":["null","int"]},{"doc":"A five-character location code, right-padded with spaces, from the associated patron record.","name":"patron_home_library_code","type":["null","string"]},{"name":"pcode3","doc":"Patron 'home region' code","type":["null","int"]},{"name":"postal_code","doc":"Patron postal code","type":["null","string"]},{"name":"geoid","type":["null","string"],"doc":"Patron census tract"},{"name":"key","doc":"Obfuscated crKey","type":["string"]},{"name":"minutes_used","type":["null","int"],"doc":"minutes_used"},{"name":"transaction_et","doc":"Transaction date (ISO 8601 date without time in ET)","type":["null","string"]},{"name":"branch","type":["null","string"],"doc":"pcrBranch"},{"name":"area","type":["null","string"],"doc":"pcrArea"},{"name":"staff_override","type":["null","string"],"doc":"pcrUserData1"}]},"stream":"PcReserve","schema":"{\"name\":\"PcReserve\",\"type\":\"record\",\"fields\":[{\"name\":\"patron_id\",\"type\":[\"string\"],\"doc\":\"Obfuscated patron id\"},{\"name\":\"patron_retrieval_status\",\"doc\":\"Status of patron record retrieval (found, missing, guest pass)\",\"type\":[\"null\",\"string\"]},{\"name\":\"ptype_code\",\"doc\":\"Patron ptype code\",\"type\":[\"null\",\"int\"]},{\"doc\":\"A five-character location code, right-padded with spaces, from the associated patron record.\",\"name\":\"patron_home_library_code\",\"type\":[\"null\",\"string\"]},{\"name\":\"pcode3\",\"doc\":\"Patron 'home region' code\",\"type\":[\"null\",\"int\"]},{\"name\":\"postal_code\",\"doc\":\"Patron postal code\",\"type\":[\"null\",\"string\"]},{\"name\":\"geoid\",\"type\":[\"null\",\"string\"],\"doc\":\"Patron census tract\"},{\"name\":\"key\",\"doc\":\"Obfuscated crKey\",\"type\":[\"string\"]},{\"name\":\"minutes_used\",\"type\":[\"null\",\"int\"],\"doc\":\"minutes_used\"},{\"name\":\"transaction_et\",\"doc\":\"Transaction date (ISO 8601 date without time in ET)\",\"type\":[\"null\",\"string\"]},{\"name\":\"branch\",\"type\":[\"null\",\"string\"],\"doc\":\"pcrBranch\"},{\"name\":\"area\",\"type\":[\"null\",\"string\"],\"doc\":\"pcrArea\"},{\"name\":\"staff_override\",\"type\":[\"null\",\"string\"],\"doc\":\"pcrUserData1\"}]}"},"count":1,"statusCode":200,"debugInfo":[]} \ No newline at end of file diff --git a/test/stubs/records.json b/test/stubs/records.json deleted file mode 100644 index 805cc47..0000000 --- a/test/stubs/records.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "records": [ - { - "recordId": "789", - "data": "oAICoAICoAICoAICoAICLDIwMTctMTEtMTQgMTE6NDM6NDktMDUMc2llcnJhCmxvY2FsAgJvAhICLDIwMTctMTItMDUgMDQ6MDA6MDAtMDUCAAKUAgIAAgItAgpld2EwbgIAAgQxMAICLQICLQICAgACCmV3ICAgAgACCA==", - "approximateArrivalTimestamp": "2012-04-23T18:25:43.511Z" - }, - { - "recordId": "123", - "data": "oAICoAICoAICoAICoAICLDIwMTctMTEtMTQgMTE6NDM6NTAtMDUMc2llcnJhCmxvY2FsAgJvAlgCLDIwMTctMTItMDUgMDQ6MDA6MDAtMDUCAAKSAwIAAgItAgpmd2owYQIAAgQ2MAICLQICcgIEAgACCmZ3ICAgAgACCg==", - "approximateArrivalTimestamp": "2012-04-23T18:25:43.511Z" - }, - { - "recordId": "456", - "data": "lgsCSDlhNmZiYmU5LWJkMTAtNDA2Ny05ZmVhLWEwODM4ZGU2YzUyNwIGNzE1Ah4yMzQ1Njc4OTA5ODc2NTQCHDMyMTAxMDk2MTE1MjE1AgZQVUwAAAIyMjAxNy0xMC0wNFQxNjo0MToyNS0wNDowMAA=", - "approximateArrivalTimestamp": "2012-04-23T18:25:43.511Z" - } - ], - "region": "us-east-1", - "deliveryStreamArn": "arn:aws:kinesis:EXAMPLE", - "invocationId": "invocationIdExample" -} - - diff --git a/test/test-helper.js b/test/test-helper.js deleted file mode 100644 index 87f0112..0000000 --- a/test/test-helper.js +++ /dev/null @@ -1,50 +0,0 @@ -const chai = require('chai') -const chaiAsPromised = require('chai-as-promised') -const sinonChai = require('sinon-chai') - -chai.should() -chai.use(sinonChai) -chai.use(chaiAsPromised) - -global.expect = chai.expect - -let envCache = {} - -/** - * Stores the given values in process.env - */ -function setEnv (hash) { - Object.keys(hash).forEach((k) => { - envCache[k] = process.env[k] - process.env[k] = hash[k] - }) -} - -/** - * After calling `setEnv`, use this to restore process.env to the former state - */ -function restoreEnv () { - Object.keys(envCache).forEach((k) => { - if (typeof envCache[k] === 'undefined') { - delete process.env[k] - } else { - process.env[k] = envCache[k] - } - }) - envCache = {} -} - -/** - * Given a base64 encoded, stringified JSON, returns the JSON object. - */ -// const decodeBase64Json = (v) => JSON.parse(Buffer.from(v, 'base64')) -const decodeBase64Json = (v) => { - const s = Buffer.from(v, 'base64').toString('utf8') - return JSON.parse(s) -} - -module.exports = { - setEnv, - restoreEnv, - decodeBase64Json -} diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/conftest.py b/tests/conftest.py new file mode 100644 index 0000000..9851041 --- /dev/null +++ b/tests/conftest.py @@ -0,0 +1,33 @@ +import json +import os +import pytest + +# Sets OS vars for entire set of tests +TEST_ENV_VARS = { + "ENVIRONMENT": "test", + "NYPL_DATA_API_BASE_URL": "https://qa-platform.nypl.org/api/v0.1/current-schemas/", +} + + +@pytest.fixture +def test_data(): + test_data_directory = "tests/stubs" + test_data = {} + + for file in os.listdir(test_data_directory): + with open(f"{test_data_directory}/{file}", "r") as f: + key = file.split(".")[0] + test_data[key] = json.load(f) + return test_data + + +@pytest.fixture(scope="session", autouse=True) +def tests_setup_and_teardown(): + # Will be executed before the first test + os.environ.update(TEST_ENV_VARS) + + yield + + # Will execute after final test + for os_config in TEST_ENV_VARS.keys(): + del os.environ[os_config] diff --git a/tests/stubs/CircTrans.json b/tests/stubs/CircTrans.json new file mode 100644 index 0000000..1f92f6a --- /dev/null +++ b/tests/stubs/CircTrans.json @@ -0,0 +1,196 @@ +{ + "name": "CircTransRecord", + "type": "record", + "fields": [ + { + "name": "id", + "type": "int", + "doc": "System-generated sequential ID." + }, + { + "name": "patron_id", + "type": [ + "null", + "int" + ], + "doc": "De-identified Patron ID for record" + }, + { + "name": "item_id", + "type": [ + "null", + "int" + ], + "doc": "Item ID for record" + }, + { + "name": "volume_id", + "type": [ + "null", + "int" + ], + "doc": "Volume ID for record" + }, + { + "name": "bib_id", + "type": [ + "null", + "int" + ], + "doc": "Bib ID for record" + }, + { + "name": "transaction_gmt", + "type": [ + "null", + "string" + ], + "doc": "Transaction date in UNIX format." + }, + { + "name": "application_name", + "type": "string", + "doc": "The name of the program that generated the transaction. Valid program names are: circ (includes transactions made using PC Circ) circa (for transactions written by selfcheckwebserver and in-house use [transaction codes 'u' and 's'], which use webpac to execute transactions.) milcirc milmyselfcheck readreq selfcheck" + }, + { + "name": "source_code", + "type": "string", + "doc": "The transaction source. Possible values are: local INN-Reach ILL" + }, + { + "name": "op_code", + "type": [ + "null", + "string" + ], + "doc": "Type of transaction: o = checkout i = checkin n = hold nb = bib hold ni = item hold nv = volume hold h = hold with recall hb = hold recall bib hi = hold recall item hv = hold recall volume f = filled hold r = renewal b = booking u = use count" + }, + { + "name": "stat_group_code_num", + "type": [ + "null", + "int" + ], + "doc": "The number of the terminal at which the transaction occurred or the user-specified statistics group number for PC-Circ transactions. Also stores the login's statistics group number for circulation transactions performed with the following Circa applications: checkout checkin count internal use" + }, + { + "name": "due_date_gmt", + "type": [ + "null", + "string" + ], + "doc": "Due date in UNIX format. The application of this date depends on the op_code for the transaction. The due date is not included for bookings (op_code b) or filled holds (op_code f). For op_code 'i' (checkin), this is the original due date. For op_code 'r' (renewal), this is the renewal due date. For op_code 'o' (checkouts), this is the item due date. For op_codes 'n' (holds) and 'h' (holds with recall), a non-zero entry indicates that the hold is for a checked-out item that is due on the specified date." + }, + { + "name": "count_type_code_num", + "type": [ + "null", + "int" + ], + "doc": "Indicates the type of use count (for op_code 'u'): Code Number Count Type 1 INTL USE (fixflds 93) 2 COPY USE (fixflds 94) 3 IUSE3 (fixflds 74) 4 PIUSE: generated by the system" + }, + { + "name": "itype_code_num", + "type": [ + "null", + "int" + ], + "doc": "Item type code. (Defined by the library.)" + }, + { + "name": "icode1", + "type": [ + "null", + "int" + ], + "doc": "Item code 1. (Defined by the library.)" + }, + { + "name": "icode2", + "type": [ + "null", + "string" + ], + "doc": "Item code 2. (Defined by the library.)" + }, + { + "name": "item_location_code", + "type": [ + "null", + "string" + ], + "doc": "A five-character location code, right-padded with spaces, from the associated item record." + }, + { + "name": "item_agency_code_num", + "type": [ + "null", + "int" + ], + "doc": "A one-character AGENCY code from the associated item record." + }, + { + "name": "ptype_code", + "type": [ + "null", + "string" + ], + "doc": "Patron type code. (Defined by the library.)" + }, + { + "name": "pcode1", + "type": [ + "null", + "string" + ], + "doc": "Patron code 1. (Defined by the library.)" + }, + { + "name": "pcode2", + "type": [ + "null", + "string" + ], + "doc": "Patron code 2. (Defined by the library.)" + }, + { + "name": "pcode3", + "type": [ + "null", + "int" + ], + "doc": "Patron code 3. (Defined by the library.)" + }, + { + "name": "pcode4", + "type": [ + "null", + "int" + ], + "doc": "Patron code 4. (Defined by the library.)" + }, + { + "name": "patron_home_library_code", + "type": [ + "null", + "string" + ], + "doc": "A five-character location code, right-padded with spaces, from the associated patron record." + }, + { + "name": "patron_agency_code_num", + "type": [ + "null", + "int" + ], + "doc": "A one-character AGENCY code from the associated patron record." + }, + { + "name": "loanrule_code_num", + "type": [ + "null", + "int" + ] + } + ] +} \ No newline at end of file diff --git a/tests/stubs/LocationHours.json b/tests/stubs/LocationHours.json new file mode 100644 index 0000000..486963e --- /dev/null +++ b/tests/stubs/LocationHours.json @@ -0,0 +1,52 @@ +{ + "name": "LocationHours", + "type": "record", + "fields": [ + { + "name": "drupal_location_id", + "doc": "The library's id as stored in Drupal", + "type": [ + "string" + ] + }, + { + "name": "name", + "doc": "The library's full name", + "type": [ + "null", + "string" + ] + }, + { + "name": "weekday", + "doc": "Day of the week for which the hours apply", + "type": [ + "string" + ] + }, + { + "name": "regular_open", + "doc": "Opening time (hh:mm) for the library in ET", + "type": [ + "null", + "string" + ] + }, + { + "name": "regular_close", + "doc": "Closing time (hh:mm) for the library in ET", + "type": [ + "null", + "string" + ] + }, + { + "name": "date_of_change", + "doc": "Date (ISO 8601 date without time in ET) at which the regular hours for the given day changed", + "type": [ + "null", + "string" + ] + } + ] +} \ No newline at end of file diff --git a/tests/stubs/PatronInfo.json b/tests/stubs/PatronInfo.json new file mode 100644 index 0000000..2984112 --- /dev/null +++ b/tests/stubs/PatronInfo.json @@ -0,0 +1,85 @@ +{ + "name": "PatronInfo", + "type": "record", + "fields": [ + { + "name": "patron_id", + "doc": "Obfuscated patron record id", + "type": [ + "string" + ] + }, + { + "name": "address_hash", + "doc": "Obfuscated concatenation of patron record id, address, city, region, and postal code", + "type": [ + "null", + "string" + ] + }, + { + "name": "postal_code", + "doc": "Patron five-digit postal code", + "type": [ + "null", + "string" + ] + }, + { + "name": "geoid", + "doc": "11-digit geoid, also known as a census tract", + "type": [ + "null", + "string" + ] + }, + { + "name": "creation_date_et", + "doc": "Date (ISO 8601 date without time in ET) of the patron record's creation", + "type": [ + "null", + "string" + ] + }, + { + "name": "deletion_date_et", + "doc": "Date (ISO 8601 date without time in ET) of the patron record's deletion. Null if the patron is still active.", + "type": [ + "null", + "string" + ] + }, + { + "name": "circ_active_date_et", + "doc": "Date (ISO 8601 date without time in ET) of the patron's last circulation activity", + "type": [ + "null", + "string" + ] + }, + { + "name": "ptype_code", + "doc": "Patron ptype code", + "type": [ + "null", + "int" + ] + }, + { + "name": "pcode3", + "doc": "Patron 'home region' code", + "type": [ + "null", + "int" + ] + }, + { + "name": "patron_home_library_code", + "doc": "A five-character location code, right-padded with spaces, from the associated patron record", + "type": [ + "null", + "string" + ] + } + ] +} \ No newline at end of file diff --git a/tests/stubs/PcReserve.json b/tests/stubs/PcReserve.json new file mode 100644 index 0000000..960e5b1 --- /dev/null +++ b/tests/stubs/PcReserve.json @@ -0,0 +1,108 @@ +{ + "name": "PcReserve", + "type": "record", + "fields": [ + { + "name": "patron_id", + "type": [ + "string" + ], + "doc": "Obfuscated patron id" + }, + { + "name": "patron_retrieval_status", + "doc": "Status of patron record retrieval (found, missing, guest pass)", + "type": [ + "null", + "string" + ] + }, + { + "name": "ptype_code", + "doc": "Patron ptype code", + "type": [ + "null", + "int" + ] + }, + { + "doc": "A five-character location code, right-padded with spaces, from the associated patron record.", + "name": "patron_home_library_code", + "type": [ + "null", + "string" + ] + }, + { + "name": "pcode3", + "doc": "Patron 'home region' code", + "type": [ + "null", + "int" + ] + }, + { + "name": "postal_code", + "doc": "Patron postal code", + "type": [ + "null", + "string" + ] + }, + { + "name": "geoid", + "type": [ + "null", + "string" + ], + "doc": "Patron census tract" + }, + { + "name": "key", + "doc": "Obfuscated crKey", + "type": [ + "string" + ] + }, + { + "name": "minutes_used", + "type": [ + "null", + "int" + ], + "doc": "minutes_used" + }, + { + "name": "transaction_et", + "doc": "Transaction date (ISO 8601 date without time in ET)", + "type": [ + "null", + "string" + ] + }, + { + "name": "branch", + "type": [ + "null", + "string" + ], + "doc": "pcrBranch" + }, + { + "name": "area", + "type": [ + "null", + "string" + ], + "doc": "pcrArea" + }, + { + "name": "staff_override", + "type": [ + "null", + "string" + ], + "doc": "pcrUserData1" + } + ] +} \ No newline at end of file diff --git a/tests/stubs/circ_trans_event.json b/tests/stubs/circ_trans_event.json new file mode 100644 index 0000000..31fab2c --- /dev/null +++ b/tests/stubs/circ_trans_event.json @@ -0,0 +1,45 @@ +{ + "invocationId": "invocationIdExample", + "deliveryStreamArn": "arn:aws:kinesis:deliveryStream/CircTransAnon-qa", + "sourceKinesisStreamArn": "arn:aws:kinesis:us-east-1:946183545209:stream/CircTransAnon-qa", + "region": "us-east-1", + "records": [ + { + "recordId": "123", + "approximateArrivalTimestamp": 1495072949453, + "kinesisRecordMetadata": { + "sequenceNumber": "49545115243490985018280067714973144582180062593244200961", + "subsequenceNumber": "123456", + "partitionKey": "partitionKey-03", + "shardId": "shardId-000000000000", + "approximateArrivalTimestamp": 1495072949453 + }, + "data": "AB4xMjMwOTgyMTMwNDkyMzQAHjIzNG85MmkzNDIzNDIzNAAccGF0cm9uLWlkLTEyMzQC9gECBG1hArYPAgo1NTU1NQACkAcCLGl0ZW0tbG9jYXRpb24tY29kZS0xMjMCzAoCFm9wX2NvZGUtYWJjAiYyMDIxLTEyLTEzVDEyOjE1OjAwAiYyMDIyLTEyLTEzVDEyOjE1OjAwKGFwcGxpY2F0aW9uX25hbWUgYWJjAqoMApyLAR5zb3VyY2VfY29kZS0xMjM=" + }, + { + "recordId": "456", + "approximateArrivalTimestamp": 1495072949453, + "kinesisRecordMetadata": { + "sequenceNumber": "49545115243490985018280067714973144582180062593244200961", + "subsequenceNumber": "123456", + "partitionKey": "partitionKey-03", + "shardId": "shardId-000000000000", + "approximateArrivalTimestamp": 1495072949453 + }, + "data": "ACIxMjMwOTgyMTMwNDkyMzQuMgAeMjM0bzkyaTM0MjM0MjM0AB5wYXRyb24taWQtMTIzNDUCpBMCBm1hMgKomgECDjU1NTU1LTIAAq5HAi5pdGVtLWxvY2F0aW9uLWNvZGUtMTIzNAKKagIab3BfY29kZS1hYmMtMgImMjAyMS0xMi0xM1QxMjoxNTowMAImMjAyMi0xMi0xM1QxMjoxNTowMChhcHBsaWNhdGlvbl9uYW1lIGFiYwKqDAKciwEec291cmNlX2NvZGUtMTIz" + }, + { + "recordId": "789", + "approximateArrivalTimestamp": 1495072949453, + "kinesisRecordMetadata": { + "sequenceNumber": "49545115243490985018280067714973144582180062593244200961", + "subsequenceNumber": "123456", + "partitionKey": "partitionKey-03", + "shardId": "shardId-000000000000", + "approximateArrivalTimestamp": 1495072949453 + }, + "data": "ACIxMjMwOTgyMTMwNDkyMzQuMwAeMjM0bzkyaTM0MjM0MjM0ACBwYXRyb24taWQtMTIzNDU2AqQTAgZtYTMCmocMAg41NTU1NS0zAALYyQUCMGl0ZW0tbG9jYXRpb24tY29kZS0xMjM0NQKKagIab3BfY29kZS1hYmMtMwImMjAyMS0xMi0xM1QxMjoxNTowMAImMjAyMi0xMi0xM1QxMjoxNTowMChhcHBsaWNhdGlvbl9uYW1lIGFiYwKqDAKciwEec291cmNlX2NvZGUtMTIz" + } + ] + } + \ No newline at end of file diff --git a/tests/stubs/invalid_records.json b/tests/stubs/invalid_records.json new file mode 100644 index 0000000..f72f311 --- /dev/null +++ b/tests/stubs/invalid_records.json @@ -0,0 +1,9 @@ +{ + "records": [ + { + "recordId": "789", + "data": "bad-encoding", + "approximateArrivalTimestamp": "2012-04-23T18:25:43.511Z" + } + ] +} \ No newline at end of file diff --git a/tests/stubs/patron_info_event.json b/tests/stubs/patron_info_event.json new file mode 100644 index 0000000..b288df0 --- /dev/null +++ b/tests/stubs/patron_info_event.json @@ -0,0 +1,44 @@ +{ + "invocationId": "invocationIdExample", + "deliveryStreamArn": "arn:aws:kinesis:deliveryStream/PatronInfo-qa", + "sourceKinesisStreamArn": "arn:aws:kinesis:us-east-1:946183545209:stream/PatronInfo-qa", + "region": "us-east-1", + "records": [ + { + "recordId": "123", + "approximateArrivalTimestamp": 1495072949453, + "kinesisRecordMetadata": { + "sequenceNumber": "49545115243490985018280067714973144582180062593244200961", + "subsequenceNumber": "123456", + "partitionKey": "partitionKey-03", + "shardId": "shardId-000000000000", + "approximateArrivalTimestamp": 1495072949453 + }, + "data": "AChwYXRyb24taWQtMTIzNDU2Nzg5MAIuYWRkcmVzcy1oYXNoLTEyMzQ1Njc4OTACCjU1NTU1AhYxMjM0NTY3ODkwMQIUMjAyMi0xMi0wMQIUMjAyMi0xMi0wMwIUMjAyMi0xMi0wMgL2AQK2DwIEbWE=" + }, + { + "recordId": "456", + "approximateArrivalTimestamp": 1495072949453, + "kinesisRecordMetadata": { + "sequenceNumber": "49545115243490985018280067714973144582180062593244200961", + "subsequenceNumber": "123456", + "partitionKey": "partitionKey-03", + "shardId": "shardId-000000000000", + "approximateArrivalTimestamp": 1495072949453 + }, + "data": "AChwYXRyb24taWQtMTIzNDU2Nzg5MQIuYWRkcmVzcy1oYXNoLTEyMzQ1Njc4OTECCjY2NjY2AAIUMjAyMi0xMS0wMQACFDIwMjItMTEtMDICkAcCnAoCBm1hMg==" + }, + { + "recordId": "789", + "approximateArrivalTimestamp": 1495072949453, + "kinesisRecordMetadata": { + "sequenceNumber": "49545115243490985018280067714973144582180062593244200961", + "subsequenceNumber": "123456", + "partitionKey": "partitionKey-03", + "shardId": "shardId-000000000000", + "approximateArrivalTimestamp": 1495072949453 + }, + "data": "AChwYXRyb24taWQtMTIzNDU2Nzg5MgIuYWRkcmVzcy1oYXNoLTEyMzQ1Njc4OTIAAAIUMjAyMi0xMC0wMQAAAAAA" + } + ] +} diff --git a/tests/stubs/valid_records_csv.json b/tests/stubs/valid_records_csv.json new file mode 100644 index 0000000..01e5818 --- /dev/null +++ b/tests/stubs/valid_records_csv.json @@ -0,0 +1,45 @@ +{ + "invocationId": "invocationIdExample", + "deliveryStreamArn": "arn:aws:kinesis:deliveryStream/LocationHours-qa", + "sourceKinesisStreamArn": "arn:aws:kinesis:us-east-1:946183545209:stream/LocationHours-qa", + "region": "us-east-1", + "records": [ + { + "recordId": "123", + "approximateArrivalTimestamp": 1495072949453, + "kinesisRecordMetadata": { + "sequenceNumber": "49545115243490985018280067714973144582180062593244200961", + "subsequenceNumber": "123456", + "partitionKey": "partitionKey-03", + "shardId": "shardId-000000000000", + "approximateArrivalTimestamp": 1495072949453 + }, + "data": "AARhYQISTGlicmFyeSBBAAZNb24CCjA5OjAwAgoxNzowMAIUMjAyMy0wMS0wMQ==" + }, + { + "recordId": "456", + "approximateArrivalTimestamp": 1495072949453, + "kinesisRecordMetadata": { + "sequenceNumber": "49545115243490985018280067714973144582180062593244200961", + "subsequenceNumber": "123456", + "partitionKey": "partitionKey-03", + "shardId": "shardId-000000000000", + "approximateArrivalTimestamp": 1495072949453 + }, + "data": "AARiYgISTGlicmFyeSBCAAZUdWUAAAIUMjAyMy0wMi0wMg==" + }, + { + "recordId": "789", + "approximateArrivalTimestamp": 1495072949453, + "kinesisRecordMetadata": { + "sequenceNumber": "49545115243490985018280067714973144582180062593244200961", + "subsequenceNumber": "123456", + "partitionKey": "partitionKey-03", + "shardId": "shardId-000000000000", + "approximateArrivalTimestamp": 1495072949453 + }, + "data": "AARjYwIeTGlicmFyeSB8IEMgISw9AAZXZWQCCjAxOjAwAgoyMzowMAIUMjAyMy0wMy0wMw==" + } + ] + } + \ No newline at end of file diff --git a/tests/stubs/valid_records_json.json b/tests/stubs/valid_records_json.json new file mode 100644 index 0000000..8479f3b --- /dev/null +++ b/tests/stubs/valid_records_json.json @@ -0,0 +1,24 @@ +{ + "records": [ + { + "recordId": "123", + "data": "ACxhYmMxMjM0NTY3ODkxMDExMTIxMzE0Agpmb3VuZAL2AQIEbWECtg8CCjU1NTU1AAAGMTIzAhgCFDIwMjEtMTItMTMCFGJyYW5jaCBhYmMCDGFyZWEgMQIaU3RhZmZPdmVycmlkZQ==", + "approximateArrivalTimestamp": "2012-04-23T18:25:43.511Z" + }, + { + "recordId": "456", + "data": "ADBhYmMxMjM0NTY3ODkxMDExMTIxMzE0MTUCDm1pc3NpbmcAAAAAAAAGMTI0AhgCFDIwMjEtMTItMTQCFGJyYW5jaCBkZWYCDGFyZWEgMgA=", + "approximateArrivalTimestamp": "2012-04-23T18:25:43.511Z" + }, + { + "recordId": "789", + "data": "ADRhYmMxMjM0NTY3ODkxMDExMTIxMzE0MTUxNgIUZ3Vlc3QgcGFzcwAAAAAAAAYxMjUCHgIUMjAyMS0xMi0xNQIUYnJhbmNoIGdoaQIMYXJlYSAzAA==", + "approximateArrivalTimestamp": "2012-04-23T18:25:43.511Z" + } + ], + "region": "us-east-1", + "deliveryStreamArn": "arn:aws:kinesis:EXAMPLE", + "invocationId": "invocationIdExample" +} + + diff --git a/tests/test_lambda_function.py b/tests/test_lambda_function.py new file mode 100644 index 0000000..5b3683f --- /dev/null +++ b/tests/test_lambda_function.py @@ -0,0 +1,102 @@ +import lambda_function +import logging +import pytest + +patron_info_processed_records = [ + { + "recordId": "123", + "result": "Ok", + "data": "eyJwYXRyb25faWQiOiAicGF0cm9uLWlkLTEyMzQ1Njc4OTAiLCAiYWRkcmVzc19oYXNoIjogImFkZHJlc3MtaGFzaC0xMjM0NTY3ODkwIiwgInBvc3RhbF9jb2RlIjogIjU1NTU1IiwgImdlb2lkIjogIjEyMzQ1Njc4OTAxIiwgImNyZWF0aW9uX2RhdGVfZXQiOiAiMjAyMi0xMi0wMSIsICJkZWxldGlvbl9kYXRlX2V0IjogIjIwMjItMTItMDMiLCAiY2lyY19hY3RpdmVfZGF0ZV9ldCI6ICIyMDIyLTEyLTAyIiwgInB0eXBlX2NvZGUiOiAxMjMsICJwY29kZTMiOiA5ODcsICJwYXRyb25faG9tZV9saWJyYXJ5X2NvZGUiOiAibWEifQ==", + }, + { + "recordId": "456", + "result": "Ok", + "data": "eyJwYXRyb25faWQiOiAicGF0cm9uLWlkLTEyMzQ1Njc4OTEiLCAiYWRkcmVzc19oYXNoIjogImFkZHJlc3MtaGFzaC0xMjM0NTY3ODkxIiwgInBvc3RhbF9jb2RlIjogIjY2NjY2IiwgImdlb2lkIjogbnVsbCwgImNyZWF0aW9uX2RhdGVfZXQiOiAiMjAyMi0xMS0wMSIsICJkZWxldGlvbl9kYXRlX2V0IjogbnVsbCwgImNpcmNfYWN0aXZlX2RhdGVfZXQiOiAiMjAyMi0xMS0wMiIsICJwdHlwZV9jb2RlIjogNDU2LCAicGNvZGUzIjogNjU0LCAicGF0cm9uX2hvbWVfbGlicmFyeV9jb2RlIjogIm1hMiJ9", + }, + { + "recordId": "789", + "result": "Ok", + "data": "eyJwYXRyb25faWQiOiAicGF0cm9uLWlkLTEyMzQ1Njc4OTIiLCAiYWRkcmVzc19oYXNoIjogImFkZHJlc3MtaGFzaC0xMjM0NTY3ODkyIiwgInBvc3RhbF9jb2RlIjogbnVsbCwgImdlb2lkIjogbnVsbCwgImNyZWF0aW9uX2RhdGVfZXQiOiAiMjAyMi0xMC0wMSIsICJkZWxldGlvbl9kYXRlX2V0IjogbnVsbCwgImNpcmNfYWN0aXZlX2RhdGVfZXQiOiBudWxsLCAicHR5cGVfY29kZSI6IG51bGwsICJwY29kZTMiOiBudWxsLCAicGF0cm9uX2hvbWVfbGlicmFyeV9jb2RlIjogbnVsbH0=", + }, +] + +circ_trans_processed_records = [ + { + "recordId": "123", + "result": "Ok", + "data": "eyJpZCI6IDE0NCwgInBhdHJvbl9pZCI6IDE0NCwgIml0ZW1faWQiOiAxNDQsICJ2b2x1bWVfaWQiOiAxNDQsICJiaWJfaWQiOiAxNDQsICJ0cmFuc2FjdGlvbl9nbXQiOiAiMjAxNy0xMS0xNCAxMTo0Mzo0OS0wNSIsICJhcHBsaWNhdGlvbl9uYW1lIjogInNpZXJyYSIsICJzb3VyY2VfY29kZSI6ICJsb2NhbCIsICJvcF9jb2RlIjogIm8iLCAic3RhdF9ncm91cF9jb2RlX251bSI6IDksICJkdWVfZGF0ZV9nbXQiOiAiMjAxNy0xMi0wNSAwNDowMDowMC0wNSIsICJjb3VudF90eXBlX2NvZGVfbnVtIjogMCwgIml0eXBlX2NvZGVfbnVtIjogMTM4LCAiaWNvZGUxIjogMCwgImljb2RlMiI6ICItIiwgIml0ZW1fbG9jYXRpb25fY29kZSI6ICJld2EwbiIsICJpdGVtX2FnZW5jeV9jb2RlX251bSI6IDAsICJwdHlwZV9jb2RlIjogIjEwIiwgInBjb2RlMSI6ICItIiwgInBjb2RlMiI6ICItIiwgInBjb2RlMyI6IDEsICJwY29kZTQiOiAwLCAicGF0cm9uX2hvbWVfbGlicmFyeV9jb2RlIjogImV3ICAgIiwgInBhdHJvbl9hZ2VuY3lfY29kZV9udW0iOiAwLCAibG9hbnJ1bGVfY29kZV9udW0iOiA0fQ==", + }, + { + "recordId": "456", + "result": "Ok", + "data": "eyJpZCI6IDE0NCwgInBhdHJvbl9pZCI6IDE0NCwgIml0ZW1faWQiOiAxNDQsICJ2b2x1bWVfaWQiOiAxNDQsICJiaWJfaWQiOiAxNDQsICJ0cmFuc2FjdGlvbl9nbXQiOiAiMjAxNy0xMS0xNCAxMTo0Mzo1MC0wNSIsICJhcHBsaWNhdGlvbl9uYW1lIjogInNpZXJyYSIsICJzb3VyY2VfY29kZSI6ICJsb2NhbCIsICJvcF9jb2RlIjogIm8iLCAic3RhdF9ncm91cF9jb2RlX251bSI6IDQ0LCAiZHVlX2RhdGVfZ210IjogIjIwMTctMTItMDUgMDQ6MDA6MDAtMDUiLCAiY291bnRfdHlwZV9jb2RlX251bSI6IDAsICJpdHlwZV9jb2RlX251bSI6IDIwMSwgImljb2RlMSI6IDAsICJpY29kZTIiOiAiLSIsICJpdGVtX2xvY2F0aW9uX2NvZGUiOiAiZndqMGEiLCAiaXRlbV9hZ2VuY3lfY29kZV9udW0iOiAwLCAicHR5cGVfY29kZSI6ICI2MCIsICJwY29kZTEiOiAiLSIsICJwY29kZTIiOiAiciIsICJwY29kZTMiOiAyLCAicGNvZGU0IjogMCwgInBhdHJvbl9ob21lX2xpYnJhcnlfY29kZSI6ICJmdyAgICIsICJwYXRyb25fYWdlbmN5X2NvZGVfbnVtIjogMCwgImxvYW5ydWxlX2NvZGVfbnVtIjogNX0=", + }, + { + "recordId": "789", + "result": "ProcessingFailed", + "data": "lgsCSDlhNmZiYmU5LWJkMTAtNDA2Ny05ZmVhLWEwODM4ZGU2YzUyNwIGNzE1Ah4yMzQ1Njc4OTA5ODc2NTQCHDMyMTAxMDk2MTE1MjE1AgZQVUwAAAIyMjAxNy0xMC0wNFQxNjo0MToyNS0wNDowMAA=", + }, +] + + +class TestLambdaFunction: + @pytest.fixture + def test_instance_3_success(self, mocker, test_data): + mocker.patch("lambda_function.load_env_file") + mock_record_processor = mocker.MagicMock() + mock_record_processor.schema.return_value = test_data["PatronInfo"] + mock_record_processor.process_record.side_effect = patron_info_processed_records + mocker.patch( + "lambda_function.RecordProcessor", return_value=mock_record_processor + ) + + @pytest.fixture + def test_instance_1_failure_2_success(self, mocker, test_data): + mocker.patch("lambda_function.load_env_file") + mock_record_processor = mocker.MagicMock() + mock_record_processor.schema.return_value = test_data["CircTrans"] + mock_record_processor.process_record.side_effect = circ_trans_processed_records + mocker.patch( + "lambda_function.RecordProcessor", return_value=mock_record_processor + ) + + def test_lambda_handler_no_event_error( + self, test_instance_3_success, caplog): + with pytest.raises(lambda_function.RecordParsingError): + lambda_function.lambda_handler(None, None) + assert "Event is undefined." in caplog.text + + def test_lambda_handler_no_event_records_return_empty_array( + self, test_instance_3_success, caplog): + event = { + "invocationId": "invocationIdExample", + "deliveryStreamArn": "deliveryExample", + "sourceKinesisStreamArn": "streamExample", + "region": "us-east-1", + } + with pytest.raises(Exception): + (lambda_function.lambda_handler(event, None)) + + def test_lambda_handler_success( + self, test_instance_3_success, test_data, caplog): + event = test_data["patron_info_event"] + assert lambda_function.lambda_handler(event, None) == { + "records": patron_info_processed_records + } + assert ( + "Processing complete. Successful transformations - 3. Failed transformations - 0." + in caplog.text + ) + assert "Finished lambda processing." in caplog.text + + def test_lambda_handler_one_failure_two_success( + self, test_instance_1_failure_2_success, mocker, test_data, caplog): + event = test_data["patron_info_event"] + assert lambda_function.lambda_handler(event, None) == { + "records": circ_trans_processed_records + } + assert ( + "Processing complete. Successful transformations - 2. Failed transformations - 1." + in caplog.text + ) + assert "Finished lambda processing." in caplog.text diff --git a/tests/test_record_processor.py b/tests/test_record_processor.py new file mode 100644 index 0000000..6c7ee50 --- /dev/null +++ b/tests/test_record_processor.py @@ -0,0 +1,186 @@ +import json +import pytest + +from record_processor import RecordProcessor + +decoded_records_json = [ + { + "patron_id": "abc1234567891011121314", + "patron_retrieval_status": "found", + "ptype_code": 123, + "patron_home_library_code": "ma", + "pcode3": 987, + "postal_code": "55555", + "geoid": None, + "key": "123", + "minutes_used": 12, + "transaction_et": "2021-12-13", + "branch": "branch abc", + "area": "area 1", + "staff_override": "StaffOverride", + }, + { + "patron_id": "abc123456789101112131415", + "patron_retrieval_status": "missing", + "ptype_code": None, + "patron_home_library_code": None, + "pcode3": None, + "postal_code": None, + "geoid": None, + "key": "124", + "minutes_used": 12, + "transaction_et": "2021-12-14", + "branch": "branch def", + "area": "area 2", + "staff_override": None, + }, + { + "patron_id": "abc12345678910111213141516", + "patron_retrieval_status": "guest pass", + "ptype_code": None, + "patron_home_library_code": None, + "pcode3": None, + "postal_code": None, + "geoid": None, + "key": "125", + "minutes_used": 15, + "transaction_et": "2021-12-15", + "branch": "branch ghi", + "area": "area 3", + "staff_override": None, + }, +] + +decoded_records_csv = [ + { + "drupal_location_id": "aa", + "name": "Library A", + "weekday": "Mon", + "regular_open": "09:00", + "regular_close": "17:00", + "date_of_change": "2023-01-01", + }, + { + "drupal_location_id": "bb", + "name": "Library B", + "weekday": "Tue", + "regular_open": None, + "regular_close": None, + "date_of_change": "2023-02-02", + }, + { + "drupal_location_id": "cc", + "name": "Library | C !,=", + "weekday": "Wed", + "regular_open": "01:00", + "regular_close": "23:00", + "date_of_change": "2023-03-03", + }, +] + + +class TestRecordProcessor: + @pytest.fixture + def test_pc_reserve_instance(self, mocker, test_data): + mock_avro_decoder = mocker.MagicMock() + mock_avro_decoder.get_json_schema.return_value = json.dumps( + test_data["PcReserve"] + ) + # Must decode second layer of encryption after decoing first layer of base64 + mock_avro_decoder.decode_record.side_effect = lambda x: { + b"\x00,abc1234567891011121314\x02\nfound\x02\xf6\x01\x02\x04ma\x02\xb6\x0f\x02\n55555\x00\x00\x06123\x02\x18\x02\x142021-12-13\x02\x14branch abc\x02\x0carea 1\x02\x1aStaffOverride": decoded_records_json[ + 0 + ], + b"\x000abc123456789101112131415\x02\x0emissing\x00\x00\x00\x00\x00\x00\x06124\x02\x18\x02\x142021-12-14\x02\x14branch def\x02\x0carea 2\x00": decoded_records_json[ + 1 + ], + b"\x004abc12345678910111213141516\x02\x14guest pass\x00\x00\x00\x00\x00\x00\x06125\x02\x1e\x02\x142021-12-15\x02\x14branch ghi\x02\x0carea 3\x00": decoded_records_json[ + 2 + ], + }[x] + mocker.patch("record_processor.AvroDecoder", return_value=mock_avro_decoder) + processor = RecordProcessor("https://test_schema_url") + return processor + + @pytest.fixture + def test_location_hours_instance(self, mocker, test_data): + mock_avro_decoder = mocker.MagicMock() + mock_avro_decoder.get_json_schema.return_value = json.dumps( + test_data["LocationHours"] + ) + # Must decode second layer of encryption after decoing first layer of base64 + mock_avro_decoder.decode_record.side_effect = lambda x: { + b"\x00\x04aa\x02\x12Library A\x00\x06Mon\x02\n09:00\x02\n17:00\x02\x142023-01-01": decoded_records_csv[ + 0 + ], + b"\x00\x04bb\x02\x12Library B\x00\x06Tue\x00\x00\x02\x142023-02-02": decoded_records_csv[ + 1 + ], + b"\x00\x04cc\x02\x1eLibrary | C !,=\x00\x06Wed\x02\n01:00\x02\n23:00\x02\x142023-03-03": decoded_records_csv[ + 2 + ], + }[x] + mocker.patch("record_processor.AvroDecoder", return_value=mock_avro_decoder) + processor = RecordProcessor("https://test_schema_url") + return processor + + def test_process_record_json_no_exception( + self, test_data, test_pc_reserve_instance, caplog): + valid_records = test_data["valid_records_json"]["records"] + expected_processed_records = [ + { + "recordId": "123", + "result": "Ok", + "data": "eyJwYXRyb25faWQiOiAiYWJjMTIzNDU2Nzg5MTAxMTEyMTMxNCIsICJwYXRyb25fcmV0cmlldmFsX3N0YXR1cyI6ICJmb3VuZCIsICJwdHlwZV9jb2RlIjogMTIzLCAicGF0cm9uX2hvbWVfbGlicmFyeV9jb2RlIjogIm1hIiwgInBjb2RlMyI6IDk4NywgInBvc3RhbF9jb2RlIjogIjU1NTU1IiwgImdlb2lkIjogbnVsbCwgImtleSI6ICIxMjMiLCAibWludXRlc191c2VkIjogMTIsICJ0cmFuc2FjdGlvbl9ldCI6ICIyMDIxLTEyLTEzIiwgImJyYW5jaCI6ICJicmFuY2ggYWJjIiwgImFyZWEiOiAiYXJlYSAxIiwgInN0YWZmX292ZXJyaWRlIjogIlN0YWZmT3ZlcnJpZGUifQ==", + }, + { + "recordId": "456", + "result": "Ok", + "data": "eyJwYXRyb25faWQiOiAiYWJjMTIzNDU2Nzg5MTAxMTEyMTMxNDE1IiwgInBhdHJvbl9yZXRyaWV2YWxfc3RhdHVzIjogIm1pc3NpbmciLCAicHR5cGVfY29kZSI6IG51bGwsICJwYXRyb25faG9tZV9saWJyYXJ5X2NvZGUiOiBudWxsLCAicGNvZGUzIjogbnVsbCwgInBvc3RhbF9jb2RlIjogbnVsbCwgImdlb2lkIjogbnVsbCwgImtleSI6ICIxMjQiLCAibWludXRlc191c2VkIjogMTIsICJ0cmFuc2FjdGlvbl9ldCI6ICIyMDIxLTEyLTE0IiwgImJyYW5jaCI6ICJicmFuY2ggZGVmIiwgImFyZWEiOiAiYXJlYSAyIiwgInN0YWZmX292ZXJyaWRlIjogbnVsbH0=", + }, + { + "recordId": "789", + "result": "Ok", + "data": "eyJwYXRyb25faWQiOiAiYWJjMTIzNDU2Nzg5MTAxMTEyMTMxNDE1MTYiLCAicGF0cm9uX3JldHJpZXZhbF9zdGF0dXMiOiAiZ3Vlc3QgcGFzcyIsICJwdHlwZV9jb2RlIjogbnVsbCwgInBhdHJvbl9ob21lX2xpYnJhcnlfY29kZSI6IG51bGwsICJwY29kZTMiOiBudWxsLCAicG9zdGFsX2NvZGUiOiBudWxsLCAiZ2VvaWQiOiBudWxsLCAia2V5IjogIjEyNSIsICJtaW51dGVzX3VzZWQiOiAxNSwgInRyYW5zYWN0aW9uX2V0IjogIjIwMjEtMTItMTUiLCAiYnJhbmNoIjogImJyYW5jaCBnaGkiLCAiYXJlYSI6ICJhcmVhIDMiLCAic3RhZmZfb3ZlcnJpZGUiOiBudWxsfQ==", + }, + ] + for i, record in enumerate(valid_records): + assert ( + test_pc_reserve_instance.process_record(record, "json") + == expected_processed_records[i] + ) + + def test_process_records_csv_no_exception( + self, test_data, test_location_hours_instance, caplog): + valid_records = test_data["valid_records_csv"]["records"] + expected_processed_records = [ + { + "recordId": "123", + "result": "Ok", + "data": "YWF8TGlicmFyeSBBfE1vbnwwOTowMHwxNzowMHwyMDIzLTAxLTAxCg==", + }, + { + "recordId": "456", + "result": "Ok", + "data": "YmJ8TGlicmFyeSBCfFR1ZXx8fDIwMjMtMDItMDIK", + }, + { + "recordId": "789", + "result": "Ok", + "data": "Y2N8TGlicmFyeSBcfCBDICEsPXxXZWR8MDE6MDB8MjM6MDB8MjAyMy0wMy0wMwo=", + }, + ] + + for i, record in enumerate(valid_records): + assert ( + test_location_hours_instance.process_record(record, "csv") + == expected_processed_records[i] + ) + + def test_process_records_exception( + self, test_data, test_pc_reserve_instance, test_location_hours_instance): + invalid_records = test_data["invalid_records"] + with pytest.raises(Exception): + test_pc_reserve_instance.process_records(invalid_records, "json") + with pytest.raises(Exception): + test_location_hours_instance.process_records(invalid_records, "csv")