From f2e8a81710ad4cd0f7ea59acd3ba0425bed512d4 Mon Sep 17 00:00:00 2001 From: Mark Maxham Date: Thu, 21 May 2020 09:43:00 -0700 Subject: [PATCH] Catch-up from lacuna-tech/mds-core (#249) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit *Autogenerated Release notes, commits, authors from Squash:* * [mds-metrics] Fix binning bug (#70) * Fix weird array construction bug in metrics API binning method * Remove async modifier * Cleanup and add state_snapshot test * Cleanup * Bump root version * Publish - @container-images/env-inject@0.1.21 - @container-images/mds-agency@0.1.22 - @container-images/mds-audit@0.1.23 - @container-images/mds-compliance@0.1.22 - @container-images/mds-daily@0.1.22 - @container-images/mds-metrics-sheet@0.1.23 - @container-images/mds-metrics@0.0.4 - @container-images/mds-native@0.0.20 - @container-images/mds-policy-author@0.1.22 - @container-images/mds-policy@0.1.22 - @container-images/mds-provider@0.1.22 - @mds-core/mds-agency@0.0.24 - @mds-core/mds-api-authorizer@0.1.22 - @mds-core/mds-api-helpers@0.1.22 - @mds-core/mds-api-server@0.1.22 - @mds-core/mds-audit@0.1.34 - @mds-core/mds-cache@0.1.22 - @mds-core/mds-compliance@0.1.23 - @mds-core/mds-daily@0.0.23 - @mds-core/mds-db@0.1.22 - @mds-core/mds-logger@0.1.20 - @mds-core/mds-metrics-sheet@0.0.23 - @mds-core/mds-metrics@0.0.4 - @mds-core/mds-native@0.0.20 - @mds-core/mds-policy-author@0.0.19 - @mds-core/mds-policy@0.0.23 - @mds-core/mds-provider@1.0.22 - @mds-core/mds-providers@0.1.22 - @mds-core/mds-stream@0.1.22 - @mds-core/mds-test-data@0.1.22 - @mds-core/mds-types@0.1.19 - @mds-core/mds-utils@0.1.22 * Add some more logs for the Metrics API prototype (#73) * Bump root package version * Publish - @container-images/env-inject@0.1.22 - @container-images/mds-agency@0.1.23 - @container-images/mds-audit@0.1.24 - @container-images/mds-compliance@0.1.23 - @container-images/mds-daily@0.1.23 - @container-images/mds-metrics-sheet@0.1.24 - @container-images/mds-metrics@0.0.5 - @container-images/mds-native@0.0.21 - @container-images/mds-policy-author@0.1.23 - @container-images/mds-policy@0.1.23 - @container-images/mds-provider@0.1.23 - @mds-core/mds-agency@0.0.25 - @mds-core/mds-api-authorizer@0.1.23 - @mds-core/mds-api-helpers@0.1.23 - @mds-core/mds-api-server@0.1.23 - @mds-core/mds-audit@0.1.35 - @mds-core/mds-cache@0.1.23 - @mds-core/mds-compliance@0.1.24 - @mds-core/mds-daily@0.0.24 - @mds-core/mds-db@0.1.23 - @mds-core/mds-logger@0.1.21 - @mds-core/mds-metrics-sheet@0.0.24 - @mds-core/mds-metrics@0.0.5 - @mds-core/mds-native@0.0.21 - @mds-core/mds-policy-author@0.0.20 - @mds-core/mds-policy@0.0.24 - @mds-core/mds-provider@1.0.23 - @mds-core/mds-providers@0.1.23 - @mds-core/mds-stream@0.1.23 - @mds-core/mds-test-data@0.1.23 - @mds-core/mds-types@0.1.20 - @mds-core/mds-utils@0.1.23 * [mds-db] Fix `editGeography` (#74) * Fix `editGeography`. * ts assist * Fixed `editGeography` with TS assist. * Bump root version * Publish - @container-images/env-inject@0.1.23 - @container-images/mds-agency@0.1.24 - @container-images/mds-audit@0.1.25 - @container-images/mds-compliance@0.1.24 - @container-images/mds-daily@0.1.24 - @container-images/mds-metrics-sheet@0.1.25 - @container-images/mds-metrics@0.0.6 - @container-images/mds-native@0.0.22 - @container-images/mds-policy-author@0.1.24 - @container-images/mds-policy@0.1.24 - @container-images/mds-provider@0.1.24 - @mds-core/mds-agency@0.0.26 - @mds-core/mds-api-authorizer@0.1.24 - @mds-core/mds-api-helpers@0.1.24 - @mds-core/mds-api-server@0.1.24 - @mds-core/mds-audit@0.1.36 - @mds-core/mds-cache@0.1.24 - @mds-core/mds-compliance@0.1.25 - @mds-core/mds-daily@0.0.25 - @mds-core/mds-db@0.1.24 - @mds-core/mds-logger@0.1.22 - @mds-core/mds-metrics-sheet@0.0.25 - @mds-core/mds-metrics@0.0.6 - @mds-core/mds-native@0.0.22 - @mds-core/mds-policy-author@0.0.21 - @mds-core/mds-policy@0.0.25 - @mds-core/mds-provider@1.0.24 - @mds-core/mds-providers@0.1.24 - @mds-core/mds-stream@0.1.24 - @mds-core/mds-test-data@0.1.24 - @mds-core/mds-types@0.1.21 - @mds-core/mds-utils@0.1.24 * fix error message (#77) * mdscl/istio: install (un)install via helm straight up (#75) * mdscl/istio: install (un)install via helm straight up * mdsctl/istio: uninstallIstio cleanup * [mds-audit] Support attaching media to audits (#76) * Add new tables for audit attachments * Integrate with audit attachments db tables * New endpoints to add and delete attachments; return attachments with audits * Lint * Minor cleanups to s3 upload key and base url * Move S3 config to env variables * Let caller catch writeAttachment exception; type fixes * Lint * [mds-audit] Include initial provider state with audits returned via API (#42) * Add MDS vehicle status and location to get audit response * Add test for provider info in audit response * Capitalize DESC keyword * Add provider_event_time to Get Audit response * Remove references to dropped columns from schema * Use enums * Fix import * Make ORDER BY configurable in readEventsWithTelemetry * Fix errors when running `yarn image` * Use optional chaining from TS 3.7 * Bump root version * Publish - @container-images/env-inject@0.1.24 - @container-images/mds-agency@0.1.25 - @container-images/mds-audit@0.1.26 - @container-images/mds-compliance@0.1.25 - @container-images/mds-daily@0.1.25 - @container-images/mds-metrics-sheet@0.1.26 - @container-images/mds-metrics@0.0.7 - @container-images/mds-native@0.0.23 - @container-images/mds-policy-author@0.1.25 - @container-images/mds-policy@0.1.25 - @container-images/mds-provider@0.1.25 - @mds-core/mds-agency@0.0.27 - @mds-core/mds-api-authorizer@0.1.25 - @mds-core/mds-api-helpers@0.1.25 - @mds-core/mds-api-server@0.1.25 - @mds-core/mds-audit@0.1.37 - @mds-core/mds-cache@0.1.25 - @mds-core/mds-compliance@0.1.26 - @mds-core/mds-daily@0.0.26 - @mds-core/mds-db@0.1.25 - @mds-core/mds-logger@0.1.23 - @mds-core/mds-metrics-sheet@0.0.26 - @mds-core/mds-metrics@0.0.7 - @mds-core/mds-native@0.0.23 - @mds-core/mds-policy-author@0.0.22 - @mds-core/mds-policy@0.0.26 - @mds-core/mds-provider@1.0.25 - @mds-core/mds-providers@0.1.25 - @mds-core/mds-stream@0.1.25 - @mds-core/mds-test-data@0.1.25 - @mds-core/mds-types@0.1.22 - @mds-core/mds-utils@0.1.25 * Backend method for finding policies that use particular geographies. (#49) * [mds-logger] Overloading log with log level parameter to support ternary conditions & fluent syntax (#71) * Overloading log with log level parameter to support ternary conditions & fluent syntax * remove psql-client (#84) * need to have the domain inserted for both TLS and non-TLS (#83) * allow additional hosts for egress if needed (#82) * Add more granular readAllEvents performance logging (#80) * [mds-schema-validators] Pull all JSON Schema-related definitions and code into a new package. (#78) * Consolidating validator/schema-related code in mds-policy to mds-utils * Consolidating validator/schema-related code in mds-compliance to mds-utils * Consolidating validator/schema-related code in mds-compliance to mds-utils * extracting JSON Schema definitions and code into mds-json-schema * fixing build errors caused by moving validation to new package * renaming mds-json-schema -> mds-schema-validators and cleaning up build issues * moving ValidationError back to mds-utils * removing hapi/joi from mds-utils * updating tsconfigs * Referencing RULE_TYPES when constructing schemas * Revert "Referencing RULE_TYPES when constructing schemas" This reverts commit 87def6e65910b44b3e632f6b3525bf993804882b. * Referencing RULE_TYPES when constructing schemas * Configuration Service/API (#91) * Config service client and tests * Config API and tests * Config API container image * Add debug configurations for config api/service * create a configmap from files in helm/mds/mds-config, mount it to the mds-config pod under that path /mds-config * Allow settings to use .json or .json5 extensions * [mds-compliance] Compliance engine sorts vehicles by earliest telemetry data, so most … (#89) * Compliance engine sorts vehicles by earliest telemetry data, so most recent events will be the ones tagged as violators. * Updating sort function. * Removing unnecessary spread operator. * Fix helm configmap glob (#95) * [mds-cache, mds-db] Instrument some cache and db operations with timing logs (#97) * Instrument some cache and db operations with timing logs * Add provider to getVehicles timing log * Update dev/tooling dependencies. (#93) * Upgrade to latest package dependency versions * Clean up test scripts using config files * Bump some coverage levels * [mds-audit] Relax lookup VIN response requirements (#96) * Relax lookup VIN response requirements * Add logs for missing info; return telemetry when possible * Bump root version * Publish - @container-images/env-inject@0.1.25 - @container-images/mds-agency@0.1.26 - @container-images/mds-audit@0.1.27 - @container-images/mds-compliance@0.1.26 - @container-images/mds-config@0.0.2 - @container-images/mds-daily@0.1.26 - @container-images/mds-metrics-sheet@0.1.27 - @container-images/mds-metrics@0.0.8 - @container-images/mds-native@0.0.24 - @container-images/mds-policy-author@0.1.26 - @container-images/mds-policy@0.1.26 - @container-images/mds-provider@0.1.26 - @mds-core/mds-agency@0.0.28 - @mds-core/mds-api-authorizer@0.1.26 - @mds-core/mds-api-helpers@0.1.26 - @mds-core/mds-api-server@0.1.26 - @mds-core/mds-audit@0.1.38 - @mds-core/mds-cache@0.1.26 - @mds-core/mds-compliance@0.1.27 - @mds-core/mds-config-service@0.0.2 - @mds-core/mds-config@0.0.2 - @mds-core/mds-daily@0.0.27 - @mds-core/mds-db@0.1.26 - @mds-core/mds-logger@0.1.24 - @mds-core/mds-metrics-sheet@0.0.27 - @mds-core/mds-metrics@0.0.8 - @mds-core/mds-native@0.0.24 - @mds-core/mds-policy-author@0.0.23 - @mds-core/mds-policy@0.0.27 - @mds-core/mds-provider@1.0.26 - @mds-core/mds-providers@0.1.26 - @mds-core/mds-schema-validators@0.1.2 - @mds-core/mds-stream@0.1.26 - @mds-core/mds-test-data@0.1.26 - @mds-core/mds-types@0.1.23 - @mds-core/mds-utils@0.1.26 * Fix debugging for unit tests (#105) * mdsctl: prompt-to-proceed if not using docker-desktop (#85) * mdsctl: prompt-to-proceed if not using docker-desktop * mdsctl: bit of cleanup * Add moped to VEHICLE_TYPES enum (#106) * Support merging multiple settings properties (#108) * Support merging multiple settings files * Get settings property names from query string * Support existing single settings property API * [mds-agency] [mds-db] Initial Stop Prototype (#47) * Add Stop datatype, and r/w endpoints to Agency * Add proper validation when POSTing stops * Add some basic error handling * Start adding tests... * Clean up bad db types * Test cleanup * More cleanup * More cleanup pt2 * More cleanup * Add read back test * More tests * Last of the tests? * Some formatting cleanup * Remove unused import * Make geography_id optional for stops, add lat/lng requirement * Cleanup * Review nits * Clean up schema, use SqlVals() * Remove markdown plugin * Update carshare -> car, and add moped type to vehicleTypesCountMapSchema definition * Break build... this is intentional, I promise * Revert "Break build... this is intentional, I promise" This reverts commit d8ba98963e3d2c523aa8212e9715f3adf76bd057. * Fixture updates * Add recorded column * Cleanup * Add option to skip bbox check when fetching vehicles by bbox (#113) * [mds-audit] Speed up VIN lookup by parallelizing cache reads (#110) * Speed up VIN lookup by parallelizing cache reads * Fix build error (#114) * Update VS Code ESLint settings (#117) * [mds-db] Refactor makeReadOnlyQuery to permit use of SqlVals() (#116) * Refactor makeReadOnlyQuery to permit use of SqlVals() * Fix minor oversight * [mds-agency] [mds-web-sockets] Basic outbound websocket implementation for MDS-Agency (#99) * First pass at WS * Add tsconfig * Add basic websocket emission to mds-agency * Emission cleanup * Add tsconfig * Cleanup * Dependency cleanup * More dependency cleanup * WIP * More WIP * WIP * WIP * Manual event triggering works, wheee * Remove agency socket interaction, to be replaced with reading from KNE * Added basic auth handling, need to add logic for token expiration. Could also just add a 30s heartbeat requirement. * Validate auth * More auth cleanup * Header validation cleanup * Generate random events and telemetry to send down websocket * Nits * Add outbound ws client * Message emission working * Purge console.log * Clean up authorizers * Cleanup * Bump deps * Bump deps * Cleanup * Test coverage * Cleanup * Moar cleanup * So much cleanup * Bap * nits * Moar nits * Refactor async in forEach to use Promise.all() * Rename nit * Export cleanup * Exception handling cleanup * Add missing index.ts file * Remove stale clients * Make sure to kill the connections too * Exec function * Minor authorizer refactor (#111) * Force exit for agency tests to close open socket * Moar exit forces * Import alignment * Cleanup * Changes for internal routing and basic helm config * Remove test code * Cleanup * [mds-agency] Fix import of package with no default export (#120) * [mds-web-sockets] Check for admin:all scope when authenticating clients (#121) * Add admin-all scope check when authenticating ws clients * Nullish coalesce all the things * Remove unnecessary check * Fix image name for mds-web-sockets (#122) * .Values.api.env will populate all APIs' environments (#92) * [mds-web-sockets] Don't always start WebSocket server (#123) * Don't always start WebSocket server * Add missing launch file * use branch name for docker image tag suffix (#124) * use literal branch name as docker suffix * portable sed * [mds-web-sockets] Fix PING message handling * [mds-processors] Refined Processor Implementation (#104) * Cleanup * update requirement versions * revert to original dir copy * cleanup * Beginning of cleanups * more cleanup/ removed streaming logic scoped for Q1 * trip-proc cleanup * type fixes * ts refactor * helm reconcile * mds-logger * metric cleanup * helm fixes * breaking db methods into seperate file * cache/db refactor * Clean up geo.ts * Clean up annotation.ts * Clean up proc-event.ts * Clean up proc.ts * Clean up misc * Clean up metrics.ts * Update MetricsTableRow type * Resolve/suppress all linter warnings, and resolve compilation errors. * Add eslint-reasons to most directive disables * Refactor geo.ts files into utils * Add stricter typing * Cleanup * Fix dependencies/versions * cache/db fixes * proc fixes * mdsctl: cleanup (comment out nats) * Cleanup * Optional chaining * Add ParseError * upgrade natss * mdsctl: update * mdsctl: update bin/mdsctl add knative(broker,channel) * fix typo * mdsclt: ko hack, mds pause for natss * mdsctl: s/pause/condition/g * [WIP] [mds-metrics] Add dump all metrics rows API endpoint (#94) * WIP codify types add initial request handler test add query construction tests named args obj (no more defaults) Add explanatory comments more comments * s/timestamp/start_time * rename some things + cleanup * update test * fix incorrect import * fix type signature * mdsctl: kne w/o namespace labeling * Remove default param val * - Add id column to reports tables - Restore sorting of code * Do not initialize the db at startup! * Reduce unnecessary switch cases * Fix lint error that's been breaking build * Upgrade to cloudevents 1.0 with typings * added presets to mdsctl * update proc-event * mdsctl uncomment natss * Remove deprecated Redis stream * Remove package-lock file * Generic server for handling knative cloudevents * Make handler async * Unit test placeholder * [mds-metrics] Adds geography_id and provider_id query params to /metr… (#98) * [mds-metrics] Adds geography_id and provider_id query params to /metrics/all Usage: GET /metrics/all?geography_id={}&provider_id={} Both fields are optional. Each id query param must be a valid UUID or you'll get a 500. TODO test w/ cURL or Insomnia @evanxadkins This is also going into PR #88 Closes [MDSAAS-487](https://lacunadotai.atlassian.net/browse/MDSAAS-487) * fix up lint and add comment * remove geography_id query param * initial stab at vehicle_type querying * vehicle_type query param support * add english bin_size support * switch to nifty time utils * crazy time validation logic * add note * fix mutable moment bug * add note * clean up request handler * clean up notes * add tsv format query param * add note * add some tsv export tests * fix failing test * tests should be more stable now * fix import * purge link * remove flaky test (again) * lint * type enforcement * eslint errors * Regenerate lock file * Switch trip/provider handlers to event server * Clean up a couple test commands/timeouts * Add better parsing logs * trip-proc update * promise handling * prov-proc update * metrics update * Handle parse errors from cron source events * Improve test coverage * Minor cleanup * Feature/jwtodd events trips provider processor (#101) * nats: internal deployment of processors * cjs: remove hardwired minute interval * helm: fix cronjobsource schedule * processors: add cronjob template * interval: 1m * event sync * nats: processor (internal-apis; no need to port-forward), simulator cli, natss broker/channel * helm/mds: template cleanup(broker, processor-cronjob) * mdsctl: tad better ko ref * helm/mds cleanup * mdsctl: make natss a pre-condition of mds * mdsctl: cleanup * bug fixes * Package update * [mds-metrics] Add stub endpoint (#103) * add stub endpoint * fix async/string * simplify JSON tsv processing * re-enable/fix up tsv export in main api * WIP * cache update * cache update * Update test scripts for new packages * Update versions of mds-core dependencies * Add missing dependencies, fix build/test errors * package updates * minor syntax changes * mdsctl: sed of osx; todo: fix for all * mdsctl: better (osx,windows) natss install * multimodal changes * mdsctl: support deleting 2+ images * mdsctl: build image cleanup, simulator build * event_count metric addition * [mds-db] [mds-metrics] Add SqlVals() to processors db queries (#107) * refactor to use SqlVals() * revert unrelated changes * helm/mds: +mds-config, for t in helm/mds/templates/* s/key/name/g, move natss-channel into deploy namespace (eg mds); verified: simulator, logs mds-(event,provider,trip)-processor * Add unit tests to mds-event-processor (#112) * Revert "[mds-db] [mds-metrics] Add SqlVals() to processors db queries (#107)" This reverts commit 0cee743787328d6ffa4bbca963370a3030218f4d. * fix weird lint error * initial test * crlf yikes * moar tests * lower codecov * more unit tests * small helm changes * sqlVals * small error handling changes * Revert "sqlVals" This reverts commit f2299b25606045e219fe0a1c374ae8140afbd89b. * vscode settings * kne/natss:0.11.0, multi-tenant nats/redis msg routing (#115) * mdsctl: delete 2+ images * filter prep * natss: conditionally prefix the event:type with the tenantId else use 'mds' as the default * rm tenantId dbug * events/multi-tenancy: patch db statments to vary event-type prefix, attempted to push the tenentId down to utils but bad things happened to my system; next: test concurrent tenants, verify db statements * kne: move deploy process to helm (from kubectl apply -f [url]) * nats: helm'ify * nats/helm: pin to gcr * nats: prioritize (un)install natss via helm * redis: multi-tenant * multi-tenant: cleanup * mdsctl: not to sync kne-contrib from a release * redis/multi-tenant: clean up comment * redis/multi-tenant: clean up argument quoting * mdsctl: helm kne-eventing-init * nats/multi-tenant: filter out tenant-routing prefix prior to forwarding the message to the handler * natss event processor: clean-up * helm/kne-contrib: r0.11.0 natss-channel.yaml * mdsctl: fixme cleanup * tenantid: better ternary * Clean up cloud event parsing. * Suppress deprecation warnings * Feature/maxj/sqlvals (#119) * [mds-audit] Speed up VIN lookup by parallelizing cache reads (#110) * Speed up VIN lookup by parallelizing cache reads * Fix build error (#114) * Update VS Code ESLint settings (#117) * [mds-db] Refactor makeReadOnlyQuery to permit use of SqlVals() (#116) * Refactor makeReadOnlyQuery to permit use of SqlVals() * Fix minor oversight * re-enable sqlvals * [mds-agency] [mds-web-sockets] Basic outbound websocket implementation for MDS-Agency (#99) * First pass at WS * Add tsconfig * Add basic websocket emission to mds-agency * Emission cleanup * Add tsconfig * Cleanup * Dependency cleanup * More dependency cleanup * WIP * More WIP * WIP * WIP * Manual event triggering works, wheee * Remove agency socket interaction, to be replaced with reading from KNE * Added basic auth handling, need to add logic for token expiration. Could also just add a 30s heartbeat requirement. * Validate auth * More auth cleanup * Header validation cleanup * Generate random events and telemetry to send down websocket * Nits * Add outbound ws client * Message emission working * Purge console.log * Clean up authorizers * Cleanup * Bump deps * Bump deps * Cleanup * Test coverage * Cleanup * Moar cleanup * So much cleanup * Bap * nits * Moar nits * Refactor async in forEach to use Promise.all() * Rename nit * Export cleanup * Exception handling cleanup * Add missing index.ts file * Remove stale clients * Make sure to kill the connections too * Exec function * Minor authorizer refactor (#111) * Force exit for agency tests to close open socket * Moar exit forces * Import alignment * Cleanup * Changes for internal routing and basic helm config * Remove test code * Cleanup * [mds-agency] Fix import of package with no default export (#120) * remove quotes around vals.add() * follow existing pattern for IN * clean up unused imports * more cleanup * more lint? * helm/mds: fix values(apis,processors) * Fix Cloud Event emitter cache * Remove deprecated test * Restore cloud event test * minor proc package fix * Feature/maxj/additional query params (#118) * specify multiple provider_id query values * clean up types, normalize logic to arrays * add multiple bin processing support * clean up bin_size handling * add some additional handling code * %s/let/return/g * clean up types/naming * clean up default arg * fix test * mdsctl: fully helm'ify the mds install (by labelling the namespace; noting the cost is an extra 'default' in-memory broker) * CE prefix metric query removal * Dependency updates * added README * Small cleanups * Fix helm template * Fix project reference * Add unit test to fix coverage * add recorded column to reporting tables * Update README.md * added now util function * Update README.md * run triggers on the top of the hour * helm/kne: remove in-memory-controller; tested with `mdsctl -p processors install:mds,simulator` and observing events propogated to logs * helm/mds: add `postgresql.password` empty-by-default field with clarifying comment * helm/knative: remove imc k8s deploy * [mds-provider-processor] A few unit tests for mds-provider-processor. (#128) * A few unit tests for mds-provider-processor. * Fixed test threshold. * this is not right but it makes tests pass * Clean up project references * Adding cache.initialize for consistency. * unit tests for metric utils * formatting * cleanup * Restore configmap functionality * type reduction * hour cron interval * Mount config volume in all pods (#130) Co-authored-by: Neil Goldader Co-authored-by: Michael Durling Co-authored-by: James Todd <99516+jwtodd@users.noreply.github.com> Co-authored-by: Max Johansen Co-authored-by: Alex Gottschalk <389982+invertigo@users.noreply.github.com> Co-authored-by: janedotx * [mds-web-sockets] Add proper env-injection to mds-web-sockets (#133) * mdsctl: remove dashboard, get cli:postgresql and cli:redis working again (#126) * mdsclt: rm dashhboard, fix cli * mdsctl: get cli:[postgresql|redis] working again * mdsctl: add helm@2 to PATH given it is installed in a non-default/opt location; note: dep on #104, will work as-is if pushed prior * helm/mds: match #104 re $.Values.apis.env parse error * mdsctl: helm init cleanup, don't display usage message when an error is present Co-authored-by: Neil Goldader * [mds-agency] Don't retry then send a 500 on a failed cache/stream/socket write (#134) * Don't retry then send a 500 on a failed cache/stream/socket write * Use recorded_event rather than event * Refactor telemetry writing * fix crash if values not present (#125) * [mds-audit] Return last active device registered if db has duplicates for provider + vin (#127) * Return last device registered if db has duplicates for provider + vin * Fix lint * Prefer an active device when de-duping * Load configuration data via service (#132) * Load configuration data via service * Rename a few things... * Fix tests Co-authored-by: Neil Goldader * [mds-agency] [mds-utils] Extract normalizeToArray() and leverage in Daily (#136) * refactor normalizeToArray() * use new helper * [mds-*] Append commit hash to image tag if on non-master branch (#139) * Append commit hash to image tag if on non-master branch * Review nits * replace heisentest with unit tests (#138) Co-authored-by: Neil Goldader * Update package dependencies (#140) * [mds-web-sockets] Initial web-socket test to get the framework in place. (#137) * Initial web-socket test to get the framework in place. * Minor refactor * mdsctl: number of usability improvements (#135) * mdsctl: seed PATH for non-default install bins; confirm remote-deploy only once * mdsctl: tiller aws * mdsctl: cleanup * mdsctl: better remoting, add ability to cycle a pod, lessen remote warning check * mdsctl: add cycle default, all services and procesors * mdsctl: cleanup neg responses * mdsctl: nit * mdsctl: deploy in-cluster pgcl/redis-cli; note: can remove pcli/redis from brew * mdsctl: rm brew(pgcli, redis) * mdsctl: rm dead preset hook * mdsctl: fix bugs(uninstallHelm, don't require MDS_SIMULATOR_REPOSISORY if MDS_SIMULATOR is set to a viable install) * mdsctl: move some non-obvious configuration switches to comment-notes * helm/natss: provide ability to override default resources(requests.cpu, limits.memory) * readme: update mdsctl invocation * mdsctl: +grafana; note: not configured correctly just yet * mdsctl: +(un)install(dnslookup,curl,grafana) * mdsctl: support kn(eventing,monitoring,serving) * mdsctl: helm-delete by namespace * mdsctl: touch ~/.bashrc if it doesn't exist, better bash command-to-brew-recipe mapping support * helm/natss: set default memory to 512M, override with 32M * mdsctl: install:knative(monintoring); will be continued in another branch * natss: default memory=512M, fix mdsctl dns-lookup ns bug * mdsctl: processors(+websockets) * [mds-agency] [mds-web-sockets] Improve agency/telemetry error handling, and add basic health check to WebSocket client (#141) * First pass at cleaning up telemetry error handling * Patch the client to properly handle not being able to connect to web-sockets * More attemps at fixing client code... * Moar cleanup * More cleanup * add unified state representation (#144) Co-authored-by: Neil Goldader * [mds-metrics] Fixes metrics scope processing (#146) * adds metrics:read and metrics:read:provider scoping * fix error msg * add note * push provider query logic to middleware * simplify logic * fix import * lint * fix unit tests * coverage * alphabet * add state transition diagram (#148) * Support async access token scope validators (#147) * Support async access token scope validators * Dependency updates * ESLint/Prettier * remove self loop (#149) * [mds-utils] Remove unused state transition function (#145) * remove unused function * lint * add expected transition values * Make redis multi calls async (#151) * [mds-*] Upgrade to Node.js 12 (v12.14.1) runtime (#154) * Upgrade to Node.js 12 (v12.14.1) runtime * Upgrade node types * [mds-metrics] Add unauthorized error if query params attempt to exceed scope (#156) * add unauthorized error if query params attempt to exceed scope * reorder scope checking * Feature/eadks proc cleanup (#150) * cleanup spike * telemetryMap structure change * optimize registered count metric * fixed test case * null vehicle_type cache read bug fix * service area test cases * test case type enforcement * cleanup * query fix * reduce logic * removed unused import * added error msg * removed unused import * syntax edits * migration script and sql logs * Remove provider API from City Services cluster (#159) * Remove provider API from City Services cluster * Remove provider API scopes * fix heisenbug by moving tests to end (#161) Co-authored-by: Neil Goldader * Extend daily timeout (#162) * increase timeout * timeout env var * reorder number cast * make sure we have AWS env vars before we make the call asking for AWS creds (#164) * two changes to fix heisenbug pt 2 in policy-author (#165) * [mds-cache][mds-processors] Cache optimization for event and trip processors (#160) * cleanup spike * telemetryMap structure change * optimize registered count metric * fixed test case * null vehicle_type cache read bug fix * service area test cases * test case type enforcement * cleanup * query fix * restructure spike * hscan addition * proc cache changes * hscan reformat * hscan bug fix * reduced cache maintanence logic * removed unused import * edit to cache method names * merge cleanup * merge cleanup * more merge cleanup * comment syntax * map edits * reduced logic * typdef fixme comment * revert values.yaml Co-authored-by: Mark Maxham Co-authored-by: Neil Goldader * forward parsing error to client (#166) * Don't mutate datetime (#167) * no mutation * refactor modules + add test * Add moment-based timestamps * Export getCurrentDate() Co-authored-by: Neil Goldader * Fix clone error on Windows by removing unit test for invalid filename (#168) * [mds-stream] [mds-event-processor] NATS implementation for mds-event-processor. (#143) * First pass at nats implementation for mds-event-processor * Some cleanup * Works in-cluster with manual IP for NATS.connect args * Actually await things * nats: mdsctl(nats), disable kne/natss * Remove trigger.yaml * Check env.NATS instead of env.SINK * mdsctl: natss * Switch from vanilla nats to nats-streaming. This enables us to sychronously handle subscriptions. * stan ftw * add STAN_CLUSTER_ID env var * WIP * WIP * WIP * WIP * Re-add event-server wrapper for mds-event-processor * Revert some prior changes, structure events with CE * Add credentials support * stan.creds * Add STAN_CREDS env var * Some changes... * stan.creds mount * cleanup * Stop using mds-event-server because it causes problems, working end-to-end in cluster * Patch * infer image:tags from remote registry * +gcloud * Re-add getRawData() * Re-add mds-event-server, switch to using nats-streaming 0.3.0 client * Clean up dependencies * Update dependencies * Ack failed messages * helm(nats,stan) * stan/cleanup * +nats-box * +nats-box, pin cluster-id * stan cluster id * add [nats|stan]x which are single-pod no-auth nats-io/k8s deploys * helm'ify (nats,stan)x * Cluster handled pod routing * +stan=statefulset * +stans:0.16.2 * u/d * Event-server refactoring to improve readability * Add getNats() to get a persistent nats client and save it if it falls over * Add re-connect logic * Turns out the nats client will attempt to reconnect for us, groovy. * Enable reconnects * Misc cleanup * disable istio-injection in nats deployments * prune kne * cleanup * stan: disable istio * mdsctl: patch nats helm so that it's operations are distinct from init,cluster * (un)install:stan fix * cleanup natsbox conf * install {tools}/helm:2.14.1 if not preent in the env * bump stan disk allocation * allow for more-more-then-one (natsbox, curl, dns) deploys * add ServiceEntry egress for nats, and DestinationRule to disable mTLS for the destination namespace * switch default nats namespace to "nats" from "default", use correct nats endpoint for stan * mdsctl: +(nats,stan)-local, rm(kne*); next: conf mds->nats.nats * mds: add env NATS=nats.nats, set STAN_CLUSTER=stan; would like to remove env STAN, STAN_CREDENTIALS, SINK * patch * helm/mds/.../egress.yaml: fix typo * patch: get image versions * +kubectx, helm/mds/deployment conf nats ns * cold-start install patch * cleanup * stan: set max-message,max-bytes=0 (unlimited) * mdsctl: keep-cache support for yarn * trip-proc cron job * Cron restart policy fix * cronjobs in the house * sync mds-trip * cleanup * mds/templates: image w/ & w/o registry * cleanup env * Remove CE_NAME and STAN in favor of NATS * Update STAN_CLUSTER to nats-streaming * Switch to using env.NATS for streaming * More switching to env.NATS * provider-proc cron edits * Await parseTripsEvents * u/d readme(mdsctl) * u/d mdsctl * u/d mdsctl * mdsctl: support provider=aws:imageTag (get latest tag by image name) * mdsctl: u/d readme * Optional processor fix * mount mds-config configmap * Remove db.startup, cache.startup, getConfig. They're not working properly at the moment * Remove db.startup from mds-provider-processor * +surveyor, rename stan-statefulset to nats-streaming-server * helm/mds: disable (trip, provider) processors, as they are invokved via cron * surveyor: rm creds * helm/natsbox: rename to nats-box * Force exit after function is done * use namespace name as default TENANT_ID * Fix bad variable injection * allow empty tenantId for legacy installs (ladot), use namespace if key is missing from .Values * helm/mds: clean-up proto dead-code * not using knative anymore * consistent naming * cleanup resource names * differentiate nats/stan config-map names * s/example-nats/nats/ * switch cron to hourly, turn off migrations for agency * Update mdsctl * Update README.md * Update mdsctl * Update mdsctl * remove dependency on ~/.bashrc * mdsctl: remove persisting conf in ~/.bashrc * minor refactor - put event-processor into APIs as a service deployment and put trip- and provider- processors into cronjobs. retire old metrics-sheet specific cronjob * pull out nats-surveyor to be applied to a subsequent branch * Drop nullish coalescence operator in favor of logical OR for TENANT_ID * Update TENANT_REGEXP * update template to match yaml, remove spurious env vars * helm/nats: patch stan conf (store typo) * Switch processors -> cronjobs * mdsctl: build/yarn-clean * helm: prune nats-local * Typo * Env var defaults cleanup * u/d readme re k8s conf * Add comment detailing writeCloudEvent Co-authored-by: James Todd <99516+jwtodd@users.noreply.github.com> Co-authored-by: Alex Gottschalk <389982+invertigo@users.noreply.github.com> Co-authored-by: Evan Adkins <53316916+evanxadkins@users.noreply.github.com> * Update README.md readme/docker-desktop-config cleanup * Assume cache as source-of-truth for output to get time since last event due to performance problems in production (#169) * istio-disable cronjobs (#170) * [mds-*] Add Okteto Configuration for in-cluster debugging (#171) * First-pass at okteto integration * Add per-service okteto config * Move config back to top level * Update docs, fix missing name * update docs * Oops * Update docs * Update docs moar * Docs updates * Bump okteto resources massively to avoid crashes at debug init (#172) * Make all tests async (#173) * change envoy idle timeout to offset from nodejs idle timeout. reduces incidents of a race condition where envoy attempts to connect with a timed-out node, resulting in a 503 UC * [mds-db] Refactor en-mass import/exports in mds-db to be more readable (#177) * Update Config API to support partial configs (#178) * Refactor config service to improve error handling and test coverage * Add partial config support * Unit tests for partial config * Change error (404) response body * Upgrade dependencies; fix typescript/lint errors (#179) * Export interfaces (#181) * move annotation for preventing istio sidecar injection to correct met… (#180) * move annotation for preventing istio sidecar injection to correct metadata location * move annotation for preventing istio sidecar injection to correct metadata location * remove dupe * too many specs and templates * [mds-web-sockets] First pass at some internal JWT validation. (#157) * Promisify all the things * Cleanup * Working with Bearer tokens * WIP * Working tests whoop * Fix error message * Increase default timeout values to reduce spurious 503 errors from Istio (#184) * Update Agency, Policy, and the root README docs (#182) * OMF-26 API Version Middleware (#189) * API Version Middleware * Minor corrections * Swallow STAN fatal errors. Previously, these would cause a node crash dump. (#190) * Convert empty event.event_type_reason to null (#195) * [mds-cache] Clean up promise logic in hreads (#153) * Await batch hgetallasync rather than awaiting each invocation * Remove Promise junk * Remove janky promises, update typedef * Promise.all all of the things * Remove inner Promise.all * Linterz * yarn upgrade-interactive --latest (#193) * Fix redis multi definition (#196) * Jurisdiction API (GET, POST endpoints) (#187) * getOne/getAll jurisdiction service methods * create Jurisdiction service method * initialize/shutdown jurisdiction service * Initial GET /jurisdictions endpoint * Drop schema after unit tests * Clean up return statement * Add container image for MDS Jurisdiction API * API enpoints for create / find jurisdiction * Add validation on create * Update copyright notices * Use port 4011 for local jurisdiction API * Return 404 when jurisdiction not found * Add some error typing to jurisdiction service * Remove unused type * Scopes for writing jurisdictions + api tests * Add scopes for jurisdiction read * Add jurisdiction claim filtering * Stop tests from failing in the presence of extra db tables * mdsctl installs mochawesome; commiting * Rename a scope * Add copyright notices * Add versioning middleware to Jurisdiction API * Updated lock file * Merge origin/develop and update dependencies * mdsctl yarn.lock updates * Add some DB connection logging * Use R/W connection for running TypeORM migrations (#197) * Clean up Versioned API types; Reorganize Jurisdiction/Native APIs (#198) * Refactor Jurisdiction API * Clean up Native API types * Rename some private interfaces/types * Use array destructuring * Reorganize Native API middleware/handlers * Rename utils files * Catch db connection errors separately (#199) * [mds-provider-processor] Provider filter for device cache (#174) * provider-device cache call * cron schedule revert * updated tests * test case bug * cleanup * nullish coalescing * registered device map refactor * registered device map refactor * cleanup * [mds-web-sockets] Add structured response for authentication success and failure (#204) * Add structured response for authentication success and failure * Fix test * [mds-agency] [mds-utils] Add better object validation to agency. (#200) * Add device object validation, begin working on building out event validators * Add event and telemetry validation to agency request handlers * Remove comment * Refactor to match other usage better, add allowUnknown validator option * More cleanup * Cleanup * Endpoints to update/delete a jurisdiction (#201) * Update a single jurisdiction * Add unit tests * Sort versions by timestamp * Support "deleting" jurisdictions (with tests) * Separate out ORM operations into their own module. * Fix import statements * Tidy up some error messaging * Add a comment * carve out exception to jwt auth for websockets api (auth is done internally due to different headers for ws protocol) (#192) * [mds-*] Merge in OMF/develop (#206) * Kubernetes Contribution (#233) * mdsctl/preset: onward to gh * mdsctl: landed in gh * Add additional test + simplify sum logic * generalize over summing any columns * fix test index type * Split up mds-db into more manageable files. * mdsctl: open:dashboard patch * Increase test timeout to support k8s DB/Redis (#4) * mdsctl: uinstall:simulator * mdsctl: add preset:no-events * mdsctl: -p:no-events * mdsctl: install:simulator python pathing * review feedback, boiling all of index.ts down * k8s/util-namespace: bring forward from cola * Add last provider event to audit_events table on every audit event * Add endpoint to get vehicle by provider and vin * mdsctl: support running from path * Add geos summaries endpoint. * mdsctl: support running from any directory, add --quiet option to be 'less verbose' (can likely do more) * Consolidate provider event flattening, fix function-called-before-definition lint error * review feedback * mdsctl: provide a means to disable (postgresql, redis) persistence * mdsctl: conditionally update ~/[rc] to add mdsctl to PATH env * mdsctl: conditionally add mdsctl to PATH * mdsctl: fix install:logging namespace * Bump versions * mdsctl: override fluentbit * mdsctl: logging local * mdsctl: don't istio sidecar logging * test fails fuck * mdsctl: push logging to util namespace * Fixed Geography type. * Using fancier types. * improving tests * review feedback * mdsctl: leverage getopts/optspec * mdsctl: cleanup * Resolve Geography related build errors. * Implement count minimums. * Break readGeographies into separate methods for reading Geography and GeographySummary objects. * Rename readGeographiesSummary -> readGeographiesSummaries, and replace duplicate logic with a call to readGeographies then simply strip out the geography_json * Minor tweaks due to vscode doing some poor automagical renaming * Better parameter destructuring * Rename readGeographiesSummaries -> readGeographySummaries * Resolve lint error, oops * Add additional logging info to getProviderMetrics() * Bump version * mdsctl: tab completion * [mds-agency] s/car/carshare/g MDSAAS-277 Slack discussion https://lacuna-tech.slack.com/archives/CHXQXQK5M/p1571325224046400 * update ref to enum key * add a small test * mdsctl: remove python env dep * mdsctl: s/presets/preset/g * Resolve bad import * finitio * MDSAAS-275 Remove AWS Lambda handlers, etc. (#25) * MDSAAS-275 Remove AWS Lambda handlers, etc. * Remove lambda deployment script runner * Run ESLint concurrently before unit tests * Remove AWS reference from comment * Remove explicit typing * Bump version * Publish - @container-images/env-inject@0.1.13 - @container-images/mds-agency@0.1.14 - @container-images/mds-audit@0.1.15 - @container-images/mds-compliance@0.1.14 - @container-images/mds-daily@0.1.14 - @container-images/mds-metrics-sheet@0.1.15 - @container-images/mds-native@0.0.12 - @container-images/mds-policy-author@0.1.14 - @container-images/mds-policy@0.1.14 - @container-images/mds-provider@0.1.14 - @mds-core/mds-agency@0.0.16 - @mds-core/mds-api-authorizer@0.1.14 - @mds-core/mds-api-helpers@0.1.14 - @mds-core/mds-api-server@0.1.14 - @mds-core/mds-audit@0.1.26 - @mds-core/mds-cache@0.1.14 - @mds-core/mds-compliance@0.1.15 - @mds-core/mds-daily@0.0.15 - @mds-core/mds-db@0.1.14 - @mds-core/mds-logger@0.1.12 - @mds-core/mds-metrics-sheet@0.0.15 - @mds-core/mds-native@0.0.12 - @mds-core/mds-policy-author@0.0.11 - @mds-core/mds-policy@0.0.15 - @mds-core/mds-provider@1.0.14 - @mds-core/mds-providers@0.1.14 - @mds-core/mds-stream@0.1.14 - @mds-core/mds-test-data@0.1.14 - @mds-core/mds-types@0.1.11 - @mds-core/mds-utils@0.1.14 * comments * Rename previous_geographies column and update deleteGeography so it handles all cases where read_only is not true. * Fix bad null checking. Closes MDSAAS-289 * MDSAAS-202 Upgrade to Node.js 10.16.3 LTS runtime (#12) * MDSAAS-202 Upgrade to Node.js 10.16.3 LTS * Suppress node deprecation warnings * review feedback * bump root version * Publish - @container-images/env-inject@0.1.14 - @container-images/mds-agency@0.1.15 - @container-images/mds-audit@0.1.16 - @container-images/mds-compliance@0.1.15 - @container-images/mds-daily@0.1.15 - @container-images/mds-metrics-sheet@0.1.16 - @container-images/mds-native@0.0.13 - @container-images/mds-policy-author@0.1.15 - @container-images/mds-policy@0.1.15 - @container-images/mds-provider@0.1.15 - @mds-core/mds-agency@0.0.17 - @mds-core/mds-api-authorizer@0.1.15 - @mds-core/mds-api-helpers@0.1.15 - @mds-core/mds-api-server@0.1.15 - @mds-core/mds-audit@0.1.27 - @mds-core/mds-cache@0.1.15 - @mds-core/mds-compliance@0.1.16 - @mds-core/mds-daily@0.0.16 - @mds-core/mds-db@0.1.15 - @mds-core/mds-logger@0.1.13 - @mds-core/mds-metrics-sheet@0.0.16 - @mds-core/mds-native@0.0.13 - @mds-core/mds-policy-author@0.0.12 - @mds-core/mds-policy@0.0.16 - @mds-core/mds-provider@1.0.15 - @mds-core/mds-providers@0.1.15 - @mds-core/mds-stream@0.1.15 - @mds-core/mds-test-data@0.1.15 - @mds-core/mds-types@0.1.12 - @mds-core/mds-utils@0.1.15 * match path prefix, optionally followed by a slash, optionally followed by [-a-z] character set * Populate publish_date on geographies. * review feedback * incrase timeout * Fix bad geographies migration * Minor fix * Bump main version * Publish - @container-images/env-inject@0.1.15 - @container-images/mds-agency@0.1.16 - @container-images/mds-audit@0.1.17 - @container-images/mds-compliance@0.1.16 - @container-images/mds-daily@0.1.16 - @container-images/mds-metrics-sheet@0.1.17 - @container-images/mds-native@0.0.14 - @container-images/mds-policy-author@0.1.16 - @container-images/mds-policy@0.1.16 - @container-images/mds-provider@0.1.16 - @mds-core/mds-agency@0.0.18 - @mds-core/mds-api-authorizer@0.1.16 - @mds-core/mds-api-helpers@0.1.16 - @mds-core/mds-api-server@0.1.16 - @mds-core/mds-audit@0.1.28 - @mds-core/mds-cache@0.1.16 - @mds-core/mds-compliance@0.1.17 - @mds-core/mds-daily@0.0.17 - @mds-core/mds-db@0.1.16 - @mds-core/mds-logger@0.1.14 - @mds-core/mds-metrics-sheet@0.0.17 - @mds-core/mds-native@0.0.14 - @mds-core/mds-policy-author@0.0.13 - @mds-core/mds-policy@0.0.17 - @mds-core/mds-provider@1.0.16 - @mds-core/mds-providers@0.1.16 - @mds-core/mds-stream@0.1.16 - @mds-core/mds-test-data@0.1.16 - @mds-core/mds-types@0.1.13 - @mds-core/mds-utils@0.1.16 * better regex - match the pathPrefix followed by either EoL or a slash and zero or more characters * route by FQDN in ingress * update values for ingress routing by domain * ALTER TABLE RENAME COLUMN ... TO ... (#36) * remove letsencrypt option - never worked reliably and not the ingress is not compatible with non-LE TLS (not possible to switch back and forth) * remove letsencrypt option - never worked reliably and not the ingress is not compatible with non-LE TLS (not possible to switch back and forth) * standardize API TCP ports to 4000, remove some never-used selectors for enabling/disabling DB and Cache env vars, make Eventing selector global instead of per-api * bump package version * Publish - @container-images/env-inject@0.1.16 - @container-images/mds-agency@0.1.17 - @container-images/mds-audit@0.1.18 - @container-images/mds-compliance@0.1.17 - @container-images/mds-daily@0.1.17 - @container-images/mds-metrics-sheet@0.1.18 - @container-images/mds-native@0.0.15 - @container-images/mds-policy-author@0.1.17 - @container-images/mds-policy@0.1.17 - @container-images/mds-provider@0.1.17 - @mds-core/mds-agency@0.0.19 - @mds-core/mds-api-authorizer@0.1.17 - @mds-core/mds-api-helpers@0.1.17 - @mds-core/mds-api-server@0.1.17 - @mds-core/mds-audit@0.1.29 - @mds-core/mds-cache@0.1.17 - @mds-core/mds-compliance@0.1.18 - @mds-core/mds-daily@0.0.18 - @mds-core/mds-db@0.1.17 - @mds-core/mds-logger@0.1.15 - @mds-core/mds-metrics-sheet@0.0.18 - @mds-core/mds-native@0.0.15 - @mds-core/mds-policy-author@0.0.14 - @mds-core/mds-policy@0.0.18 - @mds-core/mds-provider@1.0.17 - @mds-core/mds-providers@0.1.17 - @mds-core/mds-stream@0.1.17 - @mds-core/mds-test-data@0.1.17 - @mds-core/mds-types@0.1.14 - @mds-core/mds-utils@0.1.17 * Upgrade dependencies (#29) * Upgrade dependencies to latest versions * More upgrades * hard-code container port * initial commit, docker-compose with an nginx gateway proxy * default ports * move out of subdir * simplify PG_HOST_READER - can leave unset and the mds-db code will fall back to PG_HOST, make PG_PORT optional * add optional slack integration vars, common secret for APIs * changed secret name * Add standard API request logging (#43) * Add standard request logging * port must be string not int * fix slack token and secret * fix value for ingress domain FQDN * Remove provider event processor (#41) * Remove provider event processor * Drop deprecated provider tables * Enable SQL logging during migrations * Only drop tables that are/were part of the schema * need to quote domains in case of a wildcard globa ("*") * Remove read_only column from geographies. * Remove unused provider-stats cache methods * bump version * hard-code port number * correct secret name * template docker-compose with latest build versions * Add perf logging to MDS Daily db/cache calls * fluentbit (mds/requirements rm, util/values pin to es host) (#50) * narrow fluentbit refs * fluentbit: rm from mds/requirements, pin to elasticsearch-master in util/values * helm/(mds,util): rm fluent from mds * pull changes from fix/michael/remove-provider-api * Add perf logging to MDS Daily db/cache calls * Resolve linter errors * Bump root package version * Publish - @container-images/env-inject@0.1.17 - @container-images/mds-agency@0.1.18 - @container-images/mds-audit@0.1.19 - @container-images/mds-compliance@0.1.18 - @container-images/mds-daily@0.1.18 - @container-images/mds-metrics-sheet@0.1.19 - @container-images/mds-native@0.0.16 - @container-images/mds-policy-author@0.1.18 - @container-images/mds-policy@0.1.18 - @container-images/mds-provider@0.1.18 - @mds-core/mds-agency@0.0.20 - @mds-core/mds-api-authorizer@0.1.18 - @mds-core/mds-api-helpers@0.1.18 - @mds-core/mds-api-server@0.1.18 - @mds-core/mds-audit@0.1.30 - @mds-core/mds-cache@0.1.18 - @mds-core/mds-compliance@0.1.19 - @mds-core/mds-daily@0.0.19 - @mds-core/mds-db@0.1.18 - @mds-core/mds-logger@0.1.16 - @mds-core/mds-metrics-sheet@0.0.19 - @mds-core/mds-native@0.0.16 - @mds-core/mds-policy-author@0.0.15 - @mds-core/mds-policy@0.0.19 - @mds-core/mds-provider@1.0.18 - @mds-core/mds-providers@0.1.18 - @mds-core/mds-stream@0.1.18 - @mds-core/mds-test-data@0.1.18 - @mds-core/mds-types@0.1.15 - @mds-core/mds-utils@0.1.18 * Purge mds-provider implementation * Update LICENSE * Remove mds-cache weird duplicate functions * Clean up mds-audit weirdness * Clean up mds-db weirdness * Clean up mds-types weirdness * Clean up helm template weirdness Co-authored-by: Hunter Owens * Fix yarn.lock * [mds-policy-author][mds-geography-author] Extract geography author service from policy author. (#203) * Extract geography author service from policy author. * review feedback * review feedback * review feedback * RF * RF * satisfy linter * SqlVals and vals_list used to sanitize db input. * rf * helm unittest (#209) * mdsctl: helm unittest service * heml uuniest: auth * helm unittest: auth disable * helm unittest: autoscaler * helm unittest: +configmap * helm unittest: cronjob * helm unittest: egress * helm unittest: ingress * helm unittest: ingress domain * helm unittest: deployment * helm unittest: deployment secret * helm unittest: deployment overrides * helm unittest: better suite labels * helm force upgrade * mdsctl: ensure resolvable k8s endpoint (thx jane) * Reorder writing an event and related telemetry to avoid race condition where event is written but telemetry has not been written yet (#205) * [mds-agency] [mds-web-sockets] Add NATS-Streaming consumption for mds-web-sockets (#208) * Add mds-web-sockets consumption of NATS stream rather than agency having to persist a direct connection with the web-socket server * Clean up okteto.yml * Handle STAN errors better * eslint-ignore * Istanbul, not eslint * [mds-policy] [mds-geography] Extract read-only geography service from policy service (#211) * Extract read-only geo points from Policy. * making mds-policy tests pass * Tests for mds-geography. * pacifying linter * remove todo comment * removing TODO comment * Feature/alex/monitoring alerting (#213) * initial commit, canned alerts for prometheus * whitespace, add additional alerts * clean up summaries and descriptions * clean up slack notifications * better tracking of container restarts (count over time instead of absolute), better labeling of statefulset failures * use rate over last five minutes * bump version * update lockfile, try removing auth from grafana (thanks james) (currently broken) * add prometheus datasource, some useful dashboards. remove login since access requires authenticated kubectl * update * ignore downloaded charts * more meaningful restart rate * tuning * add alarms for pod CPU and memory going over limit * switch disk usage alerts from static to predictive * Upgrade package dependencies (#212) * Upgrade package dependencies * Add type annotation to fix build error * This test was destined to fail starting yesterday (#215) * [mds-processors][mds-metrics] removal for OMF release (#210) * removal spike * db/cache util and type removal * turf deps * purge metrics + more processors * remove cron from mdsctl Co-authored-by: Max Johansen * [mds-policy-author] Minor changes to policy to bring it back in line with the spec. (#217) * Fixing parameters for policy author. * Adding User Rule type to Policy. * [mds-stream] Add outbound kafka stream support (#216) * Add outbound kafka stream support * Cleanup * Review cleanup pt1 * Refactoring and cleanup * Call KafkaStream.initialize() from top-level initialize method * Call stream.initialize() at the top of mds-agency/request-handlers * Update README.md * Remove stream.initialize() from agency tests * Remove stream.initialize() from compliance tests * Writer refactor, and reader implementation * Fix unhappy exports * Remove callback fallback :P * clean up stream init * Run with new Prettier 2.0 defaults (#221) * Run with new prettier defaults * Revert trailingComma to previous default * Revert arrowParens to previous default * Removing Slack client and pushover notifications (#226) * Removing Slack client and pushover notifications * Implement new method to redact sensitive text * Kafka Stream Processor (#224) * Beginnings of initial refactor * More cleanup of kafkajs, removal of node-rdkafka * Rename and refactor * Use Nullable * Remove temporary type declaration * Remove file * Make producers topic specific * Basic functional streap labeler * Remove EventServer * Remove mds-event-server * Refactor stream processing * Fix module export issues * Add container image for vehicle event processor * Fix package name * Add helm deployment for event-processor * await NATS write instead of returning * Remove reliance on deprecated service_area_id * Incorporate review feedback * Merge remote-tracking branch 'origin/develop' into feature/neil/begin-kafka-refactor-to-kafkajs Co-authored-by: Neil Goldader * [mds-utils] Update relative time parsing to snap to the nearest hour when 'now' is provided as a query param (#223) * Update relative time parsing to snap to the nearest hour when is provided as a query param * Undo old prettier weirdness * Add caching of device labels (#229) * Add caching of device labels * Rename some consts * Add control methods to stream processor (#232) * Add control methods to stream processor * Minor package refactoring * Add copyright notices * Remove some spammy logs from mds-cache * [mds-stream-processor] Add geo labeling for mds-stream-processor (#231) * Add geo labeling for mds-stream-processor * Add tests * Add Sinon devDependency * Add LatencyLabeler tests * Filter based on point in shape, then map to geography_id * Remove now unused import * Only compute for published geographies * [ladot-service-areas] [mds-agency] [mds-daily] Purge service-areas (#219) * Purge service-areas * Rename mds-test-data/ladot-service-areas to mds-test-data/test-areas * Get rid of require statements * Remove mds-metrics-sheet vehicle counts (#228) * Remove mds-metrics-sheet vehicle counts * Remove vehicle-counts.ts * Remove duplicate Nullable definition * Fix mds-metrics-sheet container image build (#233) * Reorganize files * Remove extraneous vehicle-counts references Co-authored-by: Neil Goldader * Fix event type for vehicle reads in case of a cache miss (#225) * Fix Project References (#234) * Agency should not be writing telemetry to socket * Fix typescript project references * Make sinon a dev dependency * Upgrade dependencies * [mds-native] Removing mds-native. (#214) * Removing mds-native. * Removing helm tests. * Removing reference in nginx. * [mds-db] [mds-policy-author][mds-compliance] Fix/final separation of policy from geo (#218) * Publishing a policy no longer publishes a geography. Instead it throws an error. * minor fixes * Removing container-images directory for mds-native. (#236) * Remove leftover artifacts of mds-metrics (#238) * [mds-stream] [mds-stream-processor] TENANT_ID prefix for Kafka Streams (#235) * Add TENANT_ID prefix to kafka to support multi-tenancy on one Kafka cluster * Yarn.lock changes I guess? * Add a couple source/sink examples (#237) * Re-add stream init that was removed, thanx git (#239) * Add FileSource and FileSync connectors for mds-stream-processors (#240) * [mds-stream-processor] First pass at vehicle telemetry processor (#241) * First pass at vehicle telemetry processor * Add getEnvVar method to mds-utils * Refactor getEnvVar * Refactor TelemetryFlattener to TelemetryLabeler, add OptionalTelemetryLabeler * Rename FlattenedTelemetry, update NullableProperties type * [helm] Update helm for telemetry processor (#245) * Update helm for telemetry processor * do not create auth policies or ingress virtualservices for APIs with no pathPrefix (ie internal-only services) Co-authored-by: Alex Gottschalk * Initial Metrics Service (#244) * Add ORM model for metrics * Update dependencies * Create some service helper methods * Implement metrics filtering on read * Allow specifying one of more filter values * Refactor metric query tests to minimize redundancy * Refactor utility function and add missing licenses * Remove OptionalTelemetryLabeler * Code review revisions * was missing geography api (#242) * Split metrics-service into server/client modules (#246) * [mds-stream] Switch from NATS Streaming -> Vanilla NATS (#243) * First pass * Minor fix that will be replaced once I merge in * Retry forever * waitOnFirstConnect fix * Fix variable names * Add typing for NATS messages * NATS env var refactor * nats-streaming folder -> nats folder * Thanks F2 * Path fix * Enable ES2019 features in node:12.14.1-alpine (#247) * Refactor ORM Connection Configuration (#249) * Fix coverage excludes * Support named connections * Curry the ORM connection factory functions * Refactor Jurisdiction Service (#250) * ServiceProvider interface * CreateRepository function * CreateRepositoryMethod function * Move jurisdiction types/utils to service package (#251) * Move jurisdiction types/utils to service package * Use helper method for filter * Move schema to separate file * Export default connection for TypeORM CLI (#252) * Export default connection for TypeORM CLI * More robust destructuring * Fix import statement * Clean up error handling * Improve test coverage * Use recommended typescript config for istanbul (#255) * Use recommended typescript config for istanbul * Clean up some config files * Create types for mapping between entity/domain models (#257) * Create types for mapping between entity/domain * Fix test failure * Fluent model mapper syntax * Use object schema for jurisdiction validation * Fix imports and make mapper syntax consistent (#258) * [mds-stream-processor] First pass at pre-computed surrounding bbox (#253) * First pass at pre-computed surrounding bbox * Add some tests * Remove console.log * Typecasting is bads * Add geography caching test * Remove unused import * Better destructuringz Co-authored-by: Mark Maxham * Update prettier config to enforce single quotes only in js/ts files, since we may want to have double quotes in yaml files. (#256) * Update prettier config to enforce single quotes only in js/ts files, since we may want to have double quotes in yaml files. * Making file formatting changes restricting to js and ts files * [mds-config][mds-config-service] Remove mds-config and mds-config-service (#254) * Removing mds-config and mds-config-service, as these will not be part of the OMF contribution. * Removing mds-config and mds-config-service as part of prepping for the OMF contribution. * Reverting single quotes in yaml files. * Reverting more single quotes. Co-authored-by: Mark Maxham * Allow services to specify TypeORM CLI options. (#259) Use typeorm executable in ./node_modules/.bin * Serializable service errors (#261) * Use service provider interface for unit tests * Move metrics service * Move jurisdiction service * Serializable service error objects * Abstract processing of repository errors * RepositoryError.GetProperty helper * Use static error factory and type guards * Minor refactor of type guard syntax * Resolution of type errors that result from a yarn.lock deletion (#260) * Beginnings of cleanup that result from a yarn.lock deletion * Add parseQuery helper, massage our ApiRequest types to be loosly compatible with new express types * Refactor parseQuery * Require at least one key for parseQuery(...).keys(), add ApiQuery type * add ability to deploy an observable nats without stan stack (#185) * spike: surveyor * mdsctl: spike kill-switch; eg: MDS_NO_BREW=true ./bin/mdsctl * +surveyor, no_brew kill-switch * pin stan(s) to mds ns, wire stan:cluster-id=tenantId(default:mds) * stan * bootstrap nsc; next: deploy credentialed (nats,stan), surveyor * nats: u/g operator 0.7, nats 2.1.2 * natsbox: fix yml typo * nats-server/nats-account-server authenticated; next: nets-surveyor * mdsctl: simplier nsc-account-id util * mdsctl/install:nats : progressive (via kubectl-wait) deploy, authenticated nats; next: authenticated stan * mdsctl: istio-wait * mdsctl/helm: broaden support for `-p local` (ie drive down cpu/mem resource allocation) * mdsctl: invokable from zsh * mdsctl/installNats: use nats-operator helm charts * mdsctl: use nats-operator helm charts * stan * mdsctl: authenticated/connected stan; next: surveyor,mds * mdsctl: u/d stan:0.17.0, stan:authenticted, surveyor up with exception nats-surveyor (bad creds location) * mdsctl: authenticated surveyor; next: verify with natsbox, credential mds(agency,event-processor) * mdsctl: add s'more helm plugins (eg: diff, secrets, etc) * nats: helm cleanup, make (max_age, max_bytes) configurable * helm: cleanup; nats-surveyor: make cluster-size configurable * helm: mds+stan-creds * surveyor-observations: externalize to helm/values * mdsctl: nsc credentials override * mdscl: nsc cleanup * mdsctl: remove stan from bootstrap; helm/nats-account-server: change type to deployment * mdsctl: cd to nsc store-home * fix: nats-account-server deployment template, stan-namespace typo * fix: surveyor sys.creds, stan namespace, mds to nats egress * mdctl: s/pause-kubectl=45/pause-kubectl=60/ * mdsctl: provide config overlay for profile(s) * mdsctl: cleanup nsc/credentials conf * mdsctl: support credential store config modality * mdsctl: nsc install cleanup * patch * container-images: patch for removed -c:foo=bar syntax * diags: first cut at an agency->nats->processors sequence diag * mdsctl: natsbox configured to leverage surveyor creds, update readme toolchain(nsc), a bit more puml diags * mdsctl: zip 'n ship nas operator data * mdsctl: nas u/d * nats: rbac=false, split out 1x nats-init from nats-operator * nats: deploys to aws:individual-developers * nats: support no-nats deployments * surveyor-prometheus now starts up * mdsctl: better support of simulator operatsions, add values to nats-streaming, nats-surveyor * simple prometheus nats pub/sub event counter; next: figure out how to provide a /metrics endpoint for mds-stream prometheus metrics, configure prometheus to scrape mds-(agency,event-processor) * bit more prometheus * nats: isolated without surveyor (credentials) * mdsctl: override default nats namespace * remote * mdsctl: conditionally leverage cloud-ops infra * mdsctl: install mds w/ cloud-ops env cleanup * mdsctl: glob helm unittest * mds/values: comment out ( geography-author, jurisdiction) until images build mdsctl supports cloud-ops managed pg/redis natsbox creds commented out * fix build image: jurisdiction,geography-author * nats-streaming: change pvc from 20G->11G * nit * mdsctl: put pg/pwd back in for local deploy * working through deploying nats/stan to non-default ns * drop nats-operator * drop helm/nats-streaming-server chart * s/stan-creds/nats-creds/g * more stann cleanup * fix: omit operations pre-check * cleanup * fix mds-telemetry buildImage * patch deployment.yaml with conditional nats block * patch: (alex) re-imagined service-deployment-status check * better helm-repo-up * u/d package.json, yarn.lock * kick build * Remove lingering mds-config artifacts Co-authored-by: Neil Goldader * Improved service error/result types and handling (#262) * Improved service error/result types and processing * Improve test coverage * Add API helper for parsing req.params/req.query * Export processors as functions instead of values (#265) * Make the parser part of an options object (#264) * MCONSOLE-230: Convert webpack.config to a TypeScript module (#266) * Convert webpack.config to a TypeScript module * Update TypeScript/ESLint/Webpack dependencies * Order project references * Initialize repository on first connect (#268) * Remove hardcoded access token scope values (#269) * Upgrade dependencies * Make APIs declare the specific scopes they enforce * Add convenience functions for checking API access * [mds-api-server] [mds-types] Extend ApiQuery to allow string arrays (#270) * Extend ApiQuery to allow string arrays * Make query partial * Standardize API server startup (#267) * Allow array of errors in API response (#272) * Remove undeclared mds-cache dependency (#273) * delegate app (#271) * Remove mds-utils dependency on mds-db (#274) * [mds-test-data] Remove a dependency on mds-db (#277) * Remove mds-utils dependency on mds-db * Remove circular dependency * Remove some legacy Provider API code * Convert test data to TS (#278) * Make test areas TS default exports * Use webpack wrapper plugin to inject build info (#275) * Use webpack wrapper plugin to inject build info * Make container image packages private to prevent them from being published to npm Co-authored-by: Neil Goldader * API Request Query and Webpack Configuration enhancements (#279) * More expressive type for API query parameters * Use builder pattern for webpack configuration to simplify syntax * +invokeAny (#281) * +invokeAny * fix typo * Resolve some issues with exporting types (#282) * Resolve some issues with exporting types * Tooling updates - Don't run separate build step before bundle - Use ts-loader with webpack to only compile bundled files - Switch .eslintrc file to json - Add typings for WrapperWebpackPlugin - Remove --fix from test:eslint * More mds-core reorganization changes (#283) * Use mixin pattern for common entity construction * Tidy up jurisdiction/metric model mappers - .map function for working with single objects - .mapper function for working with arrays - move mapping functions to repository layer * Update dependencies * Export missing stream types and fix imports * Common uuid function for all of mds-core * Make metrics dimensions/aggregates nullable (#285) * Use entry points from packages folder for images (#284) * Use entry points from packages folder for images * Update scripts to start processors * Initial mds.metrics stream processor (#286) * Initial mds.metrics stream processor Consume messages from mds.metrics Write to database using metrics service * Adding versioning Policy, Policy Author, Geography, and Geography Author services. (#276) * MDS Geography (read-only) service has versioning. * Versioning support added in Geography Author. API and tests need to be updated to return version and reflect that new data. * Versioning implemented and tested for geography author. * minor fixes * Beginning versioning of mds-policy. * Versioning added for mds-policy * Removing unnecessary types * Policy author has versioning * In the midst of the audit versioning * mds-policy-author create handler has correct types * Basic policy author endpoints versioned. * Adding AuditResponseLocals type. * minor fixes * minor fixes * [mds-repository] connection manager improvements (#287) * Export a wrapper function for uuid * ConnectionManager improvements * Re-enable code coverage * Generate a standard initial repository migration * Consolidate logging * Only run migrations if table name specified * Move mds-cache to mds-agency-cache, so the mds-cache name can be used for an abstract cache interface (#289) * Model mapper helper (#288) * CreateModelMapper helper function * Use CreateModelMapper * Reorganize and improve test coverage * Default "recorded" value; renames for consistency * Do validation in the service * Create jurisdiction schema const * Assign property defaults in the repository * Create a utility for pluralizing a string * Remove extra @Column decorator (#290) * Metrics Model Changes (#291) * Add naming strategy for unique constaints * Update MetricEntity and migrations - Allow null values for dimension columns * Add support for nullable filters * [mds-audit] Feature/conform audit to mds versioning (#280) * Versioning Audit API half done. * Audit API versioned. * Implement an abstract repository class (#292) * ConnectionManager class * Abstract repository class * Define entity models using domain types * Update jurisdiction/metrics repositories * General tooling updates (#293) * Dependency upgrades * Rename an npm script * Establish baseline code coverage check at 85% - Set --lines to high water mark for those packages that don't meet 85% * Remove "engines" from package.json * Move packages from mds-core (#294) * Remove packages/container-images - @mds-core/mds-metrics-service - @mds-core/mds-stream-processor - @container-images/mds-event-processor - @container-images/mds-metrics-processor - @container-images/mds-telemetry-processor * Create jursidiction service pod for migrations (#295) * Create jursidiction service pod for migrations * Bundle entry points don't need .ts extensions * Generic controller for starting services * Clean up Mixin type definitions * Use unique migration index name (#296) * Create unique index names for migration tables * Move migrations to repository initialization * Abstract ReadOnly/ReadWrite repository classes * add egress rules for kafka (#298) Co-authored-by: Neil Goldader * Fix a potential issue with handling process.env (#299) * Fix a potential issue with handling process.env and add some unit tests * Minor repository updates (#300) * Pass the connection name to CLI * Add some filter utilities * Make options optional * Add unit tests for filters * Minor type updates and dependency upgrades (#301) * Type the ConnectionManager mode * Update dependencies * New service client helpers * Restructure unit tests * Make async getServiceResult helper (#303) * [mds-utils] FilterEmptyHelper rework (#304) * Rename filterEmptyHelper to isDefined, and allow it to be used on non-lists * Rework kafka to use isDefined * [mds-utils] Move generic passing in isDefined so T can be properly inferred (#306) * Move generic passing in isDefined * Split isDefined into isDefined and filterDefined * Add isDefinedOptions, make warnOnEmpty an option contained in said options type * Remove unused import * Configuration and dependency updates (#308) * Configuration and dependency updates * Turn on no-extraneous-dependencies eslint rule * Remove unused npm scripts from container-images * Have seen restarts in k8s when PG isn't ready (#307) * Trying new approach for defining service clients (#309) * [mds-utils] [mds-stream] Add ClientDisconnectedError (#311) * Add ClientDisconnectedError * Move exceptions.ts, create exception-messages.ts for reusable error message definitions * Make ExceptionMessages an object * logger.warn on a failed cache/stream write for PUT agency/vehicles endpoint (#312) * LCORE-145 Change namespace for custom claims (#305) * LCORE-145 Change namespace for custom claims Consolidating three environment variables into one Changing the default to openmobilityfoundation.org TOKEN_CUSTOM_CLAIM_NAMESPACE should be set for all existing deployments * Support token claim fallbacks and add logging * Remove cypress and upgrade dependencies (#315) * Remove cypress; not using * Upgrade dependencies * Fix typo Co-authored-by: Mark Maxham * Remove deprecated methods (#314) * Catch errors from writing to cache/stream upon a non-existant device (#313) * [mds-stream] [mds-web-sockets] Refactor NATS to follow the same structure as Kafka, and switch to nats.ts (#248) * First pass at refactoring nats to follow the same structure as Kafka * Bump ts-nats version * Add eslint disable * Bump ts-nats in mds-web-sockets * Renaming * AgencyStreamNats shutdown * Refactor mds-web-sockets container image to launch from server config in package * Move server.ts -> wsServer.ts and launch_server.ts -> server.ts, execute server from dockerfile entrypoint * Fix launch path * wsServer -> ws-server * Add untracked file * Upgrade typescript (#317) * Upgrade Typescript and other dependencies * Add ES2020.Promise lib for Promise.allSettled * Roll back typescript/tslib (#319) * [mds-policy] [mds-policy-author] Spec alignment and error handling updates (#318) * WIP * Cleanup * Some code-cov cleanup * Moar coverage increase * Add unpublished reading capabilities to mds-policy given permitted scopes * Add tests for reading of unpublished policies, clean up corresponding app logic * Comment updates * Remove some unused imports * Change test name * [mds-agency] Feature: add versioning to agency (#302) * API responses typed up and versioning added. * Updating application headers in tests. * minor fixes * Type updated. * Mostly done fixing failing tests. Still need tests for ensuring version field is in response body. * minor fixes * minor fixes * Changing version number * Making error responses conform to spec better. * minor fixes * minor fixes * updating error handling in agency * Spec realignment minor fixes. * [draft] Feature - Bypass JWT for internal requests (#152) * first pass * template namespace name * fix range in external-service role, remove clusterrbacconfig from chart, fix quoting * whitespace * comment * clean up comment * Moving read-only geo points out of geo author, leaving metadata endpoints. (#321) * Extremely small audit fix. (#320) Co-authored-by: Mark Maxham * Do not ignore ioredis at bundle-time (#323) * [mds-compliance][mds-db] Fix/compliance handles timestamp (#316) * Query for policy by rule_id. * Adding ability to see Policies active at a particular time. * Refactored snapshot logic. * Adding test to ensure timestamp paramenter works for count endpoint * minor fixes * Making policy rules unique and refactoring compliance api logic to reflect that, and renaming `end_date` param to `timestamp`. * repairing test cases that broke because rules cannot be repeated * more consistent error handling and better active policies and rule uniqueness checks * Versioning and fixing the definition of an active policy * minor fixes * minor fixes Co-authored-by: Mark Maxham * add toggles for internal jwt bypass and for setting up the cluster rbac - default to no internal jwt bypass (#325) * Upgrade to Node 14.2.0, fully switch over to ES2020 (#327) * [mds-policy] [mds-policy-author] Add global error handling middleware, improve error handling generally (#322) * Add global error handling middleware, improve error handling generally * Undo * Remove TODOs Co-authored-by: janedotx * Add custom service error type support (#329) * [mds-agency] Removing version param from agency payload to conform with general v… (#331) * Removing versiono param from agency payload to conform with general versioninig policy. * Updating ApiResponse * Better agency types. * [mds-policy-author] [mds-geography-author] Reject payloads that specify a publish_date (#328) * Update geography schema to not allow unknown properties, update policy and geography validators to not allow unknown properties, add tests to verify POST and PUT that specify a publish_date are rejected * Add more specific checks to the tests Co-authored-by: janedotx * [mds-api-server] [ALL VERSIONED APIS] Feature/neil/add versioning options (#332) * WIP * Distil down versioning middlewares, add tests * Clean up tests * [mds-policy-author][mds-geography*] Fix/add data payload to policy author and geo services (#335) * Adding data envelop and correcting mds-policy-author version * Add data envelop to geo service and correcting version number. * More data envelop for policy-author stuff. * Version corrected and data envelopes added to mds-geography-author. * Fix PR Template (#333) * Fix PR Template * Minor correction to text in CONTRIBUTING Co-authored-by: Mark Maxham * Add missing return statement, oops (#336) * Adding data envelop back to mds-policy and correcting version number (#334) Co-authored-by: Mark Maxham * 🐛[mds-geography-author] Add missing data wrapper in geography-author that somehow got through CI/CD, ES2020->ES2019 downgrade (#337) * Add missing data wrapper in geography-author that somehow got through CI/CD * Revert from ES2020 -> ES2019 Co-authored-by: janedotx * [mds-web-socket] extensible entities (#310) Allow entities to be configurable while maintaining tight subscription/filtering * Change ts-nats import to properly resolve methods (#338) * patch helm-init issue (#339) * patch helm-init issue * patch helm init * Create separate ProcessController interface (#340) Co-authored-by: Mark Maxham * bump versions (#341) Co-authored-by: Neil Goldader Co-authored-by: janedotx Co-authored-by: Max Johansen Co-authored-by: James Todd <99516+jwtodd@users.noreply.github.com> Co-authored-by: Eric Mai Co-authored-by: levi217 <55113445+levi217@users.noreply.github.com> Co-authored-by: Alex Gottschalk <389982+invertigo@users.noreply.github.com> Co-authored-by: Ken Miller Co-authored-by: Michael Durling Co-authored-by: Evan Adkins <53316916+evanxadkins@users.noreply.github.com> Co-authored-by: Neil Goldader Co-authored-by: Alex Gottschalk Co-authored-by: Hunter Owens Co-authored-by: Tim Welch --- .eslintrc.js | 56 - .eslintrc.json | 72 + .github/CONTRIBUTING.md | 2 +- .github/PULL_REQUEST_TEMPLATE.md | 24 +- .gitignore | 7 +- .mocharc.json | 7 + .nvmrc | 2 +- .nycrc.json | 7 + .prettierrc.js | 6 - .prettierrc.json | 15 + .stignore | 3 + .vscode/launch.json | 389 +- .vscode/settings.json | 13 +- @types/cloudevents-sdk.d.ts | 1 - @types/ladot-service-areas.d.ts | 17 - @types/pushover-notifications.d.ts | 1 - @types/wrapper-webpack-plugin.d.ts | 12 + README.md | 203 +- bin/mdsctl | 1155 ++- container-images/env-inject/index.ts | 31 - container-images/env-inject/package.json | 21 - container-images/mds-agency/Dockerfile | 6 +- container-images/mds-agency/package.json | 23 +- .../mds-agency/tsconfig.build.json | 11 - .../mds-agency/tsconfig.eslint.json | 1 + container-images/mds-agency/webpack.config.js | 6 - container-images/mds-agency/webpack.config.ts | 3 + container-images/mds-audit/Dockerfile | 7 +- container-images/mds-audit/package.json | 23 +- .../mds-audit/tsconfig.build.json | 11 - .../mds-audit/tsconfig.eslint.json | 1 + container-images/mds-audit/webpack.config.js | 6 - container-images/mds-audit/webpack.config.ts | 3 + container-images/mds-compliance/Dockerfile | 4 +- container-images/mds-compliance/index.ts | 28 - container-images/mds-compliance/package.json | 23 +- .../mds-compliance/tsconfig.build.json | 11 - .../mds-compliance/tsconfig.eslint.json | 1 + .../mds-compliance/webpack.config.js | 6 - .../mds-compliance/webpack.config.ts | 3 + container-images/mds-daily/Dockerfile | 4 +- container-images/mds-daily/package.json | 23 +- .../mds-daily/tsconfig.build.json | 11 - .../mds-daily/tsconfig.eslint.json | 1 + container-images/mds-daily/webpack.config.js | 6 - container-images/mds-daily/webpack.config.ts | 3 + .../mds-geography-author/Dockerfile | 11 + .../mds-geography-author/package.json | 25 + .../mds-geography-author/tsconfig.eslint.json | 1 + .../mds-geography-author/webpack.config.ts | 3 + container-images/mds-geography/Dockerfile | 11 + container-images/mds-geography/package.json | 25 + .../mds-geography/tsconfig.eslint.json | 1 + .../mds-geography/webpack.config.ts | 3 + .../mds-jurisdiction-service/Dockerfile | 11 + .../mds-jurisdiction-service/package.json | 25 + .../tsconfig.eslint.json | 1 + .../webpack.config.ts | 3 + container-images/mds-jurisdiction/Dockerfile | 11 + .../mds-jurisdiction/package.json | 25 + .../mds-jurisdiction/tsconfig.eslint.json | 1 + .../mds-jurisdiction/webpack.config.ts | 3 + container-images/mds-metrics-sheet/Dockerfile | 2 +- .../mds-metrics-sheet/package.json | 20 +- .../mds-metrics-sheet/tsconfig.build.json | 7 - .../mds-metrics-sheet/tsconfig.eslint.json | 1 + .../mds-metrics-sheet/webpack.config.js | 6 - .../mds-metrics-sheet/webpack.config.ts | 3 + container-images/mds-native/Dockerfile | 11 - container-images/mds-native/index.ts | 28 - container-images/mds-native/package.json | 28 - .../mds-native/tsconfig.build.json | 11 - container-images/mds-native/webpack.config.js | 6 - container-images/mds-policy-author/Dockerfile | 4 +- container-images/mds-policy-author/index.ts | 28 - .../mds-policy-author/package.json | 23 +- .../mds-policy-author/tsconfig.build.json | 11 - .../mds-policy-author/tsconfig.eslint.json | 1 + .../mds-policy-author/webpack.config.js | 6 - .../mds-policy-author/webpack.config.ts | 3 + container-images/mds-policy/Dockerfile | 4 +- container-images/mds-policy/index.ts | 28 - container-images/mds-policy/package.json | 23 +- .../mds-policy/tsconfig.build.json | 11 - .../mds-policy/tsconfig.eslint.json | 1 + container-images/mds-policy/webpack.config.js | 6 - container-images/mds-policy/webpack.config.ts | 3 + container-images/mds-web-sockets/Dockerfile | 11 + container-images/mds-web-sockets/package.json | 25 + .../mds-web-sockets/tsconfig.eslint.json | 1 + .../mds-web-sockets/webpack.config.ts | 3 + container-images/webpack.config.js | 65 - cypress.json | 4 - cypress/integration/agency.spec.js | 18 - cypress/integration/cors-delete.no-spec.js | 19 - cypress/support/commands.js | 25 - cypress/support/index.js | 20 - docker-compose.yml.tpl | 116 - helm/curl/Chart.yaml | 5 + helm/curl/templates/pod.yaml | 24 + helm/dashboard/Chart.yaml | 5 - .../templates/kubernetes-dashboard.yaml | 162 - helm/dashboard/templates/service-account.yaml | 18 - helm/dns/Chart.yaml | 5 + helm/dns/templates/pod.yaml | 20 + helm/grafana/Chart.yaml | 19 + helm/grafana/README.md | 301 + helm/grafana/templates/NOTES.txt | 37 + helm/grafana/templates/_helpers.tpl | 195 + helm/grafana/templates/configmap.yaml | 18 + .../grafana/templates/dashboard-provider.yaml | 31 + helm/grafana/templates/deployment.yaml | 187 + helm/grafana/templates/ingress.yaml | 42 + helm/grafana/templates/pvc.yaml | 14 + helm/grafana/templates/secret.yaml | 18 + helm/grafana/templates/service.yaml | 30 + helm/grafana/templates/servicemonitor.yaml | 28 + helm/grafana/values-production.yaml | 327 + helm/grafana/values.yaml | 327 + helm/mds/.helmignore | 2 +- helm/mds/Chart.yaml | 2 +- helm/mds/requirements.lock | 8 +- helm/mds/requirements.yaml | 4 +- helm/mds/templates/auth.yaml | 101 +- helm/mds/templates/autoscaler.yaml | 12 +- helm/mds/templates/cronjob.yaml | 47 - helm/mds/templates/deployment.yaml | 54 +- helm/mds/templates/egress.yaml | 61 + helm/mds/templates/ingress.yaml | 30 +- helm/mds/templates/service.yaml | 3 +- .../tests/auth-agency-jwt-audience_test.yaml | 2 +- helm/mds/tests/auth-all-disabled_test.yaml | 17 + .../mds/tests/auth-all-jwt-audience_test.yaml | 2 +- ...l-default_test.yaml => auth-all_test.yaml} | 0 .../tests/auth-audit-jwt-audience_test.yaml | 2 +- .../auth-compliance-jwt-audience_test.yaml | 2 +- .../tests/auth-config-jwt-audience_test.yaml | 52 + .../tests/auth-daily-jwt-audience_test.yaml | 30 +- ...th-geography-author-jwt-audience_test.yaml | 64 + ... auth-jurisdiction-jwt-audience_test.yaml} | 38 +- .../auth-policy-author-jwt-audience_test.yaml | 64 + .../tests/auth-policy-jwt-audience_test.yaml | 32 +- ...ult_test.yaml => autoscaler-all_test.yaml} | 2 +- helm/mds/tests/autoscaler-daily_test.yaml | 34 +- .../autoscaler-geography-author_test.yaml | 90 + ...yaml => autoscaler-jurisdiction_test.yaml} | 48 +- .../tests/autoscaler-policy-author_test.yaml | 90 + helm/mds/tests/autoscaler-policy_test.yaml | 34 +- .../tests/autoscaler-web-sockets_test.yaml | 90 + .../mds/tests/cronjob-metrics-sheet_test.yaml | 108 - .../deployment-agency-no-cache_test.yaml | 164 - .../tests/deployment-agency-no-db_test.yaml | 128 - .../deployment-agency-no-events_test.yaml | 168 - ...l => deployment-agency-override_test.yaml} | 155 +- .../deployment-agency-registry_test.yaml | 179 - .../deployment-agency-resource-all_test.yaml | 183 - ..._test.yaml => deployment-agency_test.yaml} | 61 +- .../tests/deployment-all-no-cache_test.yaml | 10 - helm/mds/tests/deployment-all-no-db_test.yaml | 10 - .../tests/deployment-all-no-events_test.yaml | 10 - .../tests/deployment-all-registry_test.yaml | 12 - .../deployment-all-resource-all_test.yaml | 309 - .../mds/tests/deployment-all-secret_test.yaml | 12 - ...ult_test.yaml => deployment-all_test.yaml} | 2 +- helm/mds/tests/deployment-audit-env_test.yaml | 202 - .../tests/deployment-audit-no-cache_test.yaml | 164 - .../tests/deployment-audit-no-db_test.yaml | 128 - .../deployment-audit-no-events_test.yaml | 168 - .../tests/deployment-audit-registry_test.yaml | 179 - .../deployment-audit-resource-all_test.yaml | 183 - ...t_test.yaml => deployment-audit_test.yaml} | 63 +- .../tests/deployment-compliance-env_test.yaml | 202 - .../deployment-compliance-no-cache_test.yaml | 164 - .../deployment-compliance-no-db_test.yaml | 128 - .../deployment-compliance-no-events_test.yaml | 168 - .../deployment-compliance-registry_test.yaml | 179 - ...ployment-compliance-resource-all_test.yaml | 183 - ...t.yaml => deployment-compliance_test.yaml} | 63 +- ..._test.yaml => deployment-config_test.yaml} | 101 +- helm/mds/tests/deployment-daily-env_test.yaml | 202 - .../tests/deployment-daily-no-cache_test.yaml | 164 - .../tests/deployment-daily-no-db_test.yaml | 128 - .../tests/deployment-daily-registry_test.yaml | 179 - .../deployment-daily-resource-all_test.yaml | 183 - ...t_test.yaml => deployment-daily_test.yaml} | 75 +- ... => deployment-geography-author_test.yaml} | 77 +- ...yaml => deployment-jurisdiction_test.yaml} | 159 +- .../mds/tests/deployment-native-env_test.yaml | 202 - .../deployment-native-no-cache_test.yaml | 164 - .../tests/deployment-native-no-db_test.yaml | 128 - .../deployment-native-no-events_test.yaml | 168 - .../deployment-native-registry_test.yaml | 179 - .../deployment-native-resource-all_test.yaml | 183 - .../deployment-policy-author-env_test.yaml | 202 - ...eployment-policy-author-no-cache_test.yaml | 164 - .../deployment-policy-author-no-db_test.yaml | 128 - ...ployment-policy-author-no-events_test.yaml | 168 - ...yment-policy-author-resource-all_test.yaml | 183 - ...aml => deployment-policy-author_test.yaml} | 152 +- .../mds/tests/deployment-policy-env_test.yaml | 202 - .../deployment-policy-no-cache_test.yaml | 164 - .../tests/deployment-policy-no-db_test.yaml | 128 - .../deployment-policy-no-events_test.yaml | 168 - .../deployment-policy-registry_test.yaml | 179 - .../deployment-policy-resource-all_test.yaml | 183 - ..._test.yaml => deployment-policy_test.yaml} | 149 +- .../deployment-secret-override_test.yaml | 38 + helm/mds/tests/deployment-secret_test.yaml | 23 +- .../tests/deployment-web-sockets_test.yaml | 181 + ...default_test.yaml => egress-all_test.yaml} | 2 +- .../tests/egress-nats-natsNamespace_test.yaml | 68 + helm/mds/tests/egress-nats_test.yaml | 66 + ...ntbit_test.yaml => egress-other_test.yaml} | 46 +- .../egress-postgresql-host-reader_test.yaml | 38 +- helm/mds/tests/egress-postgresql_test.yaml | 22 +- helm/mds/tests/egress-redis_test.yaml | 20 +- ...t.yaml => ingress-agency-domain_test.yaml} | 19 +- helm/mds/tests/ingress-agency_test.yaml | 12 +- ...s-domain-lets-encrypt-production_test.yaml | 51 - ...ress-all-tls-domain-lets-encrypt_test.yaml | 50 - .../tests/ingress-all-tls-domain_test.yaml | 60 - ...-all-tls-lets-encrypt-production_test.yaml | 42 - .../ingress-all-tls-lets-encrypt_test.yaml | 41 - helm/mds/tests/ingress-all-tls_test.yaml | 59 - ...efault_test.yaml => ingress-all_test.yaml} | 6 +- ...st.yaml => ingress-audit-domain_test.yaml} | 19 +- helm/mds/tests/ingress-audit_test.yaml | 12 +- ...ml => ingress-compliance-domain_test.yaml} | 19 +- .../ingress-compliance-tls-domain_test.yaml | 94 - helm/mds/tests/ingress-compliance_test.yaml | 12 +- ...t.yaml => ingress-config-domain_test.yaml} | 21 +- ...ain_test.yaml => ingress-config_test.yaml} | 22 +- ...st.yaml => ingress-daily-domain_test.yaml} | 24 +- helm/mds/tests/ingress-daily_test.yaml | 54 +- .../tests/ingress-destination-rule_test.yaml | 38 + ...lt_test.yaml => ingress-gateway_test.yaml} | 30 +- ...ingress-geography-author-domain_test.yaml} | 66 +- .../tests/ingress-geography-author_test.yaml | 90 + ... => ingress-jurisdiction-domain_test.yaml} | 66 +- ...st.yaml => ingress-jurisdiction_test.yaml} | 65 +- .../tests/ingress-native-tls-domain_test.yaml | 94 - helm/mds/tests/ingress-native-tls_test.yaml | 93 - .../ingress-policy-author-domain_test.yaml | 92 + .../mds/tests/ingress-policy-author_test.yaml | 90 + ...t.yaml => ingress-policy-domain_test.yaml} | 62 +- helm/mds/tests/ingress-policy_test.yaml | 54 +- .../ingress-web-sockets-domain_test.yaml | 92 + helm/mds/tests/ingress-web-sockets_test.yaml | 90 + helm/mds/tests/service-agency_test.yaml | 2 +- helm/mds/tests/service-all-disabled_test.yaml | 30 + ...efault_test.yaml => service-all_test.yaml} | 2 +- helm/mds/tests/service-audit_test.yaml | 2 +- helm/mds/tests/service-compliance_test.yaml | 2 +- helm/mds/tests/service-daily_test.yaml | 18 +- .../tests/service-geography-author_test.yaml | 34 + ...st.yaml => service-jurisdiction_test.yaml} | 26 +- .../mds/tests/service-policy-author_test.yaml | 18 +- helm/mds/tests/service-policy_test.yaml | 18 +- helm/mds/tests/service-web-sockets_test.yaml | 34 + helm/mds/values.yaml | 34 +- helm/mds/values.yaml.tpl | 26 +- helm/monitoring/Chart.yaml | 5 + helm/monitoring/requirements.lock | 9 + helm/monitoring/requirements.yaml | 7 + helm/monitoring/values.yaml | 221 + helm/nats-account-server/Chart.yaml | 5 + .../templates/nats-account-server.yaml | 73 + helm/nats-box/Chart.yaml | 5 + helm/nats-box/templates/nats-box.yaml | 41 + helm/nats-init/Chart.yaml | 18 + .../templates/customresourcedefinition.yaml | 35 + helm/nats-init/templates/rbac.yaml | 108 + helm/nats-init/values.yaml | 184 + helm/nats-prometheus/Chart.yaml | 5 + .../templates/nats-prometheus.yml | 46 + helm/nats-server/Chart.yaml | 15 + helm/nats-server/templates/NOTES.txt | 26 + helm/nats-server/templates/_helpers.tpl | 22 + helm/nats-server/templates/configmap.yaml | 109 + helm/nats-server/templates/nats-box.yaml | 22 + helm/nats-server/templates/rbac.yaml | 31 + helm/nats-server/templates/service.yaml | 25 + helm/nats-server/templates/statefulset.yaml | 206 + helm/nats-server/values.yaml | 83 + helm/nats-surveyor-grafana/Chart.yaml | 5 + .../templates/nats-surveyor-grafana.yml | 55 + helm/nats-surveyor/Chart.yaml | 5 + .../nats-surveyor/templates/nats-surveyor.yml | 131 + helm/nats-surveyor/values.yaml | 9 + helm/prometheus-operator/Chart.yaml | 5 + .../templates/prometheus-operator.yml | 208 + lerna.json | 1 - nginx/nginx.conf | 3 - okteto.yml | 10 + package.json | 89 +- .../ladot-service-areas/la-city-boundary.js | 8773 ----------------- packages/ladot-service-areas/package.json | 14 - .../{mds-cache => mds-agency-cache}/index.ts | 283 +- .../package.json | 20 +- packages/mds-agency-cache/tsconfig.build.json | 12 + .../mds-agency-cache/tsconfig.eslint.json | 1 + .../{mds-cache => mds-agency-cache}/types.ts | 0 .../unflatteners.ts | 28 +- packages/mds-agency/README.md | 23 +- .../agency-candidate-request-handlers.ts | 9 +- packages/mds-agency/api.ts | 76 +- .../middleware/agency-api-version.ts | 23 + packages/mds-agency/middleware/index.ts | 17 + packages/mds-agency/package.json | 37 +- packages/mds-agency/request-handlers.ts | 329 +- .../sandbox-admin-request-handlers.ts | 27 +- packages/mds-agency/server.ts | 11 +- ...ion-tests.ts => integration-tests.spec.ts} | 253 +- ...t-handlers.ts => request-handlers.spec.ts} | 202 +- ...=> sandbox-admin-request-handlers.spec.ts} | 20 +- packages/mds-agency/tsconfig.build.json | 3 +- packages/mds-agency/tsconfig.eslint.json | 1 + packages/mds-agency/types.ts | 62 +- packages/mds-agency/utils.ts | 107 +- packages/mds-api-authorizer/index.ts | 102 +- packages/mds-api-authorizer/package.json | 22 +- .../mds-api-authorizer/tests/index.spec.ts | 106 +- .../mds-api-authorizer/tsconfig.build.json | 3 +- .../mds-api-authorizer/tsconfig.eslint.json | 1 + packages/mds-api-helpers/index.ts | 28 +- packages/mds-api-helpers/package.json | 13 +- packages/mds-api-helpers/tsconfig.build.json | 2 +- packages/mds-api-helpers/tsconfig.eslint.json | 1 + packages/mds-api-server/index.ts | 346 +- packages/mds-api-server/package.json | 21 +- packages/mds-api-server/tests/index.spec.ts | 98 +- packages/mds-api-server/tsconfig.build.json | 1 + packages/mds-api-server/tsconfig.eslint.json | 1 + packages/mds-audit/api.ts | 330 +- packages/mds-audit/attachments.ts | 173 + .../mds-audit/middleware/audit-api-version.ts | 23 + packages/mds-audit/middleware/index.ts | 17 + packages/mds-audit/package.json | 41 +- packages/mds-audit/server.ts | 11 +- packages/mds-audit/service.ts | 62 +- packages/mds-audit/tests/api.spec.ts | 400 +- packages/mds-audit/tests/attachments.spec.ts | 154 + packages/mds-audit/tests/empty.png | 0 packages/mds-audit/tests/sample.gif | Bin 0 -> 42 bytes packages/mds-audit/tests/sample.png | Bin 0 -> 68 bytes packages/mds-audit/tests/samplepng | Bin 0 -> 68 bytes packages/mds-audit/tsconfig.build.json | 3 +- packages/mds-audit/tsconfig.eslint.json | 1 + packages/mds-audit/types.ts | 146 +- packages/mds-compliance/api.ts | 148 +- packages/mds-compliance/mds-compliance-cli.ts | 22 +- .../mds-compliance/mds-compliance-engine.ts | 19 +- .../middleware/compliance-api-version.ts | 7 + packages/mds-compliance/middleware/index.ts | 1 + packages/mds-compliance/package.json | 36 +- packages/mds-compliance/server.ts | 11 +- .../test_data/low_limit_policy.json | 28 + .../mds-compliance/test_data/policies.json | 45 +- packages/mds-compliance/tests/api.spec.ts | 144 +- .../mds-compliance/tests/engine-test.spec.ts | 53 +- packages/mds-compliance/tsconfig.build.json | 4 +- packages/mds-compliance/tsconfig.eslint.json | 1 + packages/mds-compliance/types.ts | 30 +- packages/mds-compliance/validators.ts | 156 - packages/mds-daily/api.ts | 83 +- packages/mds-daily/db-helpers.ts | 33 +- packages/mds-daily/package.json | 34 +- packages/mds-daily/request-handlers.ts | 238 +- packages/mds-daily/server.ts | 11 +- .../tests/{test.ts => daily.spec.ts} | 2 +- ...{db-helpers.test.ts => db-helpers.spec.ts} | 39 - ...dlers.test.ts => request-handlers.spec.ts} | 4 +- .../tests/{utils.test.ts => utils.spec.ts} | 2 +- packages/mds-daily/tsconfig.build.json | 2 +- packages/mds-daily/tsconfig.eslint.json | 1 + packages/mds-daily/types.ts | 10 +- packages/mds-daily/utils.ts | 8 +- packages/mds-db/attachments.ts | 69 + packages/mds-db/audits.ts | 14 +- packages/mds-db/client.ts | 19 +- packages/mds-db/devices.ts | 35 +- packages/mds-db/events.ts | 37 +- packages/mds-db/geographies.ts | 164 +- packages/mds-db/index.ts | 180 +- packages/mds-db/migration.ts | 42 +- packages/mds-db/package.json | 18 +- packages/mds-db/policies.ts | 120 +- packages/mds-db/schema.ts | 123 +- packages/mds-db/sql-utils.ts | 14 +- packages/mds-db/stops.ts | 42 + packages/mds-db/telemetry.ts | 54 +- packages/mds-db/tests/mds-db.spec.ts | 236 +- packages/mds-db/tests/migration.spec.ts | 11 +- packages/mds-db/trips.ts | 82 +- packages/mds-db/tsconfig.eslint.json | 1 + packages/mds-db/types.ts | 31 +- packages/mds-geography-author/LICENSE | 201 + packages/mds-geography-author/README.md | 4 + packages/mds-geography-author/api.ts | 264 + packages/mds-geography-author/index.ts | 1 + .../geography-author-api-version.ts | 7 + .../mds-geography-author/middleware/index.ts | 1 + packages/mds-geography-author/package.json | 31 + .../server.ts | 15 +- .../mds-geography-author/tests/api.spec.ts | 521 + .../tsconfig.build.json | 1 + .../mds-geography-author/tsconfig.eslint.json | 1 + packages/mds-geography-author/types.ts | 55 + packages/mds-geography/LICENSE | 201 + packages/mds-geography/README.md | 4 + packages/mds-geography/api.ts | 100 + packages/mds-geography/index.ts | 1 + .../middleware/geography-api-version.ts | 7 + packages/mds-geography/middleware/index.ts | 1 + packages/mds-geography/package.json | 31 + packages/mds-geography/server.ts | 17 + packages/mds-geography/tests/api.spec.ts | 219 + packages/mds-geography/tsconfig.build.json | 16 + packages/mds-geography/tsconfig.eslint.json | 1 + packages/mds-geography/types.ts | 43 + .../mds-jurisdiction-service/@types/index.ts | 47 + .../mds-jurisdiction-service/client/index.ts | 30 + packages/mds-jurisdiction-service/index.ts | 18 + .../mds-jurisdiction-service/ormconfig.ts | 20 + .../mds-jurisdiction-service/package.json | 35 + .../mds-jurisdiction-service/server/index.ts | 20 + .../handlers/create-jurisdiction-handler.ts | 35 + .../handlers/create-jurisdictions-handler.ts | 35 + .../handlers/delete-jurisdiction-handler.ts | 34 + .../handlers/get-jurisdiction-handler.ts | 35 + .../handlers/get-jurisdictions-handler.ts | 34 + .../service/handlers/index.ts | 22 + .../handlers/update-jurisdiction-handler.ts | 35 + .../service/provider.ts | 26 + .../service/repository/entities/index.ts | 17 + .../entities/jurisdiction-entity.ts | 45 + .../service/repository/index.ts | 172 + .../service/repository/mappers.ts | 74 + .../1582294819607-CreateJurisdictionsTable.ts | 41 + .../service/repository/migrations/index.ts | 17 + .../service/validators.ts | 44 + .../tests/index.spec.ts | 236 + .../tsconfig.build.json | 14 + .../tsconfig.eslint.json | 1 + packages/mds-jurisdiction/@types/index.ts | 33 + packages/mds-jurisdiction/api.ts | 64 + .../handlers/create-jurisdiction.ts | 59 + .../handlers/delete-jurisdiction.ts | 43 + .../handlers/get-jurisdiction.ts | 52 + .../handlers/get-jurisdictions.ts | 38 + packages/mds-jurisdiction/handlers/index.ts | 21 + .../handlers/update-jurisdiction.ts | 54 + packages/mds-jurisdiction/handlers/utils.ts | 24 + .../{mds-native => mds-jurisdiction}/index.ts | 2 +- packages/mds-jurisdiction/middleware/index.ts | 17 + .../middleware/jurisdiction-api-version.ts | 23 + packages/mds-jurisdiction/package.json | 31 + packages/mds-jurisdiction/server.ts | 20 + packages/mds-jurisdiction/tests/api.spec.ts | 219 + packages/mds-jurisdiction/tsconfig.build.json | 15 + .../mds-jurisdiction/tsconfig.eslint.json | 1 + packages/mds-logger/index.ts | 228 +- packages/mds-logger/package.json | 11 +- packages/mds-logger/tests/index.spec.ts | 132 + packages/mds-logger/tests/test.ts | 114 - packages/mds-logger/tsconfig.eslint.json | 1 + packages/mds-metrics-sheet/index.ts | 3 +- packages/mds-metrics-sheet/metrics-log.ts | 25 +- packages/mds-metrics-sheet/package.json | 20 +- ...heet.test.ts => mds-metrics-sheet.spec.ts} | 63 +- .../mds-metrics-sheet/tsconfig.build.json | 3 +- .../mds-metrics-sheet/tsconfig.eslint.json | 1 + packages/mds-metrics-sheet/types.ts | 1 - packages/mds-metrics-sheet/utils.ts | 3 + packages/mds-metrics-sheet/vehicle-counts.ts | 135 - packages/mds-native/api.ts | 170 - packages/mds-native/package.json | 34 - packages/mds-native/tests/api.spec.ts | 242 - packages/mds-native/types.ts | 74 - packages/mds-policy-author/api.ts | 522 +- .../mds-policy-author/middleware/index.ts | 1 + .../middleware/policy-author-api-version.ts | 7 + packages/mds-policy-author/package.json | 37 +- packages/mds-policy-author/server.ts | 11 +- packages/mds-policy-author/tests/api.spec.ts | 567 +- .../mds-policy-author/tsconfig.build.json | 4 +- .../mds-policy-author/tsconfig.eslint.json | 1 + packages/mds-policy-author/types.ts | 33 +- packages/mds-policy/README.md | 16 +- packages/mds-policy/api.ts | 226 +- packages/mds-policy/middleware/index.ts | 1 + .../middleware/policy-api-version.ts | 7 + packages/mds-policy/package.json | 36 +- packages/mds-policy/server.ts | 11 +- packages/mds-policy/tests/api.spec.ts | 230 +- packages/mds-policy/tsconfig.build.json | 1 + packages/mds-policy/tsconfig.eslint.json | 1 + packages/mds-policy/types.ts | 35 +- packages/mds-providers/package.json | 6 +- packages/mds-providers/tsconfig.eslint.json | 1 + packages/mds-repository/@types/index.ts | 28 + packages/mds-repository/connection.ts | 129 + packages/mds-repository/exceptions.ts | 36 + packages/mds-repository/filters.ts | 25 + packages/mds-repository/index.ts | 23 + packages/mds-repository/mapper.ts | 25 + packages/mds-repository/migration.ts | 39 + .../mds-repository/mixins/identity-column.ts | 36 + packages/mds-repository/mixins/index.ts | 18 + .../mds-repository/mixins/recorded-column.ts | 40 + .../mds-repository/naming-strategies/index.ts | 17 + .../naming-strategies/mds-naming-strategy.ts | 34 + packages/mds-repository/package.json | 32 + packages/mds-repository/repository.ts | 126 + .../mds-repository/tests/connection.spec.ts | 40 + .../mds-repository/tests/exceptions.spec.ts | 26 + packages/mds-repository/tests/filters.spec.ts | 59 + packages/mds-repository/tests/mapper.spec.ts | 37 + .../tests/naming-strategies.spec.ts | 37 + .../mds-repository/tests/transformers.spec.ts | 26 +- .../transformers/bigint-transformer.ts | 23 + packages/mds-repository/transformers/index.ts | 17 + packages/mds-repository/tsconfig.build.json | 13 + packages/mds-repository/tsconfig.eslint.json | 1 + packages/mds-schema-validators/index.ts | 1 + packages/mds-schema-validators/package.json | 31 + .../tests/validators.spec.ts | 97 +- .../mds-schema-validators/tsconfig.build.json | 11 + .../tsconfig.eslint.json | 1 + packages/mds-schema-validators/validators.ts | 432 + packages/mds-service-helpers/@types/index.ts | 57 + packages/mds-service-helpers/client/index.ts | 41 + packages/mds-service-helpers/index.ts | 19 + packages/mds-service-helpers/package.json | 28 + packages/mds-service-helpers/server/index.ts | 153 + .../mds-service-helpers/tests/index.spec.ts | 104 + .../tsconfig.build.json | 0 .../mds-service-helpers/tsconfig.eslint.json | 1 + .../mds-stream/agency-stream-interface.ts | 19 +- packages/mds-stream/helpers.ts | 0 packages/mds-stream/index.ts | 84 +- .../mds-stream/kafka/agency-stream-kafka.ts | 39 + .../mds-stream/kafka/helpers.ts | 9 +- packages/mds-stream/kafka/index.ts | 18 + packages/mds-stream/kafka/stream-consumer.ts | 70 + packages/mds-stream/kafka/stream-producer.ts | 77 + .../mds-stream/nats/agency-stream-nats.ts | 23 + packages/mds-stream/nats/helpers.ts | 35 + packages/mds-stream/nats/stream-consumer.ts | 23 + packages/mds-stream/nats/stream-producer.ts | 27 + packages/mds-stream/package.json | 20 +- .../mds-stream/stream-interface.ts | 20 +- packages/mds-stream/tsconfig.build.json | 6 +- packages/mds-stream/tsconfig.eslint.json | 1 + packages/mds-stream/types.ts | 20 +- packages/mds-test-data/index.ts | 167 +- packages/mds-test-data/package.json | 13 +- .../test-areas/council-district-11.ts} | 6 +- .../test-areas}/la-city-boundary.ts | 24 +- .../test-areas/la-dacs.ts} | 6 +- .../test-areas/non-san-fernando-dac.ts} | 6 +- .../test-areas/restricted-areas.ts} | 6 +- .../test-areas/san-fernando-dac.ts} | 6 +- .../test-areas/service-area-new.ts} | 6 +- .../test-areas/service-area-old.ts} | 6 +- .../test-areas/service-areas.ts} | 6 +- .../test-areas/test-areas.ts} | 56 +- .../test-areas/venice-special-ops-zone.ts} | 6 +- .../test-areas/venice.ts} | 6 +- packages/mds-test-data/tsconfig.build.json | 2 + packages/mds-test-data/tsconfig.eslint.json | 1 + packages/mds-types/index.ts | 133 +- packages/mds-types/package.json | 9 +- packages/mds-types/scopes.ts | 51 - packages/mds-types/tsconfig.eslint.json | 1 + packages/mds-utils/date-time-utils.ts | 148 + .../exceptions/exception-messages.ts | 3 + .../mds-utils/{ => exceptions}/exceptions.ts | 46 +- packages/mds-utils/index.ts | 9 +- packages/mds-utils/package.json | 21 +- packages/mds-utils/state-machine.ts | 76 + .../mds-utils/tests/date-time-utils.spec.ts | 73 + .../tests/state-transition-expected.ts | 232 + packages/mds-utils/tests/utils.spec.ts | 47 +- packages/mds-utils/tsconfig.build.json | 5 +- packages/mds-utils/tsconfig.eslint.json | 1 + packages/mds-utils/utils.ts | 221 +- packages/mds-utils/validators.ts | 176 - packages/mds-web-sockets/client.ts | 66 + packages/mds-web-sockets/clients.ts | 99 + packages/mds-web-sockets/index.ts | 4 + packages/mds-web-sockets/package.json | 46 + packages/mds-web-sockets/server.ts | 6 + packages/mds-web-sockets/tests/ws.spec.ts | 107 + packages/mds-web-sockets/tsconfig.build.json | 14 + packages/mds-web-sockets/tsconfig.eslint.json | 1 + packages/mds-web-sockets/types.ts | 2 + packages/mds-web-sockets/ws-server.ts | 110 + packages/mds-webpack-config/index.ts | 142 + packages/mds-webpack-config/package.json | 30 + .../mds-webpack-config}/tsconfig.build.json | 0 .../mds-webpack-config/tsconfig.eslint.json | 1 + tsconfig.build.json | 5 +- tsconfig.eslint.json | 9 + tsconfig.json | 14 +- tsconfig.settings.json | 11 - yarn.lock | 6723 +++++++------ 607 files changed, 23894 insertions(+), 27085 deletions(-) delete mode 100644 .eslintrc.js create mode 100644 .eslintrc.json create mode 100644 .mocharc.json create mode 100644 .nycrc.json delete mode 100644 .prettierrc.js create mode 100644 .prettierrc.json create mode 100644 .stignore delete mode 100644 @types/cloudevents-sdk.d.ts delete mode 100644 @types/ladot-service-areas.d.ts delete mode 100644 @types/pushover-notifications.d.ts create mode 100644 @types/wrapper-webpack-plugin.d.ts delete mode 100644 container-images/env-inject/index.ts delete mode 100644 container-images/env-inject/package.json delete mode 100644 container-images/mds-agency/tsconfig.build.json create mode 100644 container-images/mds-agency/tsconfig.eslint.json delete mode 100644 container-images/mds-agency/webpack.config.js create mode 100644 container-images/mds-agency/webpack.config.ts delete mode 100644 container-images/mds-audit/tsconfig.build.json create mode 100644 container-images/mds-audit/tsconfig.eslint.json delete mode 100644 container-images/mds-audit/webpack.config.js create mode 100644 container-images/mds-audit/webpack.config.ts delete mode 100644 container-images/mds-compliance/index.ts delete mode 100644 container-images/mds-compliance/tsconfig.build.json create mode 100644 container-images/mds-compliance/tsconfig.eslint.json delete mode 100644 container-images/mds-compliance/webpack.config.js create mode 100644 container-images/mds-compliance/webpack.config.ts delete mode 100644 container-images/mds-daily/tsconfig.build.json create mode 100644 container-images/mds-daily/tsconfig.eslint.json delete mode 100644 container-images/mds-daily/webpack.config.js create mode 100644 container-images/mds-daily/webpack.config.ts create mode 100644 container-images/mds-geography-author/Dockerfile create mode 100644 container-images/mds-geography-author/package.json create mode 100644 container-images/mds-geography-author/tsconfig.eslint.json create mode 100644 container-images/mds-geography-author/webpack.config.ts create mode 100644 container-images/mds-geography/Dockerfile create mode 100644 container-images/mds-geography/package.json create mode 100644 container-images/mds-geography/tsconfig.eslint.json create mode 100644 container-images/mds-geography/webpack.config.ts create mode 100644 container-images/mds-jurisdiction-service/Dockerfile create mode 100644 container-images/mds-jurisdiction-service/package.json create mode 100644 container-images/mds-jurisdiction-service/tsconfig.eslint.json create mode 100644 container-images/mds-jurisdiction-service/webpack.config.ts create mode 100644 container-images/mds-jurisdiction/Dockerfile create mode 100644 container-images/mds-jurisdiction/package.json create mode 100644 container-images/mds-jurisdiction/tsconfig.eslint.json create mode 100644 container-images/mds-jurisdiction/webpack.config.ts delete mode 100644 container-images/mds-metrics-sheet/tsconfig.build.json create mode 100644 container-images/mds-metrics-sheet/tsconfig.eslint.json delete mode 100644 container-images/mds-metrics-sheet/webpack.config.js create mode 100644 container-images/mds-metrics-sheet/webpack.config.ts delete mode 100644 container-images/mds-native/Dockerfile delete mode 100644 container-images/mds-native/index.ts delete mode 100644 container-images/mds-native/package.json delete mode 100644 container-images/mds-native/tsconfig.build.json delete mode 100644 container-images/mds-native/webpack.config.js delete mode 100644 container-images/mds-policy-author/index.ts delete mode 100644 container-images/mds-policy-author/tsconfig.build.json create mode 100644 container-images/mds-policy-author/tsconfig.eslint.json delete mode 100644 container-images/mds-policy-author/webpack.config.js create mode 100644 container-images/mds-policy-author/webpack.config.ts delete mode 100644 container-images/mds-policy/index.ts delete mode 100644 container-images/mds-policy/tsconfig.build.json create mode 100644 container-images/mds-policy/tsconfig.eslint.json delete mode 100644 container-images/mds-policy/webpack.config.js create mode 100644 container-images/mds-policy/webpack.config.ts create mode 100644 container-images/mds-web-sockets/Dockerfile create mode 100644 container-images/mds-web-sockets/package.json create mode 100644 container-images/mds-web-sockets/tsconfig.eslint.json create mode 100644 container-images/mds-web-sockets/webpack.config.ts delete mode 100644 container-images/webpack.config.js delete mode 100644 cypress.json delete mode 100644 cypress/integration/agency.spec.js delete mode 100644 cypress/integration/cors-delete.no-spec.js delete mode 100644 cypress/support/commands.js delete mode 100644 cypress/support/index.js delete mode 100644 docker-compose.yml.tpl create mode 100644 helm/curl/Chart.yaml create mode 100644 helm/curl/templates/pod.yaml delete mode 100644 helm/dashboard/Chart.yaml delete mode 100644 helm/dashboard/templates/kubernetes-dashboard.yaml delete mode 100644 helm/dashboard/templates/service-account.yaml create mode 100644 helm/dns/Chart.yaml create mode 100644 helm/dns/templates/pod.yaml create mode 100755 helm/grafana/Chart.yaml create mode 100755 helm/grafana/README.md create mode 100755 helm/grafana/templates/NOTES.txt create mode 100755 helm/grafana/templates/_helpers.tpl create mode 100755 helm/grafana/templates/configmap.yaml create mode 100755 helm/grafana/templates/dashboard-provider.yaml create mode 100755 helm/grafana/templates/deployment.yaml create mode 100755 helm/grafana/templates/ingress.yaml create mode 100755 helm/grafana/templates/pvc.yaml create mode 100755 helm/grafana/templates/secret.yaml create mode 100755 helm/grafana/templates/service.yaml create mode 100755 helm/grafana/templates/servicemonitor.yaml create mode 100755 helm/grafana/values-production.yaml create mode 100755 helm/grafana/values.yaml delete mode 100644 helm/mds/templates/cronjob.yaml create mode 100644 helm/mds/tests/auth-all-disabled_test.yaml rename helm/mds/tests/{auth-all-default_test.yaml => auth-all_test.yaml} (100%) create mode 100644 helm/mds/tests/auth-config-jwt-audience_test.yaml create mode 100644 helm/mds/tests/auth-geography-author-jwt-audience_test.yaml rename helm/mds/tests/{auth-native-jwt-audience_test.yaml => auth-jurisdiction-jwt-audience_test.yaml} (69%) create mode 100644 helm/mds/tests/auth-policy-author-jwt-audience_test.yaml rename helm/mds/tests/{autoscaler-all-default_test.yaml => autoscaler-all_test.yaml} (87%) create mode 100644 helm/mds/tests/autoscaler-geography-author_test.yaml rename helm/mds/tests/{autoscaler-native_test.yaml => autoscaler-jurisdiction_test.yaml} (72%) create mode 100644 helm/mds/tests/autoscaler-policy-author_test.yaml create mode 100644 helm/mds/tests/autoscaler-web-sockets_test.yaml delete mode 100644 helm/mds/tests/cronjob-metrics-sheet_test.yaml delete mode 100644 helm/mds/tests/deployment-agency-no-cache_test.yaml delete mode 100644 helm/mds/tests/deployment-agency-no-db_test.yaml delete mode 100644 helm/mds/tests/deployment-agency-no-events_test.yaml rename helm/mds/tests/{deployment-agency-env_test.yaml => deployment-agency-override_test.yaml} (61%) delete mode 100644 helm/mds/tests/deployment-agency-registry_test.yaml delete mode 100644 helm/mds/tests/deployment-agency-resource-all_test.yaml rename helm/mds/tests/{deployment-agency-default_test.yaml => deployment-agency_test.yaml} (88%) delete mode 100644 helm/mds/tests/deployment-all-no-cache_test.yaml delete mode 100644 helm/mds/tests/deployment-all-no-db_test.yaml delete mode 100644 helm/mds/tests/deployment-all-no-events_test.yaml delete mode 100644 helm/mds/tests/deployment-all-registry_test.yaml delete mode 100644 helm/mds/tests/deployment-all-resource-all_test.yaml delete mode 100644 helm/mds/tests/deployment-all-secret_test.yaml rename helm/mds/tests/{deployment-all-default_test.yaml => deployment-all_test.yaml} (88%) delete mode 100644 helm/mds/tests/deployment-audit-env_test.yaml delete mode 100644 helm/mds/tests/deployment-audit-no-cache_test.yaml delete mode 100644 helm/mds/tests/deployment-audit-no-db_test.yaml delete mode 100644 helm/mds/tests/deployment-audit-no-events_test.yaml delete mode 100644 helm/mds/tests/deployment-audit-registry_test.yaml delete mode 100644 helm/mds/tests/deployment-audit-resource-all_test.yaml rename helm/mds/tests/{deployment-audit-default_test.yaml => deployment-audit_test.yaml} (88%) delete mode 100644 helm/mds/tests/deployment-compliance-env_test.yaml delete mode 100644 helm/mds/tests/deployment-compliance-no-cache_test.yaml delete mode 100644 helm/mds/tests/deployment-compliance-no-db_test.yaml delete mode 100644 helm/mds/tests/deployment-compliance-no-events_test.yaml delete mode 100644 helm/mds/tests/deployment-compliance-registry_test.yaml delete mode 100644 helm/mds/tests/deployment-compliance-resource-all_test.yaml rename helm/mds/tests/{deployment-compliance-default_test.yaml => deployment-compliance_test.yaml} (88%) rename helm/mds/tests/{deployment-daily-no-events_test.yaml => deployment-config_test.yaml} (77%) delete mode 100644 helm/mds/tests/deployment-daily-env_test.yaml delete mode 100644 helm/mds/tests/deployment-daily-no-cache_test.yaml delete mode 100644 helm/mds/tests/deployment-daily-no-db_test.yaml delete mode 100644 helm/mds/tests/deployment-daily-registry_test.yaml delete mode 100644 helm/mds/tests/deployment-daily-resource-all_test.yaml rename helm/mds/tests/{deployment-native-default_test.yaml => deployment-daily_test.yaml} (84%) rename helm/mds/tests/{deployment-policy-author-default_test.yaml => deployment-geography-author_test.yaml} (82%) rename helm/mds/tests/{deployment-daily-default_test.yaml => deployment-jurisdiction_test.yaml} (65%) delete mode 100644 helm/mds/tests/deployment-native-env_test.yaml delete mode 100644 helm/mds/tests/deployment-native-no-cache_test.yaml delete mode 100644 helm/mds/tests/deployment-native-no-db_test.yaml delete mode 100644 helm/mds/tests/deployment-native-no-events_test.yaml delete mode 100644 helm/mds/tests/deployment-native-registry_test.yaml delete mode 100644 helm/mds/tests/deployment-native-resource-all_test.yaml delete mode 100644 helm/mds/tests/deployment-policy-author-env_test.yaml delete mode 100644 helm/mds/tests/deployment-policy-author-no-cache_test.yaml delete mode 100644 helm/mds/tests/deployment-policy-author-no-db_test.yaml delete mode 100644 helm/mds/tests/deployment-policy-author-no-events_test.yaml delete mode 100644 helm/mds/tests/deployment-policy-author-resource-all_test.yaml rename helm/mds/tests/{deployment-policy-author-registry_test.yaml => deployment-policy-author_test.yaml} (67%) delete mode 100644 helm/mds/tests/deployment-policy-env_test.yaml delete mode 100644 helm/mds/tests/deployment-policy-no-cache_test.yaml delete mode 100644 helm/mds/tests/deployment-policy-no-db_test.yaml delete mode 100644 helm/mds/tests/deployment-policy-no-events_test.yaml delete mode 100644 helm/mds/tests/deployment-policy-registry_test.yaml delete mode 100644 helm/mds/tests/deployment-policy-resource-all_test.yaml rename helm/mds/tests/{deployment-policy-default_test.yaml => deployment-policy_test.yaml} (67%) create mode 100644 helm/mds/tests/deployment-secret-override_test.yaml create mode 100644 helm/mds/tests/deployment-web-sockets_test.yaml rename helm/mds/tests/{egress-all-default_test.yaml => egress-all_test.yaml} (87%) create mode 100644 helm/mds/tests/egress-nats-natsNamespace_test.yaml create mode 100644 helm/mds/tests/egress-nats_test.yaml rename helm/mds/tests/{egress-fluentbit_test.yaml => egress-other_test.yaml} (55%) rename helm/mds/tests/{ingress-agency-tls_test.yaml => ingress-agency-domain_test.yaml} (92%) delete mode 100644 helm/mds/tests/ingress-all-tls-domain-lets-encrypt-production_test.yaml delete mode 100644 helm/mds/tests/ingress-all-tls-domain-lets-encrypt_test.yaml delete mode 100644 helm/mds/tests/ingress-all-tls-domain_test.yaml delete mode 100644 helm/mds/tests/ingress-all-tls-lets-encrypt-production_test.yaml delete mode 100644 helm/mds/tests/ingress-all-tls-lets-encrypt_test.yaml delete mode 100644 helm/mds/tests/ingress-all-tls_test.yaml rename helm/mds/tests/{cronjob-default_test.yaml => ingress-all_test.yaml} (61%) rename helm/mds/tests/{ingress-audit-tls_test.yaml => ingress-audit-domain_test.yaml} (92%) rename helm/mds/tests/{ingress-compliance-tls_test.yaml => ingress-compliance-domain_test.yaml} (92%) delete mode 100644 helm/mds/tests/ingress-compliance-tls-domain_test.yaml rename helm/mds/tests/{ingress-daily-tls_test.yaml => ingress-config-domain_test.yaml} (81%) rename helm/mds/tests/{ingress-daily-tls-domain_test.yaml => ingress-config_test.yaml} (80%) rename helm/mds/tests/{ingress-native_test.yaml => ingress-daily-domain_test.yaml} (88%) create mode 100644 helm/mds/tests/ingress-destination-rule_test.yaml rename helm/mds/tests/{ingress-all-default_test.yaml => ingress-gateway_test.yaml} (70%) rename helm/mds/tests/{ingress-agency-tls-domain_test.yaml => ingress-geography-author-domain_test.yaml} (66%) create mode 100644 helm/mds/tests/ingress-geography-author_test.yaml rename helm/mds/tests/{ingress-audit-tls-domain_test.yaml => ingress-jurisdiction-domain_test.yaml} (67%) rename helm/mds/tests/{ingress-policy-tls_test.yaml => ingress-jurisdiction_test.yaml} (68%) delete mode 100644 helm/mds/tests/ingress-native-tls-domain_test.yaml delete mode 100644 helm/mds/tests/ingress-native-tls_test.yaml create mode 100644 helm/mds/tests/ingress-policy-author-domain_test.yaml create mode 100644 helm/mds/tests/ingress-policy-author_test.yaml rename helm/mds/tests/{ingress-policy-tls-domain_test.yaml => ingress-policy-domain_test.yaml} (70%) create mode 100644 helm/mds/tests/ingress-web-sockets-domain_test.yaml create mode 100644 helm/mds/tests/ingress-web-sockets_test.yaml create mode 100644 helm/mds/tests/service-all-disabled_test.yaml rename helm/mds/tests/{service-all-default_test.yaml => service-all_test.yaml} (89%) create mode 100644 helm/mds/tests/service-geography-author_test.yaml rename helm/mds/tests/{service-native_test.yaml => service-jurisdiction_test.yaml} (54%) create mode 100644 helm/mds/tests/service-web-sockets_test.yaml create mode 100644 helm/monitoring/Chart.yaml create mode 100644 helm/monitoring/requirements.lock create mode 100644 helm/monitoring/requirements.yaml create mode 100644 helm/monitoring/values.yaml create mode 100644 helm/nats-account-server/Chart.yaml create mode 100644 helm/nats-account-server/templates/nats-account-server.yaml create mode 100644 helm/nats-box/Chart.yaml create mode 100644 helm/nats-box/templates/nats-box.yaml create mode 100644 helm/nats-init/Chart.yaml create mode 100644 helm/nats-init/templates/customresourcedefinition.yaml create mode 100644 helm/nats-init/templates/rbac.yaml create mode 100644 helm/nats-init/values.yaml create mode 100644 helm/nats-prometheus/Chart.yaml create mode 100644 helm/nats-prometheus/templates/nats-prometheus.yml create mode 100644 helm/nats-server/Chart.yaml create mode 100644 helm/nats-server/templates/NOTES.txt create mode 100644 helm/nats-server/templates/_helpers.tpl create mode 100644 helm/nats-server/templates/configmap.yaml create mode 100644 helm/nats-server/templates/nats-box.yaml create mode 100644 helm/nats-server/templates/rbac.yaml create mode 100644 helm/nats-server/templates/service.yaml create mode 100644 helm/nats-server/templates/statefulset.yaml create mode 100644 helm/nats-server/values.yaml create mode 100644 helm/nats-surveyor-grafana/Chart.yaml create mode 100644 helm/nats-surveyor-grafana/templates/nats-surveyor-grafana.yml create mode 100644 helm/nats-surveyor/Chart.yaml create mode 100644 helm/nats-surveyor/templates/nats-surveyor.yml create mode 100644 helm/nats-surveyor/values.yaml create mode 100644 helm/prometheus-operator/Chart.yaml create mode 100644 helm/prometheus-operator/templates/prometheus-operator.yml create mode 100644 okteto.yml delete mode 100644 packages/ladot-service-areas/la-city-boundary.js delete mode 100644 packages/ladot-service-areas/package.json rename packages/{mds-cache => mds-agency-cache}/index.ts (62%) rename packages/{mds-cache => mds-agency-cache}/package.json (51%) create mode 100644 packages/mds-agency-cache/tsconfig.build.json create mode 100644 packages/mds-agency-cache/tsconfig.eslint.json rename packages/{mds-cache => mds-agency-cache}/types.ts (100%) rename packages/{mds-cache => mds-agency-cache}/unflatteners.ts (74%) create mode 100644 packages/mds-agency/middleware/agency-api-version.ts create mode 100644 packages/mds-agency/middleware/index.ts rename packages/mds-agency/tests/{db-integration-tests.ts => integration-tests.spec.ts} (89%) rename packages/mds-agency/tests/{request-handlers.ts => request-handlers.spec.ts} (72%) rename packages/mds-agency/tests/{sandbox-admin-request-handlers.ts => sandbox-admin-request-handlers.spec.ts} (92%) create mode 100644 packages/mds-agency/tsconfig.eslint.json create mode 100644 packages/mds-api-authorizer/tsconfig.eslint.json create mode 100644 packages/mds-api-helpers/tsconfig.eslint.json create mode 100644 packages/mds-api-server/tsconfig.eslint.json create mode 100644 packages/mds-audit/attachments.ts create mode 100644 packages/mds-audit/middleware/audit-api-version.ts create mode 100644 packages/mds-audit/middleware/index.ts create mode 100644 packages/mds-audit/tests/attachments.spec.ts create mode 100644 packages/mds-audit/tests/empty.png create mode 100644 packages/mds-audit/tests/sample.gif create mode 100644 packages/mds-audit/tests/sample.png create mode 100644 packages/mds-audit/tests/samplepng create mode 100644 packages/mds-audit/tsconfig.eslint.json create mode 100644 packages/mds-compliance/middleware/compliance-api-version.ts create mode 100644 packages/mds-compliance/middleware/index.ts create mode 100644 packages/mds-compliance/test_data/low_limit_policy.json create mode 100644 packages/mds-compliance/tsconfig.eslint.json delete mode 100644 packages/mds-compliance/validators.ts rename packages/mds-daily/tests/{test.ts => daily.spec.ts} (99%) rename packages/mds-daily/tests/{db-helpers.test.ts => db-helpers.spec.ts} (87%) rename packages/mds-daily/tests/{request-handlers.test.ts => request-handlers.spec.ts} (92%) rename packages/mds-daily/tests/{utils.test.ts => utils.spec.ts} (97%) create mode 100644 packages/mds-daily/tsconfig.eslint.json create mode 100644 packages/mds-db/attachments.ts create mode 100644 packages/mds-db/stops.ts create mode 100644 packages/mds-db/tsconfig.eslint.json create mode 100644 packages/mds-geography-author/LICENSE create mode 100644 packages/mds-geography-author/README.md create mode 100644 packages/mds-geography-author/api.ts create mode 100644 packages/mds-geography-author/index.ts create mode 100644 packages/mds-geography-author/middleware/geography-author-api-version.ts create mode 100644 packages/mds-geography-author/middleware/index.ts create mode 100644 packages/mds-geography-author/package.json rename packages/{mds-native => mds-geography-author}/server.ts (64%) create mode 100644 packages/mds-geography-author/tests/api.spec.ts rename packages/{mds-native => mds-geography-author}/tsconfig.build.json (88%) create mode 100644 packages/mds-geography-author/tsconfig.eslint.json create mode 100644 packages/mds-geography-author/types.ts create mode 100644 packages/mds-geography/LICENSE create mode 100644 packages/mds-geography/README.md create mode 100644 packages/mds-geography/api.ts create mode 100644 packages/mds-geography/index.ts create mode 100644 packages/mds-geography/middleware/geography-api-version.ts create mode 100644 packages/mds-geography/middleware/index.ts create mode 100644 packages/mds-geography/package.json create mode 100644 packages/mds-geography/server.ts create mode 100644 packages/mds-geography/tests/api.spec.ts create mode 100644 packages/mds-geography/tsconfig.build.json create mode 100644 packages/mds-geography/tsconfig.eslint.json create mode 100644 packages/mds-geography/types.ts create mode 100644 packages/mds-jurisdiction-service/@types/index.ts create mode 100644 packages/mds-jurisdiction-service/client/index.ts create mode 100644 packages/mds-jurisdiction-service/index.ts create mode 100644 packages/mds-jurisdiction-service/ormconfig.ts create mode 100644 packages/mds-jurisdiction-service/package.json create mode 100644 packages/mds-jurisdiction-service/server/index.ts create mode 100644 packages/mds-jurisdiction-service/service/handlers/create-jurisdiction-handler.ts create mode 100644 packages/mds-jurisdiction-service/service/handlers/create-jurisdictions-handler.ts create mode 100644 packages/mds-jurisdiction-service/service/handlers/delete-jurisdiction-handler.ts create mode 100644 packages/mds-jurisdiction-service/service/handlers/get-jurisdiction-handler.ts create mode 100644 packages/mds-jurisdiction-service/service/handlers/get-jurisdictions-handler.ts create mode 100644 packages/mds-jurisdiction-service/service/handlers/index.ts create mode 100644 packages/mds-jurisdiction-service/service/handlers/update-jurisdiction-handler.ts create mode 100644 packages/mds-jurisdiction-service/service/provider.ts create mode 100644 packages/mds-jurisdiction-service/service/repository/entities/index.ts create mode 100644 packages/mds-jurisdiction-service/service/repository/entities/jurisdiction-entity.ts create mode 100644 packages/mds-jurisdiction-service/service/repository/index.ts create mode 100644 packages/mds-jurisdiction-service/service/repository/mappers.ts create mode 100644 packages/mds-jurisdiction-service/service/repository/migrations/1582294819607-CreateJurisdictionsTable.ts create mode 100644 packages/mds-jurisdiction-service/service/repository/migrations/index.ts create mode 100644 packages/mds-jurisdiction-service/service/validators.ts create mode 100644 packages/mds-jurisdiction-service/tests/index.spec.ts create mode 100644 packages/mds-jurisdiction-service/tsconfig.build.json create mode 100644 packages/mds-jurisdiction-service/tsconfig.eslint.json create mode 100644 packages/mds-jurisdiction/@types/index.ts create mode 100644 packages/mds-jurisdiction/api.ts create mode 100644 packages/mds-jurisdiction/handlers/create-jurisdiction.ts create mode 100644 packages/mds-jurisdiction/handlers/delete-jurisdiction.ts create mode 100644 packages/mds-jurisdiction/handlers/get-jurisdiction.ts create mode 100644 packages/mds-jurisdiction/handlers/get-jurisdictions.ts create mode 100644 packages/mds-jurisdiction/handlers/index.ts create mode 100644 packages/mds-jurisdiction/handlers/update-jurisdiction.ts create mode 100644 packages/mds-jurisdiction/handlers/utils.ts rename packages/{mds-native => mds-jurisdiction}/index.ts (92%) create mode 100644 packages/mds-jurisdiction/middleware/index.ts create mode 100644 packages/mds-jurisdiction/middleware/jurisdiction-api-version.ts create mode 100644 packages/mds-jurisdiction/package.json create mode 100644 packages/mds-jurisdiction/server.ts create mode 100644 packages/mds-jurisdiction/tests/api.spec.ts create mode 100644 packages/mds-jurisdiction/tsconfig.build.json create mode 100644 packages/mds-jurisdiction/tsconfig.eslint.json create mode 100644 packages/mds-logger/tests/index.spec.ts delete mode 100644 packages/mds-logger/tests/test.ts create mode 100644 packages/mds-logger/tsconfig.eslint.json rename packages/mds-metrics-sheet/tests/{mds-metrics-sheet.test.ts => mds-metrics-sheet.spec.ts} (78%) create mode 100644 packages/mds-metrics-sheet/tsconfig.eslint.json delete mode 100644 packages/mds-metrics-sheet/vehicle-counts.ts delete mode 100644 packages/mds-native/api.ts delete mode 100644 packages/mds-native/package.json delete mode 100644 packages/mds-native/tests/api.spec.ts delete mode 100644 packages/mds-native/types.ts create mode 100644 packages/mds-policy-author/middleware/index.ts create mode 100644 packages/mds-policy-author/middleware/policy-author-api-version.ts create mode 100644 packages/mds-policy-author/tsconfig.eslint.json create mode 100644 packages/mds-policy/middleware/index.ts create mode 100644 packages/mds-policy/middleware/policy-api-version.ts create mode 100644 packages/mds-policy/tsconfig.eslint.json create mode 100644 packages/mds-providers/tsconfig.eslint.json create mode 100644 packages/mds-repository/@types/index.ts create mode 100644 packages/mds-repository/connection.ts create mode 100644 packages/mds-repository/exceptions.ts create mode 100644 packages/mds-repository/filters.ts create mode 100644 packages/mds-repository/index.ts create mode 100644 packages/mds-repository/mapper.ts create mode 100644 packages/mds-repository/migration.ts create mode 100644 packages/mds-repository/mixins/identity-column.ts create mode 100644 packages/mds-repository/mixins/index.ts create mode 100644 packages/mds-repository/mixins/recorded-column.ts create mode 100644 packages/mds-repository/naming-strategies/index.ts create mode 100644 packages/mds-repository/naming-strategies/mds-naming-strategy.ts create mode 100644 packages/mds-repository/package.json create mode 100644 packages/mds-repository/repository.ts create mode 100644 packages/mds-repository/tests/connection.spec.ts create mode 100644 packages/mds-repository/tests/exceptions.spec.ts create mode 100644 packages/mds-repository/tests/filters.spec.ts create mode 100644 packages/mds-repository/tests/mapper.spec.ts create mode 100644 packages/mds-repository/tests/naming-strategies.spec.ts rename container-images/mds-agency/index.ts => packages/mds-repository/tests/transformers.spec.ts (51%) create mode 100644 packages/mds-repository/transformers/bigint-transformer.ts create mode 100644 packages/mds-repository/transformers/index.ts create mode 100644 packages/mds-repository/tsconfig.build.json create mode 100644 packages/mds-repository/tsconfig.eslint.json create mode 100644 packages/mds-schema-validators/index.ts create mode 100644 packages/mds-schema-validators/package.json rename packages/{mds-utils => mds-schema-validators}/tests/validators.spec.ts (74%) create mode 100644 packages/mds-schema-validators/tsconfig.build.json create mode 100644 packages/mds-schema-validators/tsconfig.eslint.json create mode 100644 packages/mds-schema-validators/validators.ts create mode 100644 packages/mds-service-helpers/@types/index.ts create mode 100644 packages/mds-service-helpers/client/index.ts create mode 100644 packages/mds-service-helpers/index.ts create mode 100644 packages/mds-service-helpers/package.json create mode 100644 packages/mds-service-helpers/server/index.ts create mode 100644 packages/mds-service-helpers/tests/index.spec.ts rename packages/{mds-cache => mds-service-helpers}/tsconfig.build.json (100%) create mode 100644 packages/mds-service-helpers/tsconfig.eslint.json rename container-images/mds-daily/index.ts => packages/mds-stream/agency-stream-interface.ts (55%) create mode 100644 packages/mds-stream/helpers.ts create mode 100644 packages/mds-stream/kafka/agency-stream-kafka.ts rename container-images/mds-metrics-sheet/vehicle-counts.ts => packages/mds-stream/kafka/helpers.ts (82%) create mode 100644 packages/mds-stream/kafka/index.ts create mode 100644 packages/mds-stream/kafka/stream-consumer.ts create mode 100644 packages/mds-stream/kafka/stream-producer.ts create mode 100644 packages/mds-stream/nats/agency-stream-nats.ts create mode 100644 packages/mds-stream/nats/helpers.ts create mode 100644 packages/mds-stream/nats/stream-consumer.ts create mode 100644 packages/mds-stream/nats/stream-producer.ts rename container-images/mds-audit/index.ts => packages/mds-stream/stream-interface.ts (55%) create mode 100644 packages/mds-stream/tsconfig.eslint.json rename packages/{ladot-service-areas/council-district-11.js => mds-test-data/test-areas/council-district-11.ts} (99%) rename packages/{mds-compliance/tests => mds-test-data/test-areas}/la-city-boundary.ts (99%) rename packages/{ladot-service-areas/la-dacs.js => mds-test-data/test-areas/la-dacs.ts} (99%) rename packages/{ladot-service-areas/non-san-fernando-dac.js => mds-test-data/test-areas/non-san-fernando-dac.ts} (99%) rename packages/{ladot-service-areas/restricted-areas.js => mds-test-data/test-areas/restricted-areas.ts} (97%) rename packages/{ladot-service-areas/san-fernando-dac.js => mds-test-data/test-areas/san-fernando-dac.ts} (99%) rename packages/{ladot-service-areas/service-area-new.js => mds-test-data/test-areas/service-area-new.ts} (89%) rename packages/{ladot-service-areas/service-area-old.js => mds-test-data/test-areas/service-area-old.ts} (86%) rename packages/{ladot-service-areas/service-areas.js => mds-test-data/test-areas/service-areas.ts} (99%) rename packages/{ladot-service-areas/ladot-service-areas.js => mds-test-data/test-areas/test-areas.ts} (66%) rename packages/{ladot-service-areas/venice-special-ops-zone.js => mds-test-data/test-areas/venice-special-ops-zone.ts} (98%) rename packages/{ladot-service-areas/venice.js => mds-test-data/test-areas/venice.ts} (99%) create mode 100644 packages/mds-test-data/tsconfig.eslint.json delete mode 100644 packages/mds-types/scopes.ts create mode 100644 packages/mds-types/tsconfig.eslint.json create mode 100644 packages/mds-utils/date-time-utils.ts create mode 100644 packages/mds-utils/exceptions/exception-messages.ts rename packages/mds-utils/{ => exceptions}/exceptions.ts (52%) create mode 100644 packages/mds-utils/state-machine.ts create mode 100644 packages/mds-utils/tests/date-time-utils.spec.ts create mode 100644 packages/mds-utils/tests/state-transition-expected.ts create mode 100644 packages/mds-utils/tsconfig.eslint.json delete mode 100644 packages/mds-utils/validators.ts create mode 100644 packages/mds-web-sockets/client.ts create mode 100644 packages/mds-web-sockets/clients.ts create mode 100644 packages/mds-web-sockets/index.ts create mode 100644 packages/mds-web-sockets/package.json create mode 100644 packages/mds-web-sockets/server.ts create mode 100644 packages/mds-web-sockets/tests/ws.spec.ts create mode 100644 packages/mds-web-sockets/tsconfig.build.json create mode 100644 packages/mds-web-sockets/tsconfig.eslint.json create mode 100644 packages/mds-web-sockets/types.ts create mode 100644 packages/mds-web-sockets/ws-server.ts create mode 100644 packages/mds-webpack-config/index.ts create mode 100644 packages/mds-webpack-config/package.json rename {container-images/env-inject => packages/mds-webpack-config}/tsconfig.build.json (100%) create mode 100644 packages/mds-webpack-config/tsconfig.eslint.json create mode 100644 tsconfig.eslint.json delete mode 100644 tsconfig.settings.json diff --git a/.eslintrc.js b/.eslintrc.js deleted file mode 100644 index dff8fbd17..000000000 --- a/.eslintrc.js +++ /dev/null @@ -1,56 +0,0 @@ -module.exports = { - env: { - node: true, - es6: true, - mocha: true - }, - plugins: ['import', 'prettier', '@typescript-eslint', 'promise'], - parser: '@typescript-eslint/parser', - parserOptions: { - ecmaVersion: 2018, - sourceType: 'module', - tsconfigRootDir: __dirname, - project: 'tsconfig.settings.json' - }, - extends: [ - 'airbnb-base', - 'plugin:@typescript-eslint/recommended', - 'plugin:import/typescript', - 'plugin:prettier/recommended', - 'prettier/@typescript-eslint' - ], - rules: { - '@typescript-eslint/no-floating-promises': 'error', - '@typescript-eslint/camelcase': 'off', - '@typescript-eslint/explicit-function-return-type': 'off', - '@typescript-eslint/no-parameter-properties': 'off', - '@typescript-eslint/no-unused-vars': ['error', { ignoreRestSiblings: true }], - 'array-callback-return': 'off', - 'consistent-return': 'off', - eqeqeq: 'error', - 'import/no-extraneous-dependencies': 'off', - 'import/no-unresolved': 'off', - 'import/prefer-default-export': 'off', - 'max-classes-per-file': 'off', - 'no-console': 'warn', - 'no-plusplus': 'off', - 'no-restricted-syntax': 'off', - 'no-var': 'error', - 'prettier/prettier': 'warn', - radix: 'off', - 'promise/always-return': 'error', - 'promise/no-return-wrap': 'error', - 'promise/param-names': 'error', - 'promise/catch-or-return': 'error', - 'promise/no-native': 'off', - 'promise/no-nesting': 'error', - 'promise/no-promise-in-callback': 'error', - 'promise/no-callback-in-promise': 'error', - 'promise/avoid-new': 'error', - 'promise/no-new-statics': 'error', - 'promise/no-return-in-finally': 'error', - 'promise/valid-params': 'error', - 'promise/prefer-await-to-then': 'error', - 'promise/prefer-await-to-callbacks': 'error' - } -} diff --git a/.eslintrc.json b/.eslintrc.json new file mode 100644 index 000000000..ab0968bfe --- /dev/null +++ b/.eslintrc.json @@ -0,0 +1,72 @@ +{ + "env": { + "node": true, + "es6": true, + "mocha": true + }, + "plugins": ["import", "prettier", "@typescript-eslint", "promise"], + "parser": "@typescript-eslint/parser", + "parserOptions": { + "ecmaVersion": 2018, + "sourceType": "module", + "project": "tsconfig.eslint.json" + }, + "extends": [ + "airbnb-base", + "plugin:@typescript-eslint/recommended", + "plugin:import/typescript", + "plugin:prettier/recommended", + "prettier/@typescript-eslint" + ], + "ignorePatterns": ["*.d.ts"], + "rules": { + "@typescript-eslint/camelcase": "off", + "@typescript-eslint/explicit-function-return-type": "off", + "@typescript-eslint/no-floating-promises": "error", + "@typescript-eslint/no-parameter-properties": "off", + "@typescript-eslint/no-unused-vars": [ + "error", + { "ignoreRestSiblings": true, "args": "none" } + ], + "@typescript-eslint/no-useless-constructor": "error", + "array-callback-return": "off", + "class-methods-use-this": "off", + "consistent-return": "off", + "eqeqeq": "error", + "import/extensions": ["error", "ignorePackages", { "ts": "never" }], + "import/no-extraneous-dependencies": "error", + "import/no-unresolved": "off", + "import/prefer-default-export": "off", + "max-classes-per-file": "off", + "no-console": "warn", + "no-empty": ["error", { "allowEmptyCatch": true }], + "no-plusplus": "off", + "no-restricted-syntax": "off", + "no-useless-constructor": "off", + "no-var": "error", + "prettier/prettier": "warn", + "promise/always-return": "error", + "promise/avoid-new": "error", + "promise/catch-or-return": "error", + "promise/no-callback-in-promise": "error", + "promise/no-native": "off", + "promise/no-nesting": "error", + "promise/no-new-statics": "error", + "promise/no-promise-in-callback": "error", + "promise/no-return-in-finally": "error", + "promise/no-return-wrap": "error", + "promise/param-names": "error", + "promise/prefer-await-to-callbacks": "error", + "promise/prefer-await-to-then": "error", + "promise/valid-params": "error", + "radix": "off" + }, + "overrides": [ + { + "files": ["**/*.spec.ts"], + "rules": { + "import/no-extraneous-dependencies": "off" + } + } + ] +} diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index 5d537785a..f3d9d7b90 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -18,4 +18,4 @@ By participating, you are expected to uphold this code. In order to maintain consistency, all development dependencies related to the testing and building of the packages in this repository are installed in the root `package.json` file. This includes the various tools (`ESLint`, `Prettier`, `TypeScript`, `Mocha`, etc.) as well as the default configuration settings for those tools. -One notable exception to this is the installation of TypeScript definition (`@types`) packages which are installed as development dependencies in the individual packages that require them which is analogous to those dependencies that include their own type definitinos without a separate `@types` package. \ No newline at end of file +One notable exception to this is the installation of TypeScript definition (`@types`) packages which are installed as regular dependencies in the individual packages that require them which is analogous to those dependencies that include their own type definitinos without a separate `@types` package. \ No newline at end of file diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 030f4f613..186de7f64 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -1,18 +1,12 @@ -## PR Checklist +## 📚 Purpose +*[description/approach]* - - [ ] simple searchable title - `[mds-db] Add PG env var`, `[config] Fix eslint config` - - [ ] briefly describe the changes in this PR - - [ ] mark as draft if should not be merged - - [ ] write tests for all new functionality - -## Impacts -- [ ] Provider -- [ ] Agency -- [ ] Audit -- [ ] Policy -- [ ] Compliance -- [ ] Daily -- [ ] Native -- [ ] Policy Author +## 👌 Resolves: +- [ ] 🐛 JRA-834 fixes bug +- [ ] ✨ JRA-756 implements new feature +## 📦 Impacts: +*[list of packages]* +## ✅ PR Checklist +- [ ] Add or remove checklist items to suit your needs \ No newline at end of file diff --git a/.gitignore b/.gitignore index 01ee2a0f8..b3d7590bb 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,11 @@ .tern-project mds-*.zip dist +dump.rdb + +# This project uses Yarn. Ignore npm lock files +package-lock.json +npm-shrinkwrap.json # Created by https://www.gitignore.io/api/osx,node,windows,serverless,sublimetext,visualstudiocode # Edit at https://www.gitignore.io/?templates=osx,node,windows,serverless,sublimetext,visualstudiocode @@ -206,7 +211,7 @@ $RECYCLE.BIN/ # helm .helmignore values.*.yaml -helm/charts +helm/*/charts helm/tools # docker diff --git a/.mocharc.json b/.mocharc.json new file mode 100644 index 000000000..641b47f11 --- /dev/null +++ b/.mocharc.json @@ -0,0 +1,7 @@ +{ + "colors": true, + "recursive": true, + "require": ["tsconfig-paths/register", "source-map-support/register", "dotenv/config"], + "spec": "tests/**/*.ts", + "timeout": 10000 +} diff --git a/.nvmrc b/.nvmrc index d19159826..07ea9fa43 100644 --- a/.nvmrc +++ b/.nvmrc @@ -1 +1 @@ -10.16.3 +14.2.0 diff --git a/.nycrc.json b/.nycrc.json new file mode 100644 index 000000000..ffd5ed92a --- /dev/null +++ b/.nycrc.json @@ -0,0 +1,7 @@ +{ + "extends": "@istanbuljs/nyc-config-typescript", + "check-coverage": true, + "lines": 85, + "reporter": ["text", "html"], + "temp-directory": "./coverage/.nyc_output" +} diff --git a/.prettierrc.js b/.prettierrc.js deleted file mode 100644 index 5e9d79064..000000000 --- a/.prettierrc.js +++ /dev/null @@ -1,6 +0,0 @@ -module.exports = { - printWidth: 120, - semi: false, - singleQuote: true, - tabWidth: 2 -} diff --git a/.prettierrc.json b/.prettierrc.json new file mode 100644 index 000000000..f44646f59 --- /dev/null +++ b/.prettierrc.json @@ -0,0 +1,15 @@ +{ + "overrides": [ + { + "files": ["*.ts", "*.js"], + "options": { + "arrowParens": "avoid", + "printWidth": 120, + "semi": false, + "tabWidth": 2, + "trailingComma": "none", + "singleQuote": true + } + } + ] +} diff --git a/.stignore b/.stignore new file mode 100644 index 000000000..b6a2c98f5 --- /dev/null +++ b/.stignore @@ -0,0 +1,3 @@ +/.git +/node_modules +/images \ No newline at end of file diff --git a/.vscode/launch.json b/.vscode/launch.json index b3effbcca..7bee1462d 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -8,15 +8,21 @@ "type": "node", "request": "launch", "name": "Server: mds-agency", - "runtimeArgs": ["-r", "ts-node/register", "-r", "tsconfig-paths/register", "-r", "dotenv/config"], + "runtimeArgs": [ + "-r", + "ts-node/register", + "-r", + "tsconfig-paths/register", + "-r", + "dotenv/config" + ], "args": ["server.ts"], "console": "integratedTerminal", "internalConsoleOptions": "neverOpen", "protocol": "inspector", "cwd": "${workspaceFolder}/packages/mds-agency", "env": { - "DOTENV_CONFIG_PATH": "../../.env", - "npm_package_name": "@mds-core/mds-agency", + "DOTENV_CONFIG_PATH": "${workspaceFolder}/.env", "PATH_PREFIX": "/agency" } }, @@ -24,79 +30,109 @@ "type": "node", "request": "launch", "name": "Server: mds-audit", - "runtimeArgs": ["-r", "ts-node/register", "-r", "tsconfig-paths/register", "-r", "dotenv/config"], + "runtimeArgs": [ + "-r", + "ts-node/register", + "-r", + "tsconfig-paths/register", + "-r", + "dotenv/config" + ], "args": ["server.ts"], "console": "integratedTerminal", "internalConsoleOptions": "neverOpen", "protocol": "inspector", "cwd": "${workspaceFolder}/packages/mds-audit", "env": { - "DOTENV_CONFIG_PATH": "../../.env", - "npm_package_name": "@mds-core/mds-audit", + "DOTENV_CONFIG_PATH": "${workspaceFolder}/.env", "PATH_PREFIX": "/audit" } }, { "type": "node", "request": "launch", - "name": "Server: mds-daily", - "runtimeArgs": ["-r", "ts-node/register", "-r", "tsconfig-paths/register", "-r", "dotenv/config"], + "name": "Server: mds-compliance", + "runtimeArgs": [ + "-r", + "ts-node/register", + "-r", + "tsconfig-paths/register", + "-r", + "dotenv/config" + ], "args": ["server.ts"], "console": "integratedTerminal", "internalConsoleOptions": "neverOpen", "protocol": "inspector", - "cwd": "${workspaceFolder}/packages/mds-daily", + "cwd": "${workspaceFolder}/packages/mds-compliance", "env": { - "DOTENV_CONFIG_PATH": "../../.env", - "npm_package_name": "@mds-core/mds-daily", - "PATH_PREFIX": "/daily" + "DOTENV_CONFIG_PATH": "${workspaceFolder}/.env", + "PATH_PREFIX": "/compliance" } }, { "type": "node", "request": "launch", - "name": "Server: mds-compliance", - "runtimeArgs": ["-r", "ts-node/register", "-r", "tsconfig-paths/register", "-r", "dotenv/config"], + "name": "Server: mds-daily", + "runtimeArgs": [ + "-r", + "ts-node/register", + "-r", + "tsconfig-paths/register", + "-r", + "dotenv/config" + ], "args": ["server.ts"], "console": "integratedTerminal", "internalConsoleOptions": "neverOpen", "protocol": "inspector", - "cwd": "${workspaceFolder}/packages/mds-compliance", + "cwd": "${workspaceFolder}/packages/mds-daily", "env": { - "DOTENV_CONFIG_PATH": "../../.env", - "npm_package_name": "@mds-core/mds-compliance", - "PATH_PREFIX": "/compliance" + "DOTENV_CONFIG_PATH": "${workspaceFolder}/.env", + "PATH_PREFIX": "/daily" } }, { "type": "node", "request": "launch", - "name": "Server: mds-native", - "runtimeArgs": ["-r", "ts-node/register", "-r", "tsconfig-paths/register", "-r", "dotenv/config"], + "name": "Server: mds-jursidiction", + "runtimeArgs": [ + "-r", + "ts-node/register", + "-r", + "tsconfig-paths/register", + "-r", + "dotenv/config" + ], "args": ["server.ts"], "console": "integratedTerminal", "internalConsoleOptions": "neverOpen", "protocol": "inspector", - "cwd": "${workspaceFolder}/packages/mds-native", + "cwd": "${workspaceFolder}/packages/mds-jurisdiction", "env": { - "DOTENV_CONFIG_PATH": "../../.env", - "npm_package_name": "@mds-core/mds-native", - "PATH_PREFIX": "/native" + "DOTENV_CONFIG_PATH": "${workspaceFolder}/.env", + "PATH_PREFIX": "/jurisdiction" } }, { "type": "node", "request": "launch", "name": "Server: mds-policy", - "runtimeArgs": ["-r", "ts-node/register", "-r", "tsconfig-paths/register", "-r", "dotenv/config"], + "runtimeArgs": [ + "-r", + "ts-node/register", + "-r", + "tsconfig-paths/register", + "-r", + "dotenv/config" + ], "args": ["server.ts"], "console": "integratedTerminal", "internalConsoleOptions": "neverOpen", "protocol": "inspector", "cwd": "${workspaceFolder}/packages/mds-policy", "env": { - "DOTENV_CONFIG_PATH": "../../.env", - "npm_package_name": "@mds-core/mds-policy", + "DOTENV_CONFIG_PATH": "${workspaceFolder}/.env", "PATH_PREFIX": "/policy" } }, @@ -104,44 +140,65 @@ "type": "node", "request": "launch", "name": "Server: mds-policy-author", - "runtimeArgs": ["-r", "ts-node/register", "-r", "tsconfig-paths/register", "-r", "dotenv/config"], + "runtimeArgs": [ + "-r", + "ts-node/register", + "-r", + "tsconfig-paths/register", + "-r", + "dotenv/config" + ], "args": ["server.ts"], "console": "integratedTerminal", "internalConsoleOptions": "neverOpen", "protocol": "inspector", "cwd": "${workspaceFolder}/packages/mds-policy-author", "env": { - "DOTENV_CONFIG_PATH": "../../.env", - "npm_package_name": "@mds-core/mds-policy-author", + "DOTENV_CONFIG_PATH": "${workspaceFolder}/.env", "PATH_PREFIX": "/policy-author" } }, { "type": "node", "request": "launch", - "name": "Tests: mds-agency", - "program": "${workspaceFolder}/node_modules/mocha/bin/_mocha", - "args": [ + "name": "Server: mds-geography-author", + "runtimeArgs": [ "-r", "ts-node/register", "-r", "tsconfig-paths/register", "-r", - "dotenv/config", - "--timeout", - "999999", - "--colors", - "--recursive", + "dotenv/config" + ], + "args": ["server.ts"], + "console": "integratedTerminal", + "internalConsoleOptions": "neverOpen", + "protocol": "inspector", + "cwd": "${workspaceFolder}/packages/mds-geography-author", + "env": { + "DOTENV_CONFIG_PATH": "${workspaceFolder}/.env", + "PATH_PREFIX": "/policy-author" + } + }, + { + "type": "node", + "request": "launch", + "name": "Tests: mds-agency", + "program": "${workspaceFolder}/node_modules/mocha/bin/_mocha", + "args": [ + "--project", + "${workspaceFolder}/tsconfig.json", + "-r", + "ts-node/register", "--timeout", - "5000", - "'tests/**/*.ts'" + "0" ], "console": "integratedTerminal", "internalConsoleOptions": "neverOpen", "protocol": "inspector", "cwd": "${workspaceFolder}/packages/mds-agency", "env": { - "DOTENV_CONFIG_PATH": "../../.env", + "DOTENV_CONFIG_PATH": "${workspaceFolder}/.env", "PATH_PREFIX": "/agency" } }, @@ -151,26 +208,19 @@ "name": "Tests: mds-api-authorizer", "program": "${workspaceFolder}/node_modules/mocha/bin/_mocha", "args": [ + "--project", + "${workspaceFolder}/tsconfig.json", "-r", "ts-node/register", - "-r", - "tsconfig-paths/register", - "-r", - "dotenv/config", "--timeout", - "999999", - "--colors", - "--recursive", - "--timeout", - "5000", - "'tests/**/*.ts'" + "0" ], "console": "integratedTerminal", "internalConsoleOptions": "neverOpen", "protocol": "inspector", "cwd": "${workspaceFolder}/packages/mds-api-authorizer", "env": { - "DOTENV_CONFIG_PATH": "../../.env" + "DOTENV_CONFIG_PATH": "${workspaceFolder}/.env" } }, { @@ -179,28 +229,20 @@ "name": "Tests: mds-api-server", "program": "${workspaceFolder}/node_modules/mocha/bin/_mocha", "args": [ + "--project", + "${workspaceFolder}/tsconfig.json", "-r", "ts-node/register", - "-r", - "tsconfig-paths/register", - "-r", - "dotenv/config", - "--timeout", - "999999", - "--colors", - "--recursive", "--timeout", - "5000", - "'tests/**/*.ts'" + "0" ], "console": "integratedTerminal", "internalConsoleOptions": "neverOpen", "protocol": "inspector", "cwd": "${workspaceFolder}/packages/mds-api-server", "env": { - "npm_package_name": "mds-api-server", "npm_package_version": "0.0.0", - "DOTENV_CONFIG_PATH": "../../.env" + "DOTENV_CONFIG_PATH": "${workspaceFolder}/.env" } }, { @@ -209,26 +251,19 @@ "name": "Tests: mds-audit", "program": "${workspaceFolder}/node_modules/mocha/bin/_mocha", "args": [ + "--project", + "${workspaceFolder}/tsconfig.json", "-r", "ts-node/register", - "-r", - "tsconfig-paths/register", - "-r", - "dotenv/config", - "--timeout", - "999999", - "--colors", - "--recursive", "--timeout", - "5000", - "'tests/**/*.ts'" + "0" ], "console": "integratedTerminal", "internalConsoleOptions": "neverOpen", "protocol": "inspector", "cwd": "${workspaceFolder}/packages/mds-audit", "env": { - "DOTENV_CONFIG_PATH": "../../.env", + "DOTENV_CONFIG_PATH": "${workspaceFolder}/.env", "PATH_PREFIX": "/audit" } }, @@ -238,26 +273,19 @@ "name": "Tests: mds-compliance", "program": "${workspaceFolder}/node_modules/mocha/bin/_mocha", "args": [ + "--project", + "${workspaceFolder}/tsconfig.json", "-r", "ts-node/register", - "-r", - "tsconfig-paths/register", - "-r", - "dotenv/config", - "--timeout", - "999999", - "--colors", - "--recursive", "--timeout", - "5000", - "'tests/**/*.ts'" + "0" ], "console": "integratedTerminal", "internalConsoleOptions": "neverOpen", "protocol": "inspector", "cwd": "${workspaceFolder}/packages/mds-compliance", "env": { - "DOTENV_CONFIG_PATH": "../../.env", + "DOTENV_CONFIG_PATH": "${workspaceFolder}/.env", "PATH_PREFIX": "/compliance" } }, @@ -267,26 +295,19 @@ "name": "Tests: mds-daily", "program": "${workspaceFolder}/node_modules/mocha/bin/_mocha", "args": [ + "--project", + "${workspaceFolder}/tsconfig.json", "-r", "ts-node/register", - "-r", - "tsconfig-paths/register", - "-r", - "dotenv/config", "--timeout", - "999999", - "--colors", - "--recursive", - "--timeout", - "5000", - "'tests/**/*.ts'" + "0" ], "console": "integratedTerminal", "internalConsoleOptions": "neverOpen", "protocol": "inspector", "cwd": "${workspaceFolder}/packages/mds-daily", "env": { - "DOTENV_CONFIG_PATH": "../../.env", + "DOTENV_CONFIG_PATH": "${workspaceFolder}/.env", "PATH_PREFIX": "/daily" } }, @@ -296,83 +317,82 @@ "name": "Tests: mds-db", "program": "${workspaceFolder}/node_modules/mocha/bin/_mocha", "args": [ + "--project", + "${workspaceFolder}/tsconfig.json", "-r", "ts-node/register", - "-r", - "tsconfig-paths/register", - "-r", - "dotenv/config", - "--timeout", - "999999", - "--colors", - "--recursive", "--timeout", - "5000", - "'tests/**/*.ts'" + "0" ], "console": "integratedTerminal", "internalConsoleOptions": "neverOpen", "protocol": "inspector", "cwd": "${workspaceFolder}/packages/mds-db", "env": { - "DOTENV_CONFIG_PATH": "../../.env" + "DOTENV_CONFIG_PATH": "${workspaceFolder}/.env" } }, { "type": "node", "request": "launch", - "name": "Tests: mds-logger", + "name": "Tests: mds-jurisdiction", "program": "${workspaceFolder}/node_modules/mocha/bin/_mocha", "args": [ + "--project", + "${workspaceFolder}/tsconfig.json", "-r", "ts-node/register", - "-r", - "tsconfig-paths/register", - "-r", - "dotenv/config", - "--timeout", - "999999", - "--colors", - "--recursive", "--timeout", - "5000", - "'tests/**/*.ts'" + "0" ], "console": "integratedTerminal", "internalConsoleOptions": "neverOpen", "protocol": "inspector", - "cwd": "${workspaceFolder}/packages/mds-logger", + "cwd": "${workspaceFolder}/packages/mds-jurisdiction", "env": { - "DOTENV_CONFIG_PATH": "../../.env" + "DOTENV_CONFIG_PATH": "${workspaceFolder}/.env" } }, { "type": "node", "request": "launch", - "name": "Tests: mds-native", + "name": "Tests: mds-jurisdiction-service", "program": "${workspaceFolder}/node_modules/mocha/bin/_mocha", "args": [ + "--project", + "${workspaceFolder}/tsconfig.json", "-r", "ts-node/register", - "-r", - "tsconfig-paths/register", - "-r", - "dotenv/config", "--timeout", - "999999", - "--colors", - "--recursive", + "0" + ], + "console": "integratedTerminal", + "internalConsoleOptions": "neverOpen", + "protocol": "inspector", + "cwd": "${workspaceFolder}/packages/mds-jurisdiction-service", + "env": { + "DOTENV_CONFIG_PATH": "${workspaceFolder}/.env" + } + }, + { + "type": "node", + "request": "launch", + "name": "Tests: mds-logger", + "program": "${workspaceFolder}/node_modules/mocha/bin/_mocha", + "args": [ + "--project", + "${workspaceFolder}/tsconfig.json", + "-r", + "ts-node/register", "--timeout", - "5000", - "'tests/**/*.ts'" + "0" ], "console": "integratedTerminal", "internalConsoleOptions": "neverOpen", "protocol": "inspector", - "cwd": "${workspaceFolder}/packages/mds-native", + "cwd": "${workspaceFolder}/packages/mds-logger", "env": { - "DOTENV_CONFIG_PATH": "../../.env", - "PATH_PREFIX": "/native" + "DOTENV_CONFIG_PATH": "${workspaceFolder}/.env" } }, { @@ -381,26 +401,19 @@ "name": "Tests: mds-policy", "program": "${workspaceFolder}/node_modules/mocha/bin/_mocha", "args": [ + "--project", + "${workspaceFolder}/tsconfig.json", "-r", "ts-node/register", - "-r", - "tsconfig-paths/register", - "-r", - "dotenv/config", "--timeout", - "999999", - "--colors", - "--recursive", - "--timeout", - "5000", - "'tests/**/*.ts'" + "0" ], "console": "integratedTerminal", "internalConsoleOptions": "neverOpen", "protocol": "inspector", "cwd": "${workspaceFolder}/packages/mds-policy", "env": { - "DOTENV_CONFIG_PATH": "../../.env", + "DOTENV_CONFIG_PATH": "${workspaceFolder}/.env", "PATH_PREFIX": "/policy" } }, @@ -410,55 +423,84 @@ "name": "Tests: mds-policy-author", "program": "${workspaceFolder}/node_modules/mocha/bin/_mocha", "args": [ + "--project", + "${workspaceFolder}/tsconfig.json", "-r", "ts-node/register", - "-r", - "tsconfig-paths/register", - "-r", - "dotenv/config", - "--timeout", - "999999", - "--colors", - "--recursive", "--timeout", - "5000", - "'tests/**/*.ts'" + "0" ], "console": "integratedTerminal", "internalConsoleOptions": "neverOpen", "protocol": "inspector", "cwd": "${workspaceFolder}/packages/mds-policy-author", "env": { - "DOTENV_CONFIG_PATH": "../../.env", + "DOTENV_CONFIG_PATH": "${workspaceFolder}/.env", "PATH_PREFIX": "/policy-author" } }, { "type": "node", "request": "launch", - "name": "Tests: mds-types", + "name": "Tests: mds-geography-author", "program": "${workspaceFolder}/node_modules/mocha/bin/_mocha", "args": [ + "--project", + "${workspaceFolder}/tsconfig.json", "-r", "ts-node/register", + "--timeout", + "0" + ], + "console": "integratedTerminal", + "internalConsoleOptions": "neverOpen", + "protocol": "inspector", + "cwd": "${workspaceFolder}/packages/mds-geography-author", + "env": { + "DOTENV_CONFIG_PATH": "${workspaceFolder}/.env", + "PATH_PREFIX": "/policy-author" + } + }, + { + "type": "node", + "request": "launch", + "name": "Tests: mds-repository", + "program": "${workspaceFolder}/node_modules/mocha/bin/_mocha", + "args": [ + "--project", + "${workspaceFolder}/tsconfig.json", "-r", - "tsconfig-paths/register", - "-r", - "dotenv/config", + "ts-node/register", "--timeout", - "999999", - "--colors", - "--recursive", + "0" + ], + "console": "integratedTerminal", + "internalConsoleOptions": "neverOpen", + "protocol": "inspector", + "cwd": "${workspaceFolder}/packages/mds-repository", + "env": { + "DOTENV_CONFIG_PATH": "${workspaceFolder}/.env" + } + }, + { + "type": "node", + "request": "launch", + "name": "Tests: mds-types", + "program": "${workspaceFolder}/node_modules/mocha/bin/_mocha", + "args": [ + "--project", + "${workspaceFolder}/tsconfig.json", + "-r", + "ts-node/register", "--timeout", - "5000", - "'tests/**/*.ts'" + "0" ], "console": "integratedTerminal", "internalConsoleOptions": "neverOpen", "protocol": "inspector", "cwd": "${workspaceFolder}/packages/mds-types", "env": { - "DOTENV_CONFIG_PATH": "../../.env" + "DOTENV_CONFIG_PATH": "${workspaceFolder}/.env" } }, { @@ -467,26 +509,19 @@ "name": "Tests: mds-utils", "program": "${workspaceFolder}/node_modules/mocha/bin/_mocha", "args": [ + "--project", + "${workspaceFolder}/tsconfig.json", "-r", "ts-node/register", - "-r", - "tsconfig-paths/register", - "-r", - "dotenv/config", - "--timeout", - "999999", - "--colors", - "--recursive", "--timeout", - "5000", - "'tests/**/*.ts'" + "0" ], "console": "integratedTerminal", "internalConsoleOptions": "neverOpen", "protocol": "inspector", "cwd": "${workspaceFolder}/packages/mds-utils", "env": { - "DOTENV_CONFIG_PATH": "../../.env" + "DOTENV_CONFIG_PATH": "${workspaceFolder}/.env" } } ] diff --git a/.vscode/settings.json b/.vscode/settings.json index a61878207..621bb605b 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,16 +1,17 @@ { + "editor.codeActionsOnSave": { + "source.fixAll.eslint": true + }, + "editor.defaultFormatter": "esbenp.prettier-vscode", + "editor.formatOnSave": true, "editor.insertSpaces": true, "editor.tabSize": 2, - "eslint.autoFixOnSave": true, "eslint.enable": true, - "eslint.validate": [{ "language": "typescript", "autoFix": true }], "eslint.options": { "ignorePath": ".gitignore" }, + "eslint.validate": ["typescript"], "prettier.disableLanguages": ["markdown"], - "prettier.eslintIntegration": true, "[typescript]": { "editor.formatOnSave": false }, - "editor.formatOnSave": true, - "typescript.tsdk": "node_modules/typescript/lib", - "editor.defaultFormatter": "esbenp.prettier-vscode" + "typescript.tsdk": "node_modules/typescript/lib" } diff --git a/@types/cloudevents-sdk.d.ts b/@types/cloudevents-sdk.d.ts deleted file mode 100644 index 038098c4e..000000000 --- a/@types/cloudevents-sdk.d.ts +++ /dev/null @@ -1 +0,0 @@ -declare module 'cloudevents-sdk' diff --git a/@types/ladot-service-areas.d.ts b/@types/ladot-service-areas.d.ts deleted file mode 100644 index 11b89666f..000000000 --- a/@types/ladot-service-areas.d.ts +++ /dev/null @@ -1,17 +0,0 @@ -declare module 'ladot-service-areas' { - import { Geometry } from 'geojson' - - export interface ServiceArea { - start_date: number - end_date: number | null - prev_area: string | null - replacement_area: string | null - type: string - description: string - area: Geometry - } - - export const serviceAreaMap: { [key: string]: ServiceArea } - - export function readServiceAreas(provider_id?: string, service_area_id?: string): ServiceArea[] -} diff --git a/@types/pushover-notifications.d.ts b/@types/pushover-notifications.d.ts deleted file mode 100644 index 5cfcccc86..000000000 --- a/@types/pushover-notifications.d.ts +++ /dev/null @@ -1 +0,0 @@ -declare module 'pushover-notifications' diff --git a/@types/wrapper-webpack-plugin.d.ts b/@types/wrapper-webpack-plugin.d.ts new file mode 100644 index 000000000..7fa8de3fe --- /dev/null +++ b/@types/wrapper-webpack-plugin.d.ts @@ -0,0 +1,12 @@ +import { Plugin } from 'webpack'; + +declare class WrapperWebpackPlugin extends Plugin { + constructor(args: { + header?: string | ((fileName: string, chunkHash: string) => string) + footer?: string | ((fileName: string, chunkHash: string) => string) + test?: string | RegExp + afterOptimizations?: boolean + }); +} + +export = WrapperWebpackPlugin; diff --git a/README.md b/README.md index 9704e8bbd..5ada789b9 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,24 @@ # Overview -Repo for LADOT MDS implementation for contribution to the Open Mobility Foundation. It represents what is currently up and running for Los Angeles production MDS as well as new features under development. Includes the following: - -* A current LADOT implementation of all MDS endpoints -* Development versions of mds-audit, mds-policy, and mds-compliance -* MDS logging (mds-logger), daily metrics (mds-daily) and Google sheet reporting app for technical compliance. +Repo for LADOT MDS implementation for contribution to the Open Mobility Foundation. It represents what is currently up and running for Los Angeles production MDS as well as new features under development. + +## Contents + +### Stable Content +#### APIs +1. MDS-Agency 0.4.0 Implementation +2. MDS-Policy 0.4.0 Implementation + +### Experimental Content +#### APIs +1. MDS-Agency `/stops` [PR](https://github.com/openmobilityfoundation/mobility-data-specification/pull/430) +2. MDS-Audit [PR](https://github.com/openmobilityfoundation/mobility-data-specification/pull/326) +3. MDS-Compliance [PR](https://github.com/openmobilityfoundation/mobility-data-specification/pull/333) +4. MDS-Config +5. MDS-Daily +6. MDS-Metrics-Sheet +7. MDS-Policy-Author +8. MDS-Web-Sockets ## Installation @@ -13,6 +27,7 @@ Repo for LADOT MDS implementation for contribution to the Open Mobility Foundati * PostgreSQL * Redis * [Yarn](https://yarnpkg.com/en/docs/install#mac-stable) +* [NVM](https://github.com/nvm-sh/nvm#installation-and-update) #### Database config on macOS If you haven't installed PostegreSQL and Redis you can install them with homebrew on macOS @@ -33,6 +48,15 @@ If you encounter the following error: The following command should fix your issue `createdb -h localhost` +To run tests, you will need this: +`createdb -h localhost mdstest` + +Then add `export PG_NAME=mdstest` to your shell's environment file. (The name is not important, but you'll need to point it somehwere.) + +#### Node setup + +You should have NVM already installed from the link above. The top level directory of the project has a `.nvmrc` file and you should be able to run `nvm install` to get the right version of Node. + #### Package setup Install [Lerna](https://lerna.js.org/) @@ -111,36 +135,169 @@ lerna run prettier * Select any one of the files in a package's test folder * Press `F5` -### [Kubernetes](https://kubernetes.io)/[Istio](https://istio.io) +### Kubernetes + +MDS can readily be provisioned to a [Kubernetes](https://kubernetes.io) capable cluster, be it a local or remote. The following steps describe how to build, deploy and operate against a local MDS cluster. + +#### Prerequisites + +Obtain a local working copy of MDS: + +```sh +% git clone https://github.com/lacuna-tech/mds-core +% cd mds-core +``` + +OSX (Linux and Windows tbd) + +Install [Docker Desktop](https://download.docker.com/mac/stable/Docker.dmg): + +```sh +% open https://download.docker.com/mac/stable/Docker.dmg +``` + +Start Docker-Desktop: + +```sh +% open /Applications/Docker.app +``` + +Lastly, configure Kubernetes: + +```txt +select the 'Preferences' option +select the 'Resources' option + apply the following minimal resource changes: + CPUs: 6 + Memory: 8G + Swap: 1G +select the 'Kubernetes' option + select 'Enable Kubernetes' option +select 'Apply & Restart' +``` + +Verify: + +```sh +% which kubectl +% kubectl config set-context docker-desktop +% kubectl cluster-info +``` + +#### Bootstrap : install operational dependencies + +In order to build and operate MDS, a number of suporting technologies are leveraged by ensuring they are installed and operational via a one-time `bootstap` process: + +```sh +% ./bin/mdsctl -p local bootstrap +``` + +The principle tools are: [homebrew](https://brew.sh), [bash-4.x+](https://www.gnu.org/software/bash/), [oq](https://github.com/Blacksmoke16/oq), [jq](https://stedolan.github.io/jq/), [yarn](https://yarnpkg.com/), [nvm](https://github.com/nvm-sh/nvm), [helm-2.14.1](https://helm.sh), [k9s](https://github.com/derailed/k9s), [kubectx](https://github.com/ahmetb/kubectx), [nsc](https://docs.nats.io/nats-tools/nsc), [git](https://git-scm.com/), [gcloud](https://cloud.google.com/sdk/) and [awscli](https://aws.amazon.com/cli/). Additionally the following services are provisioned: [istio](https://istio.io) and [nats](https://nats.io). + +Verify: -MDS can be provisioned to a Kubernetes cluster as follows: +```sh +% kubectl -n istio-system get pods +% kubectl -n nats get pods +% k9s & +``` -* Install and configure [Docker Desktop](https://download.docker.com/mac/stable/Docker.dmg) - * `preferences / advanced`: cpus:6, memory:8G, swap:1G - * `preferences / kubernetes`: enabled kubernetes -* Add `kubectl` to your PATH environment, e.g. for OSX: - * `export PATH=/Applications/Docker.app/Contents/Resources/bin:${PATH}` -* Ensure an active kubernetes cluster is configured and accessible: - * `kubectl config set-context docker-desktop` +#### Build : compile source into deployable images -Lastly, build and deploy MDS to your kubernetes cluster: +Compiling and packaging MDS into a deployable form is achived as follows: -```bash -./bin/mdsctl bootstrap build install:mds test:integration +```sh +% ./bin/mdsctl build ``` -To cleanup the MDS cluster consider: +Verify: -```bash -./bin/mdsctl uninstall +```sh +% docker images | grep mds* ``` -For a complete listing of available operations consider: +#### Run : install MDS + +(tbd: ?best profile?) -```bash -./bin/mdsctl +```sh +% ./bin/mdsctl -p processors install:mds ``` +Verify: + +```sh +% curl localhost/agency +``` + +#### In-Cluster Development +Due to the nature of MDS-Core being a highly portable Typescript project that compiles down into minified javascript for its images, rapidly development in-cluster can be quite challenging. MDS-Core utilizes [Okteto](https://okteto.com) to enable developers to actively develop their code in-cluster. + +After following the above steps to set up a local MDS cluster, you can override an existing service's deployment with these steps. +1. Update `mds-core/okteto.yml`'s `name` field to be set to the service you wish to replace (e.g. `mds-agency`) +2. +```sh +% curl https://get.okteto.com -sSfL | sh +``` +3. Install the `Remote - Kubernetes` VSCode extension. +4. Run `> Okteto Up` from the VSCode command palette. +* After the remote session opens, execute this in the new shell window: +```sh +% yarn +% cd packages/${SERVICE_NAME} +% yarn start +``` +5. This session is now safe to close, and you can reattach with the `okteto.${SERVICE_NAME}` ssh profile automatically added for you using the VSCode `Remote - SSH` package. +6. When you're completely done with your session, run `> Okteto Down` from the VSCode command palette, or `okteto down` from terminal to revert the changes made by Okteto, and return your service to its previous deployment. + +#### MDS Operations + +MDS operates atop the following services: [Kubernetes](https://kubernetes.io), [Istio](https://istio.io), [NATS](https://nats.io), [PostgreSQL](https://www.postgresql.org) and [Redis](https://redis.io). + +(tbd) + +#### Additional Considerations + +Access the database: + +```sh +% ./bin/mdsctl cli:postgresql +``` + +Access the cache: + +```sh +% ./bin/mdsctl cli:redis +``` + +(tbd) Access the event stream: + +```sh +% ./bin/mdsctl install:natsbox +``` + +Access the MDS cluster: + +```sh +% k9s +``` + +Display the complete set of operations: + +```sh +% ./bin/mdsctl +``` + +#### Cleanup + +```sh +% ./bin/mdsctl uninstall:mds uninstall +``` + +## Other + +To commit code, you will need the pre-commit tool, which can be installed via `brew install pre-commit`. For more information, see [SECURITY.md](.github/SECURITY.md) + ## Contributing See [CONTRIBUTING.md](.github/CONTRIBUTING.md) diff --git a/bin/mdsctl b/bin/mdsctl index 23d5c2176..8dc429c10 100755 --- a/bin/mdsctl +++ b/bin/mdsctl @@ -1,32 +1,48 @@ #!/usr/bin/env bash -osx=Darwin -linux=Linux +[[ -f ~/.mdsctl ]] && . ~/.mdsctl +osx=darwin +linux=linux -hash brew > /dev/null 2>&1 || { - case "${MDS_OS:-$(uname)}" in +exists() { + hash ${1} > /dev/null 2>&1 + echo $? +} + +lower() { + echo "${1}" | tr '[:upper:]' '[:lower:]' +} + +os() { + os=${MDS_OS:-$(uname)} + case "$(lower ${os})" in + ${osx} | ${linux}) echo $(lower ${os});; + *) echo "unsupported";; + esac; unset os +} + +[[ "$(exists brew)" != "0" ]] && { + case "$(os)" in ${osx}) - hash brew > /dev/null 2>&1 || \ - /usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)";; + /usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)";; ${linux}) mkdir -p ~/.linuxbrew/bin - hash git > /dev/null 2>&1 || (apt-get update; apt-get install -y git) + [[ "$(exists git)" != "0" ]] && (apt-get update; apt-get install -y git) git clone https://github.com/Homebrew/brew ~/.linuxbrew/Homebrew ln -s ~/.linuxbrew/Homebrew/bin/brew ~/.linuxbrew/bin [ $(grep -Fxq "eval \$(~/.linuxbrew/bin/brew shellenv)" ~/.bashrc; echo $?) == "1" ] && \ echo "eval \$(~/.linuxbrew/bin/brew shellenv)" >> ~/.bashrc - hash curl > /dev/null 2>&1 || (apt-get update; apt-get install -y curl) + [[ "$(exists curl)" != "0" ]] && (apt-get update; apt-get install -y curl) eval $(~/.linuxbrew/bin/brew shellenv);; *) usage "unsupported os: $(uname)";; esac } if [[ "$(bash -c 'echo $BASH_VERSION')" == 3* ]]; then - case "${MDS_OS:-$(uname)}" in + case "$(os)" in ${osx} | ${linux}) brew install bash - exec ${0} ${@} & - exit;; + $(brew --prefix bash)/bin/bash -c "${0} ${@}";; *) usage "unsupported os: $(uname)";; esac fi @@ -50,36 +66,72 @@ declare -A os=( ["osx"]="${osx}" ["linux"]="${linux}") declare -A configs=( + ["profiles"]="${MDS_PROFILE:-}" + ["local-context"]="${MDS_LOCACL_CONTEXT:-docker-desktop|docker-for-desktop|minikube}" ["home"]="${MDS_HOME:-$(installDir)}" ["os"]="${MDS_OS:-`uname`}" - ["rc"]="${MDS_RC:-~/.bashrc ~/.zshrc}" + ["provider"]="${MDS_PROVIDER:-}" + ["nvm-version"]="${MDS_NVM_VERSION:-14.2.0}" ["workdir"]="/tmp/mds/tools" - ["brew"]="${MDS_BREW:-oq jq gettext yarn nvm kubernetes-helm pgcli redis git}" - ["tools"]="${MDS_TOOLCHAIN:-kubernetes-helm,pgcli}" + # fixme: consider removing envsubst in favor of sed + ["brew"]="${MDS_BREW:-oq jq envsubst yarn nvm helm k9s kubectx git aws nsc sops}" + ["brew-envsubst"]="gettext" + ["brew-helm"]="helm@2" + ["brew-k9s"]="derailed/k9s/k9s" + ["brew-aws"]="awscli" + ["brew-oq"]="blacksmoke16/tap" + ["brew-nsc"]="nats-io/nats-tools" + ["brew-cask"]="${MDS_BREW_CASK:-gcloud}" + ["brew-cask-gcloud"]="google-cloud-sdk" + ["helm-version"]="${MDS_HELM_VERSION:-2.14.1}" + ["helm-distribution"]="${MDS_HELM_DISTRIBUTION:-https://get.helm.sh/helm-v${configs[helm-version]}-{os\}-amd64.tar.gz}" + ["helm-secrets"]="${MDS_HELM_SECRETS:-}" + ["helm-upgrade-release"]="${MDS_HELM_UPGRADE_RELEASE:-}" + ["helm-cluster"]="${MDS_HELM_CLUSTER:-}" + ["helm-environment"]="${MDS_HELM_ENVIRONMENT:-}" + ["helm-mds"]="${MDS_HELM_MDS=:-}" + ["interactive"]="${MDS_INTERACTIVE:-}" + ["paths"]="${MDS_PATHS:-envsubst helm}" + ["tools"]="${MDS_TOOLCHAIN:-helm,pgcli}" ["image-repository"]="${MDS_IMAGE_REPOSITORY:-}" ["simulator-repository"]="${MDS_SIMULATOR_REPOSITORY:-}" - ["bootstrap"]="${MDS_BOOTSTRAP:-helm,istio}" + ["operations-repository"]="${MDS_OPERATIONS_REPOSITORY:-}" + ["credentials-repository"]="${MDS_CREDENTIALS_REPOSITORY:-}" + ["bootstrap"]="${MDS_BOOTSTRAP:-helm,istio,nats}" ["install"]="${MDS_INSTALL:-mds}" ["test"]="${MDS_TEST:-unit,integration}" - ["uninstall"]="${MDS_UNINSTALL:-bookinfo,logging,mds,istio,helm}" + ["uninstall"]="${MDS_UNINSTALL:-logging,mds,nats,operations,simulator,istio,helm}" ["forward"]="${MDS_FORWARD:-default}" - ["token"]="${MDS_TOKEN:-dashboard}" ["reinstall"]="${MDS_REINSTALL:-mds}" ["pause"]="${MDS_PAUSE:-3}" - ["istio-version"]="${MDS_ISTIO_VERSION:-1.2.6}" #3.0}" # fixme: 1.3.0 istio-system pods stop at INIT at times - ["istio-profile"]="${MDS_ISTIO_PROFILE:--istio-sds-auth}" + ["pause-kubectl"]="${MDS_PAUSE_KUBECTL:-45}" + # note: istio-version=1.4.2 (latest) has been verified for development environments + ["istio-version"]="${MDS_ISTIO_VERSION:-1.3.6}" + # note: fails when installing istio-nodeagent (i.e. -istio-sds-auth) + ["istio-profile"]="${MDS_ISTIO_PROFILE:-}" ["istio"]="${MDS_ISTIO_PATH:-${configs[workdir]}/istio-${configs[istio-version]}}" + ["nats"]="${MDS_NATS_PATH:-${configs[workdir]}/nats}" + ["grafana-plugins"]="${MDS_GRAFANA_PLUGINS:-devopsprodigy-kubegraf-app}" ["simulator"]="${MDS_SIMULATOR:-${configs[workdir]}/simulator}" + ["operations"]="${MDS_OPERATIONS:-${configs[workdir]}/operations}" + ["credentials"]="${MDS_CREDENTIALS:-${configs[workdir]}/credentials}" ["ingress-domain"]="${MDS_INGRESS_DOMAIN:-}" ["ingress-gateway-key-path"]="${MDS_INGRESS_GATEWAY_KEY_PATH:-}" ["ingress-gateway-certificate-path"]="${MDS_INGRESS_GATEWAY_CERTIFICATE_PATH:-}" ["values"]="${MDS_VALUES:-}" ["values-mds"]="${MDS_VALUES_MDS:-}" + ["values-nats"]="${MDS_VALUES_NATS:-}" ["presets"]="${MDS_PRESETS:-}" - ["presets-mds"]="${MDS_PRESETS_MDS:-postgresql.password=Password123#,apis.mds-agency.migration=true}" + ["presets-mds-database-password"]="${MDS_PRESETS_MDS_DATABASE_PASSWORD:-Password123#}" + ["presets-mds"]="${MDS_PRESETS_MDS:-postgresql.password=${configs[presets-mds-database-password]},apis.mds-agency.migration=true}" + ["presets-nats"]="${MDS_PRESETS_NATS:-cluster.enabled=true}" #,natsbox.enabled=false}" ["sets"]="${MDS_SETS:-}" ["sets-mds"]="${MDS_SETS_MDS:-}" - ["sets-logging"]="${MDS_SETS_LOGGING:=fluentbit.backend.es.host=elasticsearch-master.util,fluentbit.backend.es.port=9200,fluentbit.backend.es.tls=off,kibana.env.ELASTICSEARCH_URL=http:elasticsearch-master.util:9200}" + ["sets-nats"]="${MDS_SETS_NATS:-}" + # todo: try scaling down istio's resource requirements (e.g. local dev): pilot.resources.requests.memory=512Mi} + ["sets-istio"]="${MDS_SETS_ISTIO:-}" + ["sets-logging"]="${MDS_SETS_LOGGING:-fluentbit.backend.es.host=elasticsearch-master.util,fluentbit.backend.es.port=9200,fluentbit.backend.es.tls=off,kibana.env.ELASTICSEARCH_URL=http:elasticsearch-master.util:9200}" + ["sets-simulator"]="${MDS_SETS_SIMULATOR:-jwt.enabled=false,registry=}" ["setfiles"]="${MDS_SET_FILES:-}" ["setfiles-mds"]="${MDS_SET_FILES_MDS:-}" ["namespace"]="${MDS_NAMESPACE:-default}" @@ -87,34 +139,77 @@ declare -A configs=( ["namespace-dashboard"]="${MDS_NAMESPACE_LOGGING:-dashboard}" ["namespace-logging"]="${MDS_NAMESPACE_LOGGING:-util}" ["namespace-mds"]="${MDS_NAMESPACE_MDS:-mds}" + ["namespace-simulator"]="${MDS_NAMESPACE_SIMULATOR:-${configs[namespace-mds]}}" + ["namespace-nats"]="${MDS_NAMESPACE_NATS:-nats}" + ["namespace-nats-init"]="${MDS_NAMESPACE_NATS_INIT:-nats-admin}" + ["namespace-nats-account-server"]="${MDS_NAMESPACE_NATS_ACCOUNT_SERVER:-${configs[namespace-nats]}}" + ["namespace-nats-box"]="${MDS_NAMESPACE_NATS_BOX:-${configs[namespace-nats]}}" + ["namespace-dns"]="${MDS_NAMESPACE_DNS:-default}" + ["namespace-curl"]="${MDS_NAMESPACE_CURL:-default}" ["dryrun"]="${MDS_DRY_RUN:-}" - ["quiet"]="${MDS_QUIET:-}") + ["quiet"]="${MDS_QUIET:-}" + ["keep-cache"]="${MDS_KEEP_CACHE:-}") declare -A helmRepositories=( ["stable"]="https://kubernetes-charts.storage.googleapis.com" - ["istio.io"]="https://storage.googleapis.com/istio-release/releases/1.3.0/charts" + ["istio.io"]="https://storage.googleapis.com/istio-release/releases/${configs[istio-version]}/charts" ["banzaicloud-stable"]="https://kubernetes-charts.banzaicloud.com" - ["elastic"]="https://helm.elastic.co") + ["elastic"]="https://helm.elastic.co" + ["bitnami"]="https://charts.bitnami.com/bitnami" + ["nats"]=" https://nats-io.github.io/k8s/helm/charts") +declare -A helmPlugins=( + ["unittest"]=https://github.com/lrills/helm-unittest + ["secrets"]=https://github.com/futuresimple/helm-secrets + ["diff"]="https://github.com/databus23/helm-diff --version master" + ["monitor"]="https://github.com/ContainerSolutions/helm-monitor" + ["edit"]="https://github.com/mstrzele/helm-edit" + ["last"]="https://github.com/adamreese/helm-last" + ["env"]="https://github.com/adamreese/helm-env" +) declare -A apps=( ["mds-agency"]="https://localhost:[port]/agency,mds,app" ["mds-audit"]="https://localhost:[port]/audit,mds,app" ["mds-compliance"]="https://localhost:[port]/compliance,mds,app" ["mds-daily"]="https://localhost:[port]/daily,mds,app" - ["mds-native"]="https://localhost:[port]/native,mds,app" + ["mds-geography"]="https://localhost:[port]/geography,mds,app" + ["mds-geography-author"]="https://localhost:[port]/geography-author,mds,app" + ["mds-jurisdiction"]="https://localhost:[port]/jurisdiction,mds,app" + ["mds-jurisdiction-service"]="https://localhost:[port]/jurisdiction-service,mds,app" ["mds-policy"]="https://localhost:[port]/policy,mds,app" ["mds-policy-author"]="https://localhost:[port]/policy-author,mds,app" ["mds-postgresql"]="https://localhost:[port]/postgresql,mds,app,postgresql" ["mds-redis"]="https://localhost:[port]/redis,mds,app,redis" - ["dashboard"]="https://localhost:[port],dashboard,k8s-app,kubernetes-dashboard" ["grafana"]="http://localhost:[port]/dashboard/db/istio-mesh-dashboard,istio-system,app" ["kibana"]="http://localhost:[port],util,app" ["prometheus"]="http://localhost:[port],istio-system,app" ["jaeger"]="http://localhost:[port],istio-system,app,jaeger,16686" - ["kiali"]="http://localhost:[port],istio-system,app,kiali,20001" - ["bookinfo"]="http://localhost:[port],booking,app") + ["kiali"]="http://localhost:[port],istio-system,app,kiali,20001") yellow=`tput setaf 3` red=`tput setaf 9` reset=`tput sgr0` +initialize() { + for p in ${configs[paths]}; do + export PATH=$(brew --prefix ${configs[brew-${p}]:-${p}})/bin:${PATH} + done; unset p + + # fixme: make minimal-version test more flexible; may not be required when helm:3 is supported more broadly + # fixme: add conditional `hash helm` + # if [[ ! "$(helm version --short | grep -i client | cut -d ':' -f 2)" == *"${configs[helm-version]}"* ]]; then + os=$(os) + + if [[ ! -x ${configs[workdir]}/helm/${os}-amd64/helm ]]; then + mkdir -p ${configs[workdir]}/helm + curl -o ${configs[workdir]}/helm/helm-v${helmVersion}.tgz ${configs[helm-distribution]/{os\}/${os}} > /dev/null 2>&1 + (cd ${configs[workdir]}/helm; tar zxvf ${configs[workdir]}/helm/helm-v${helmVersion}.tgz > /dev/null 2>&1) + fi + + export PATH=${configs[workdir]}/helm/${os}-amd64:${PATH} + unset os + # fi + + export NVM_DIR=${HOME}/.nvm +} + warn() { echo "${yellow}warn: ${1}${reset}" } @@ -123,10 +218,29 @@ error() { echo "${red}warn: ${1}${reset}" } +lower() { + echo "${1}" | tr '[:upper:]' '[:lower:]' +} + +prompt() { + IFS= read -rp "${yellow}${1}${reset}" response + echo $(lower ${response}) +} + beerMe() { [ ${#configs[quiet]} -eq 0 ] && echo -ne "\xF0\x9F\x8D\xBA " } +urlEncode() { + local LANG=C i c e='' + for ((i=0;i<${#1};i++)); do + c=${1:$i:1} + [[ "${c}" =~ [a-zA-Z0-9\.\~\_\-] ]] || printf -v c '%%%02X' "'${c}" + e+="${c}" + done; unset c + echo "${e}" +} + usage() { [ "${1}" ] && warn "${1}" @@ -147,16 +261,15 @@ where preset-key in: minimal : minimal service deployment; default: preset(local) + mds-agency, postgresql, redis local : local resource (cpu, memory) deployment; default: limitsCpu=200m, limitsMemory=200Mi, requestsCpu=20m, requestsMemory=32Mi disabled : disable service deployment; default: all - no-events : disable eventing services no-persistence : disable persistence commands: bootstrap : install dependencies; default: ${configs[tools]},${configs[bootstrap]} build : build project install[:{service[,{service}]}] : install specified service; default: ${configs[install]} + cycle[]:[app[,app]]] : restart app; default: cycle all services test[:unit,integration] : preform specified tests; default: ${configs[test]} open:[app[,{app}]] : opens a browser for the provided application(s) - token[:dashboard] : get specified token, copied to copy-paste buffer for ${os[osx]}; default: ${configs[token]} forward[:{app[,{app}]}] : register port-forwarding for the provided application(s); default: ${configs[forward]} unforward[:{app[,{app}]}] : deregister port-forwarding for the provided application(s) cli:[postgresql,redis] : create a cli console for the provided service @@ -167,7 +280,6 @@ commands: where service in: helm - dashboard kiali istio mds @@ -176,25 +288,29 @@ where service in: metricsAdapter logging metrics - bookinfo + nats + grafana + dns + curl where app in: mds-agency mds-audit mds-compliance mds-daily - mds-native + mds-geography + mds-geography-author + mds-jurisdiction + mds-jurisdiction-service mds-policy mds-policy-author mds-postgresql mds-redis-headless - dashboard : kubernetes dashboard; see https://github.com/kubernetes/dashboard grafana : grafana; see: https://grafana.com kibana : kibana; see https://www.elastic.co/products/kibana prometheus : prometheus; see https://prometheus.io jaeger : jaeger; see https://www.jaegertracing.io kiali : kiali; see https://www.kiali.io - bookinfo : bookinfo; see https://istio.io/docs/examples/bookinfo example: % ./bin/$(basename ${0}) bootstrap build install:mds test:integration @@ -207,38 +323,53 @@ EOF } bootstrap() { - for l in ${configs[brew]}; do - lc=${l} - case ${l} in - kubernetes-helm) lc=helm;; - gettext) lc=envsubst;; - oq) brew tap blacksmoke16/tap;; - esac + check ${FUNCNAME[0]} + + for r in ${configs[brew]}; do + rc=${configs[brew-${r}]:-${r}} + + [[ "$(exists ${r})" != "0" ]] && { + case ${r} in + oq) brew tap ${rc}; rc=${r};; + nsc) brew tap ${rc}; rc=${rc}/${r};; + esac - hash ${lc} > /dev/null 2>&1 || { - brew install ${l} + brew install ${rc} # todo: unable to detect nvm install via subshell given it is an alias - case ${l} in + case ${r} in nvm) + # todo: normalize-nvm env [ ! -d ~/.nmv ] && mkdir -p ~/.nvm - [ $(grep -Fxq "export NMV_DIR=\"\${HOME}/.nvm\"" ~/.bashrc; echo $?) == "1" ] && \ - echo "export NMV_DIR=\"\${HOME}/.nvm\"" >> ~/.bashrc - [ $(grep -Fxq "eval . \$(brew --prefix nvm)/nvm.sh" ~/.bashrc; echo $?) == "1" ] && \ - echo "eval . \$(brew --prefix nvm)/nvm.sh" >> ~/.bashrc + export NVM_DIR=${HOME}/.nvm . $(brew --prefix nvm)/nvm.sh + nvm install ${configs[nvm-version]} + nvm alias default ${configs[nvm-version]} + nvm use --delete-prefix v${configs[nvm-version]} nvm install node - nvm use node - nvm install 10.16.3 - nvm alias default 10.16.3;; - gettext) - brew link --force gettext - export PATH=$(brew --prefix gettext)/bin:${PATH};; + nvm use node;; + envsubst) brew link --force ${lc};; esac } - done; unset l + done; unset r rc + + for r in ${configs[brew-cask]}; do + rc=${configs[brew-cask-${r}]:-${r}} - for p in lerna cypress mocha chai mochawesome; do + [[ "$(exists ${r})" != "0" ]] && { + brew cask install ${rc} + } + done; unset c rc + + initialize + + # todo: normalize nvm-env + . $(brew --prefix nvm)/nvm.sh + [ -s "${NVM_DIR}/nvm.sh" ] && . ${NVM_DIR}/nvm.sh + nvm install ${configs[nvm-version]} + nvm alias default ${configs[nvm-version]} + + for p in lerna mocha chai mochawesome; do yarn ${p} --version > /dev/null 2>&1 || yarn add -W ${p} done; unset p @@ -253,15 +384,23 @@ build() { check ${FUNCNAME[0]} for s in $(services); do - si=$(docker images --filter=reference="${s}" -q) - [ ${#si} -ge 1 ] && docker rmi -f ${si} - done; unset s si + for i in $(docker images --filter=reference="${s}" -q); do + [ ${#i} -ge 1 ] && docker rmi -f ${i} + done; unset i + done; unset s -# todo: provide build-config-option for progressive builds, ie no-clean + # todo: provide build-config-option for progressive builds, ie no-clean (cd ${configs[home]}; + # todo: normalize nvm-env + . $(brew --prefix nvm)/nvm.sh; + [ -s "${NVM_DIR}/nvm.sh" ] && . ${NVM_DIR}/nvm.sh; + nvm install ${configs[nvm-version]}; + nvm alias default ${configs[nvm-version]}; + [ -z "${configs[keep-cache]}" ] && yarn clean; yarn clean; yarn; - yarn image) + yarn image; + yarn values) installImages } @@ -270,25 +409,29 @@ installImages() { check ${FUNCNAME[0]} if [ "${configs[image-repository]}" ]; then - for i in $(docker images --filter "reference=mds-*" --format '{{ .Repository}}:{{ .Tag}}'); do - # docker tag ${i} ${configs[image-repository]}/${i} + case "${configs[provider],,}" in + aws) eval $(aws ecr get-login --no-include-email);; + gcp) ;; + *) ;; + esac + + for i in $(docker images --filter "reference=mds-*" --format '{{.Repository}}:{{.Tag}}'); do + docker tag ${i} ${configs[image-repository]}/${i} docker push ${configs[image-repository]}/${i} done; unset i fi } buildImage() { - docker build -t ${1}$(getBranchSuffix) . + docker build -t ${1}$(getBranchSuffix)$(getCommitHash) . } getBranchSuffix() { - case "$(git rev-parse --abbrev-ref HEAD)" in - master) suffix="";; - develop) suffix="-prerelease";; - *) suffix="-unstable";; - esac + git rev-parse --abbrev-ref HEAD | sed -E -e 's/[^-a-zA-Z0-9]/-/g' -e 's/^/-/' +} - echo ${suffix} +getCommitHash() { + git rev-parse --short HEAD | sed -E -e 's/[^-a-zA-Z0-9]/-/g' -e 's/^/-/' } # TODO: DRY @@ -306,7 +449,13 @@ templateDockerCompose() { # TODO: DRY templateValues() { - suffix=$(getBranchSuffix) + branchSuffix=$(getBranchSuffix) + if [ "$branchSuffix" = "-master" ]; then + suffix="$branchSuffix" + else + suffix="$branchSuffix"$(getCommitHash) + fi + for directory in container-images/mds-*; do api=$(basename $directory) version=$(jq -r '.version' < $directory/package.json) @@ -314,7 +463,7 @@ templateValues() { export $varname=${version}${suffix} done mkdir -p dist - envsubst < helm/mds/values.yaml.tpl > dist/values.html + envsubst < helm/mds/values.yaml.tpl > dist/values.yaml echo "Wrote dist/values.yaml" } @@ -342,22 +491,34 @@ updateHelmDependencies() { installHelm() { check ${FUNCNAME[0]} - hash helm > /dev/null 2>&1 || - case "${configs[os]}" in - ${os[osx]} | ${os[linux]}) brew install kubernetes-helm;; - *) usage "unsupported os: ${configs[os]}";; - esac + # todo: cleanup + # todo: add guards to only exec if deployment exists +kubectl delete deployment tiller-deploy -n kube-system +kubectl delete service tiller-deploy -n kube-system +kubectl create serviceaccount --namespace kube-system tiller +kubectl create clusterrolebinding tiller-cluster-rule --clusterrole=cluster-admin --serviceaccount=kube-system:tiller +kubectl patch deploy --namespace kube-system tiller-deploy -p '{"spec":{"template":{"spec":{"serviceAccount":"tiller"}}}}' - helm init --upgrade || usage "helm intialization failure" + helm init --service-account tiller --override spec.selector.matchLabels.'name'='tiller',spec.selector.matchLabels.'app'='helm' --output yaml | sed 's@apiVersion: extensions/v1beta1@apiVersion: apps/v1@' | kubectl apply -f - +# helm init --force-upgrade || usage "helm intialization failure" pause 5 for r in "${!helmRepositories[@]}"; do - helm repo add ${r} ${helmRepositories[${r}]} + [[ $(helm repo list | grep ".*${r}.*${helmRepositories[${r}]}.*" > /dev/null 2>&1; echo $?) == "1" ]] && { + helm repo add ${r} ${helmRepositories[${r}]} + } done; unset r updateHelmDependencies - [[ $(helm plugin list | grep unittest) ]] || helm plugin install https://github.com/lrills/helm-unittest + for p in "${!helmPlugins[@]}"; do + [[ $(helm plugin list | grep ${p}) ]] || helm plugin install ${helmPlugins[${p}]} + done; unset p + + case "${configs[provider],,}" in + aws | gcp) installTiller;; + *);; + esac } helmOptions() { @@ -369,54 +530,56 @@ helmOptions() { unset opts } +# fixme: cleanup installTiller() { check ${FUNCNAME[0]} - if [ $(kubectl get serviceaccount tiller --namespace kube-system > /dev/null 2>&1; echo $?) == "1" ]; then - warn "NOT FOR PRODUCTION USE" - cat << EOF | kubectl create -f - + case "${configs[provider],,}" in + gcp) + kubectl create clusterrolebinding cluster-admin-binding \ + --clusterrole=cluster-admin \ + --user="$(gcloud config get-value core/account)" + kubectl create serviceaccount --namespace kube-system tiller + kubectl create clusterrolebinding tiller-cluster-rule --clusterrole=cluster-admin --serviceaccount=kube-system:tiller + kubectl patch deploy --namespace kube-system tiller-deploy -p '{"spec":{"template":{"spec":{"serviceAccount":"tiller"}}}}';; + aws) ;& + *) + if [ $(kubectl get serviceaccount tiller --namespace kube-system > /dev/null 2>&1; echo $?) == "1" ]; then + cat << EOF | kubectl create -f - apiVersion: v1 kind: ServiceAccount metadata: name: tiller namespace: kube-system --- -apiVersion: rbac.authorization.k8s.io/v1beta1 +apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: - name: tiller-clusterrolebinding -subjects: -- kind: ServiceAccount name: tiller - namespace: kube-system roleRef: + apiGroup: rbac.authorization.k8s.io kind: ClusterRole name: cluster-admin - apiGroup: "" +subjects: +- kind: ServiceAccount + name: tiller + namespace: kube-system EOF - - helm init --service-account tiller --upgrade - pause 5 - fi + helm init --service-account tiller --upgrade + fi;; + esac } -installDashboard() { +installNamespace() { check ${FUNCNAME[0]} - helm install ${configs[home]}/helm/dashboard --name dashboard --namespace ${configs[namespace-dashboard]} \ - ${configs[values]:+$(helmOptions values ${configs[values]})} \ - ${configs[setfiles]:+$(helmOptions set ${configs[setfiles]})} \ - ${configs[sets]:+$(helmOptions set ${configs[sets]})} \ - ${configs[dryrun]:+--dry-run} -} - -installNamespace() { for ns in ${@}; do kubectl get namespace ${ns} > /dev/null 2>&1 || kubectl create namespace ${ns} ${configs[dryrun]:+--dry-run} done; unset ns } installIstio() { + check ${FUNCNAME[0]} if [ ! -d ${configs[istio]} ]; then @@ -424,33 +587,66 @@ installIstio() { (cd ${configs[workdir]}; curl -L https://git.io/getLatestIstio | ISTIO_VERSION=${configs[istio-version]} sh -) fi +# fixme: disable prometheus kubectl get namespace istio-system > /dev/null 2>&1 || { ${configs[istio]}/bin/istioctl verify-install || error "istio verify installation failure" installNamespace istio-system pause 2 - helm template ${configs[istio]}/install/kubernetes/helm/istio-init \ - --name istio-init --namespace istio-system | \ - kubectl apply -f - ${configs[dryrun]:+--dry-run} - # todo: query pod status - pause 5 + + case "${configs[provider],,}" in + gcp) + helm template ${configs[istio]}/install/kubernetes/helm/istio-init \ + --name istio-init --namespace istio-system \ + ${configs[dryrun]:+--dry-run} | kubectl apply -f -;; + *) + helm install ${configs[istio]}/install/kubernetes/helm/istio-init \ + --name istio-init --namespace istio-system \ + ${configs[dryrun]:+--dry-run};; + esac + kubectl -n istio-system wait --for=condition=complete job --all (( $(kubectl get crds | grep "istio.io" | wc -l) == 23 )) && \ echo "istio successfully installed" || echo "istio installation failure" - helm template ${configs[istio]}/install/kubernetes/helm/istio \ - --name istio --namespace istio-system \ - --values ${configs[istio]}/install/kubernetes/helm/istio/values${configs[istio-profile]}.yaml | \ - kubectl apply -f - ${configs[dryrun]:+--dry-run} - labelNamespaceIstioInjection default + case "${configs[provider],,}" in + gcp) + helm template ${configs[istio]}/install/kubernetes/helm/istio \ + --name istio --namespace istio-system \ + --values ${configs[istio]}/install/kubernetes/helm/istio/values${configs[istio-profile]}.yaml \ + ${configs[sets]:+$(helmOptions set ${configs[sets]})} \ + ${configs[sets-istio]:+$(helmOptions set ${configs[sets-istio]})} \ + ${configs[dryrun]:+--dry-run} | kubectl apply -f -;; + *) + helm install ${configs[istio]}/install/kubernetes/helm/istio \ + --name istio --namespace istio-system \ + --values ${configs[istio]}/install/kubernetes/helm/istio/values${configs[istio-profile]}.yaml \ + ${configs[sets]:+$(helmOptions set ${configs[sets]})} \ + ${configs[sets-istio]:+$(helmOptions set ${configs[sets-istio]})} \ + ${configs[dryrun]:+--dry-run};; + esac + + # labelNamespaceIstioInjection default + # pause 20 + kubectl --namespace istio-system wait --for=condition=Ready \ + $(kubectl -n istio-system get pod --selector app=istio-ingressgateway --output name --no-headers) \ + --timeout=$((${configs[pause-kubectl]}+30))s || error "istio installation error" } } labelNamespaceIstioInjection() { + check ${FUNCNAME[0]} + kubectl get namespace ${1} > /dev/null 2>&1 && \ kubectl label namespace ${1} istio-injection=enabled --overwrite=true ${configs[dryrun]:+--dry-run} } +unlabelNamespacesIstioInjection() { + check ${FUNCNAME[0]} + + kubectl label namespaces --all istio-injection- +} + installLogging() { check ${FUNCNAME[0]} @@ -479,10 +675,26 @@ installMetrics() { } } +installGrafana() { + (( $(helm list --short grafana | wc -l) == 0 )) && { + helm install --name grafana bitnami/grafana --set admin.user=admin --set admin.password=pwd --set plugins=${grafana-plugins} + # todo: port-forward + } +} + +imageTag() { + case "${configs[provider],,}" in + aws) aws ecr describe-images --repository-name ${1} --output text \ + --query 'sort_by(imageDetails,& imagePushedAt)[*].imageTags[*]' | tr '\t' '\n' | tail -1;; + gcp) gcloud container images list-tags --limit=1 --format='get(TAGS)' ${configs[image-repository]}/${1};; + *) docker images ${1} --format {{.Tag}};; + esac +} + imageVersion() { for s in $(services); do - is+="${is:+,}sets-mds+=apis.${s}.version=$(docker images ${s} --format {{.Tag}})" - done; unset s mi + is+="${is:+,}sets-mds+=apis.${s}.version=$(imageTag ${s})" + done; unset s echo ${is} } @@ -490,12 +702,38 @@ imageVersion() { installMds() { check ${FUNCNAME[0]} - installFallbackCertificate - kubectl get namespace ${configs[namespace-mds]} > /dev/null 2>&1 || { + # installFallbackCertificate + + (cd ${configs[home]}/helm/mds; /bin/rm -rf tmpcharts; helm dependency update) + + # todo: don't lock on namespace existence + [[ "$(helm list --short ${configs[namespace-mds]}-mds${configs[helm-upgrade-release]})" = "${configs[namespace-mds]}-mds${configs[helm-upgrade-release]}" ]] || { installNamespace ${configs[namespace-mds]} labelNamespaceIstioInjection ${configs[namespace-mds]} - configure $(imageVersion) - helm install ${configs[home]}/helm/mds --name mds --namespace ${configs[namespace-mds]} \ + # note: remote registries may require image-version overrides + [ -z "${configs[image-repository]}" ] && \ + configure $(imageVersion) || configure sets-mds+=registry=${configs[image-repository]} + + pause 5 + # kubectl -n ${configs[namespace-mds]} wait --for=condition=complete job --all + + [[ -d ${configs[operations]}/${configs[helm-cluster]} && "${configs[helm-environment]}" ]] && { + for f in \ + ${configs[operations]}/${configs[helm-cluster]}/values.yaml \ + ${configs[operations]}/${configs[helm-cluster]}/secrets.yaml \ + ${configs[operations]}/${configs[helm-cluster]}/secrets.${configs[helm-environment]}.yaml \ + ${configs[operations]}/${configs[helm-cluster]}/env.${configs[helm-environment]}.yaml; do + [[ -f ${f} && -r ${f} ]] && cv+="${cv:+ }${f}" + done; unset f + } + + # fixme: impl upgrade, interactive (diff) + op=install #$([ $(helm list --short | grep ${configs[namespace-mds]}-mds${configs[helm-upgrade-release]}) ] && echo upgrade || echo install) + + helm ${configs[helm-secrets]:+secrets} ${op} ${configs[home]}/helm/mds \ + --name ${configs[namespace-mds]}-mds${configs[helm-upgrade-release]} \ + --namespace ${configs[namespace-mds]} \ + ${cv:+$(helmOptions values ${cv})} \ ${configs[values]:+$(helmOptions values ${configs[values]})} \ ${configs[values-mds]:+$(helmOptions values ${configs[values-mds]})} \ ${configs[presets]:+$(helmOptions set ${configs[presets]})} \ @@ -505,11 +743,27 @@ installMds() { ${configs[setfiles]:+$(helmOptions set-file ${configs[setfiles]})} \ ${configs[setfiles-mds]:+$(helmOptions set-file ${configs[setfiles-mds]})} \ ${configs[dryrun]:+--dry-run --debug} + + unset op cv pause 12 + # kubectl -n ${configs[namespace-mds]} wait --for=condition=complete job --all } } +cycle() { + # check ${FUNCNAME[0]} + + for a in ${@}; do + (( $(kubectl -n ${configs[namespace-mds]} get pod -l app=${a} --ignore-not-found=true | wc -l) == 2 )) && { + p=$(kubectl -n ${configs[namespace-mds]} get pod -l app=${a} -o jsonpath='{.items[0].metadata.name}') + [[ ! -z "${p}" ]] && kubectl -n ${configs[namespace-mds]} delete pod ${p} + } + done; unset a p +} + installMetricsAdapter() { + check ${FUNCNAME[0]} + helm status kube-metrics-adapter > /dev/null 2>&1 || helm install --name kube-metrics-adapter --namespace ${configs[namespace-metrics-adapter]} \ banzaicloud-stable/kube-metrics-adapter @@ -517,6 +771,7 @@ installMetricsAdapter() { installFallbackCertificate() { if [[ ${configs[ingress-domain]+_} && ${#configs[ingress-domain]} -ge 1 ]]; then + check ${FUNCNAME[0]} TMPCERT=$(mktemp /tmp/k8s-fallback-cert.XXXXXX) || exit 1 openssl req -x509 -nodes -days 1 -newkey rsa:2048 \ -keyout ${TMPCERT}.key -out ${TMPCERT}.crt \ @@ -524,14 +779,15 @@ installFallbackCertificate() { kubectl -n istio-system create secret tls gateway-fallback \ --key ${TMPCERT}.key --cert ${TMPCERT}.crt /bin/rm -rf ${TMPCERT} ${TMPCERT}.key ${TMPCERT}.crt - else - warn "ingress-domain is unspecified" fi } installIngressGatewayCertificate() { + check ${FUNCNAME[0]} + if [[ ${configs[ingress-gateway-key-path]+_} && ${#configs[ingress-gateway-key-path]} -ge 1 && ${configs[ingress-gateway-certificate-path]+_} && ${#configs[ingress-gateway-certificate-path]} -ge 1 ]]; then + check ${FUNCNAME[0]} kubectl create -n istio-system secret tls istio-ingressgateway-certs \ --key ${configs[ingress-gateway-key-path]} \ --cert ${configs[ingress-gateway-certificate-path]} \ @@ -547,37 +803,294 @@ installSimulator() { if [[ ! -d ${configs[simulator]} ]]; then mkdir -p ${configs[workdir]} (cd ${configs[workdir]}; - git clone ${configs[simulator-repository]} ${configs[simulator]}) + git clone ${configs[simulator-repository]} ${configs[simulator]} || \ + usage "invalid repository: ${configs[simulator-repository]}"; + cd ${configs[simulator]}; + git pull) fi - (cd ${configs[simulator]}; - git pull; - docker build -t trackgen .; - helm install ./helm --name trackgen) + [[ "$(helm list --short ${configs[namespace-simulator]}-trackgen)" = "${configs[namespace-simulator]}-trackgen" ]] || { + for i in $(docker images --filter=reference="trackgen" -q); do + docker rmi -f ${i} + done; unset i + (cd ${configs[simulator]}; + export version=0.0.1$(getBranchSuffix)$(getCommitHash); + docker build -t trackgen:${version} .; + [[ "${configs[image-repository]}" ]] && { + docker tag trackgen:${version} ${configs[image-repository]}/trackgen:${version}; + docker push ${configs[image-repository]}/trackgen:${version}; + } + helm install ${configs[simulator]}/helm --name ${configs[namespace-simulator]}-trackgen \ + ${configs[namespace-simulator]:+--namespace ${configs[namespace-simulator]}} \ + --set version=${version} \ + ${configs[values]:+$(helmOptions values ${configs[values]})} \ + ${configs[presets]:+$(helmOptions set ${configs[presets]})} \ + ${configs[sets]:+$(helmOptions set ${configs[sets]})} \ + ${configs[sets-simulator]:+$(helmOptions set ${configs[sets-simulator]})} \ + ${configs[setfiles]:+$(helmOptions set-file ${configs[setfiles]})} \ + ${configs[dryrun]:+--dry-run --debug}) + } } -installBookinfo() { +installOperations() { check ${FUNCNAME[0]} - kubectl get namespace bookinfo-app > /dev/null 2>&1 || { - installNamespace bookinfo-app - labelNamespaceIstioInjection bookinfo-app - kubectl apply -n bookinfo-app -f ${configs[istio]}/samples/bookinfo/platform/kube/bookinfo.yaml \ - ${configs[dryrun]:+--dry-run} - kubectl apply -n bookinfo-app -f ${configs[istio]}/samples/bookinfo/networking/destination-rule-all.yaml \ - ${configs[dryrun]:+--dry-run} + if [[ ! -d ${configs[operations]} ]]; then + mkdir -p ${configs[workdir]} + (cd ${configs[workdir]}; + git clone ${configs[operations-repository]} ${configs[operations]} || \ + usage "invalid repository: ${configs[operations-repository]}"; + cd ${configs[operations]}; + git pull) + fi +} + +installCredentials() { + check ${FUNCNAME[0]} + + [[ ! -d ${configs[credentials]} && -n "${configs[credentials-repository]}" ]] && { + mkdir -p ${configs[workdir]} + (cd ${configs[workdir]}; + git clone ${configs[credentials-repository]} ${configs[credentials]} || \ + usage "invalid repository: ${configs[credentials-repository]}"; + cd ${configs[credentials]}; + git pull) + } || return 0 +} + +nkeysPath() { + echo $([[ -d ${configs[credentials]}/nats/nsc/nkeys ]] && \ + echo ${configs[credentials]}/nats/nsc/nkeys || echo ${HOME}/.nkeys) +} + +nscHome() { + echo $([[ -d ${configs[credentials]}/nats/nsc/nsc/nats ]] && \ + echo ${configs[credentials]}/nats/nsc/nsc/nats || echo ${HOME}/.nsc/nats) +} + +installNsc() { + check ${FUNCNAME[0]} + + (export NKEYS_PATH=$(nkeysPath) + export NSC_HOME=$(nscHome) + cd ${NSC_HOME} + nsc init --name KO > /dev/null 2>&1 || echo "trusted operator already exists" + nsc describe operator --name KO > /dev/null 2>&1 || nsc add operator --name KO + nsc describe account --name SYS > /dev/null 2>&1 || nsc add account --name SYS + nsc describe user --account SYS --name sys > /dev/null 2>&1 || nsc add user --account SYS --name sys) + + [[ ! -f ${configs[nats]}/resolver.conf ]] && + (export NKEYS_PATH=$(nkeysPath) + export NSC_HOME=$(nscHome) + cd ${NSC_HOME} + mkdir -p ${configs[nats]} + nsc generate config --mem-resolver --sys-account sys > ${configs[nats]}/resolver.conf) +} + +installNas() { + check ${FUNCNAME[0]} + + releaseName="${configs[nats-namespace-account-server]}-nats-account-server}" + releases="$(helm list --quiet)" + + if ! echo $releases | grep -E -q "(^| )$releaseName($| )"; then # this release has not yet been installed + nscOperatorData=${configs[nats]}/nats-operator-data.zip + + [[ ! -f ${nscOperatorData} ]] && (cd $(nscHome); zip -r ${nscOperatorData} .) + # [[ ! -f helm/nats-account-server/nats-operator-data.zip ]] && ln -s ${nscOperatorData} helm/nats-account-server/nats-operator-data.zip + + helm install ${configs[home]}/helm/nats-account-server --name ${configs[namespace-nats-account-server]}-nats-account-server \ + ${configs[namespace-nats-account-server]:+--namespace ${configs[namespace-nats-account-server]}} \ + ${configs[values]:+$(helmOptions values ${configs[values]})} \ + ${configs[values-nats]:+$(helmOptions values ${configs[values-nats]})} \ + ${configs[presets]:+$(helmOptions set ${configs[presets]})} \ + ${configs[presets-nats]:+$(helmOptions set ${configs[presets-nats]})} \ + ${configs[sets]:+$(helmOptions set ${configs[sets]})} \ + ${configs[sets-nats]:+$(helmOptions set ${configs[sets-nats]})} \ + ${configs[setfiles]:+$(helmOptions set-file ${configs[setfiles]})} \ + ${configs[setfiles-nats]:+$(helmOptions set-file ${configs[setfiles-nats]})} \ + # $(helmOptions set nscOperatorData=nats-operator-data.zip) \ + ${configs[dryrun]:+--dry-run --debug} + # fixme: push to helm chart + kubectl --namespace ${configs[namespace-nats-account-server]} create configmap nats-operator-data --from-file ${nscOperatorData} + + kubectl --namespace ${configs[namespace-nats-account-server]} wait --for=condition=Ready pod --selector=app=nats-account-server \ + --timeout=${configs[pause-kubectl]}s || error "nas installation error" + pause 3 + fi +} + +installNatsInit() { + check ${FUNCNAME[0]} + + [[ "$(helm list --short ${configs[namespace-nats]}-nats-init)" = "${configs[namespace-nats]}-nats-init" ]] || { + helm install ${configs[home]}/helm/nats-init --name ${configs[namespace-nats]}-nats-init \ + ${configs[namespace-nats]:+--namespace ${configs[namespace-nats]}} + ${configs[values]:+$(helmOptions values ${configs[values]})} \ + ${configs[values-nats]:+$(helmOptions values ${configs[values-nats]})} \ + ${configs[presets]:+$(helmOptions set ${configs[presets]})} \ + ${configs[presets-nats]:+$(helmOptions set ${configs[presets-nats]})} \ + ${configs[sets]:+$(helmOptions set ${configs[sets]})} \ + ${configs[sets-nats]:+$(helmOptions set ${configs[sets-nats]})} \ + ${configs[setfiles]:+$(helmOptions set-file ${configs[setfiles]})} \ + ${configs[setfiles-nats]:+$(helmOptions set-file ${configs[setfiles-nats]})} \ + ${configs[dryrun]:+--dry-run --debug} # > /dev/null 2>&1 + # fixme: do better; wait for crd(natsclusters.nats.io, natsservicerles.nats.io) + pause 3 } } +installNats() { + check ${FUNCNAME[0]} + + [[ "$(helm list --short ${configs[namespace-nats]}-nats-server)" = "${configs[namespace-nats]}-nats-server" ]] || { + systemAccount=$(nsc describe account --name SYS | grep "Account ID" | awk '{print $5}') + operatorJwt=$(< $(nscHome)/KO/KO.jwt) + + helm install ${configs[home]}/helm/nats-server --name ${configs[namespace-nats]}-nats-server \ + ${configs[namespace-nats]:+--namespace ${configs[namespace-nats]}} \ + ${configs[values]:+$(helmOptions values ${configs[values]})} \ + ${configs[values-nats]:+$(helmOptions values ${configs[values-nats]})} \ + ${configs[presets]:+$(helmOptions set ${configs[presets]})} \ + ${configs[presets-nats]:+$(helmOptions set ${configs[presets-nats]})} \ + ${configs[sets]:+$(helmOptions set ${configs[sets]})} \ + ${configs[sets-nats]:+$(helmOptions set ${configs[sets-nats]})} \ + $(helmOptions set systemAccount=${systemAccount}) \ + $(helmOptions set operatorJwt=${operatorJwt}) \ + ${configs[setfiles]:+$(helmOptions set-file ${configs[setfiles]})} \ + ${configs[setfiles-nats]:+$(helmOptions set-file ${configs[setfiles-nats]})} \ + ${configs[dryrun]:+--dry-run --debug} + + pause 1 + # fixme: check on nats pod status + kubectl --namespace ${configs[namespace-nats]} wait --for=condition=Ready pods \ + --selector=app=${configs[namespace-nats]}-nats-server --timeout=${configs[pause-kubectl]}s || \ + error "nats-server installation error" + } +} + +installNatsbox() { + check ${FUNCNAME[0]} + + [[ "$(helm list --short ${configs[namespace-nats-box]}-nats-box)" = "${configs[namespace-nats-box]}-nats-box" ]] || \ + helm install ${configs[home]}/helm/nats-box --name ${configs[namespace-nats-box]}-nats-box \ + ${configs[namespace-nats-box]:+--namespace ${configs[namespace-nats-box]}} \ + ${configs[values]:+$(helmOptions values ${configs[values]})} \ + ${configs[values-nats]:+$(helmOptions values ${configs[values-nats]})} \ + ${configs[presets]:+$(helmOptions set ${configs[presets]})} \ + ${configs[presets-nats]:+$(helmOptions set ${configs[presets-nats]})} \ + ${configs[sets]:+$(helmOptions set ${configs[sets]})} \ + ${configs[sets-nats]:+$(helmOptions set ${configs[sets-nats]})} \ + ${configs[setfiles]:+$(helmOptions set-file ${configs[setfiles]})} \ + ${configs[setfiles-nats]:+$(helmOptions set-file ${configs[setfiles-nats]})} \ + ${configs[dryrun]:+--dry-run --debug} +} + +installSurveyor() { + [[ "$(helm list --short ${configs[namespace-nats]}-prometheus-operator)" = "${configs[namespace-nats]}-prometheus-operator" ]] || { + helm install ${configs[home]}/helm/prometheus-operator --name ${configs[namespace-nats]}-prometheus-operator \ + ${configs[namespace-nats]:+--namespace ${configs[namespace-nats]}} \ + ${configs[values]:+$(helmOptions values ${configs[values]})} \ + ${configs[values-nats]:+$(helmOptions values ${configs[values-nats]})} \ + ${configs[presets]:+$(helmOptions set ${configs[presets]})} \ + ${configs[presets-nats]:+$(helmOptions set ${configs[presets-nats]})} \ + ${configs[sets]:+$(helmOptions set ${configs[sets]})} \ + ${configs[sets-nats]:+$(helmOptions set ${configs[sets-nats]})} \ + ${configs[setfiles]:+$(helmOptions set-file ${configs[setfiles]})} \ + ${configs[setfiles-nats]:+$(helmOptions set-file ${configs[setfiles-nats]})} \ + ${configs[dryrun]:+--dry-run --debug} + + kubectl --namespace ${configs[namespace-nats]} wait --for=condition=Available deployment.apps/prometheus-operator \ + --timeout=${configs[pause-kubectl]}s || error "prometheus-operator installation error" + } + + [[ "$(helm list --short ${configs[namespace-nats]}-nats-prometheus)" = "${configs[namespace-nats]}-nats-prometheus" ]] || { + helm install ${configs[home]}/helm/nats-prometheus --name ${configs[namespace-nats]}-nats-prometheus \ + ${configs[namespace-nats]:+--namespace ${configs[namespace-nats]}} \ + ${configs[values]:+$(helmOptions values ${configs[values]})} \ + ${configs[values-nats]:+$(helmOptions values ${configs[values-nats]})} \ + ${configs[presets]:+$(helmOptions set ${configs[presets]})} \ + ${configs[presets-nats]:+$(helmOptions set ${configs[presets-nats]})} \ + ${configs[sets]:+$(helmOptions set ${configs[sets]})} \ + ${configs[sets-nats]:+$(helmOptions set ${configs[sets-nats]})} \ + ${configs[setfiles]:+$(helmOptions set-file ${configs[setfiles]})} \ + ${configs[setfiles-nats]:+$(helmOptions set-file ${configs[setfiles-nats]})} \ + ${configs[dryrun]:+--dry-run --debug} + + # todo: fix block + # kubectl --namespace ${configs[namespace-nats]} wait --for=condition=Available deployment.apps/nats-surveyor-grafana \ + # --timeout=${configs[pause-kubectl]}s || error "nats-prometheus installation error" + } + + [[ "$(helm list --short ${configs[namespace-nats]}-nats-surveyor-grafana)" = "${configs[namespace-nats]}-nats-surveyor-grafana" ]] || { + helm install ${configs[home]}/helm/nats-surveyor-grafana --name ${configs[namespace-nats]}-nats-surveyor-grafana \ + ${configs[namespace-nats]:+--namespace ${configs[namespace-nats]}} \ + ${configs[values]:+$(helmOptions values ${configs[values]})} \ + ${configs[values-nats]:+$(helmOptions values ${configs[values-nats]})} \ + ${configs[presets]:+$(helmOptions set ${configs[presets]})} \ + ${configs[presets-nats]:+$(helmOptions set ${configs[presets-nats]})} \ + ${configs[sets]:+$(helmOptions set ${configs[sets]})} \ + ${configs[sets-nats]:+$(helmOptions set ${configs[sets-nats]})} \ + ${configs[setfiles]:+$(helmOptions set-file ${configs[setfiles]})} \ + ${configs[setfiles-nats]:+$(helmOptions set-file ${configs[setfiles-nats]})} \ + ${configs[dryrun]:+--dry-run --debug} + + # todo: fix block + # kubectl --namespace ${configs[namespace-nats]} wait --for=condition=Ready pod --selector=app=nats-surveyor-grafana \ + # --timeout=${configs[pause-kubectl]}s || error "nats-surveyor-grafana installation error" + } + + [[ "$(helm list --short ${configs[namespace-nats]}-nats-surveyor)" = "${configs[namespace-nats]}-nats-surveyor" ]] || { + helm install ${configs[home]}/helm/nats-surveyor --name ${configs[namespace-nats]}-nats-surveyor \ + ${configs[namespace-nats]:+--namespace ${configs[namespace-nats]}} \ + ${configs[values]:+$(helmOptions values ${configs[values]})} \ + ${configs[values-nats]:+$(helmOptions values ${configs[values-nats]})} \ + ${configs[presets]:+$(helmOptions set ${configs[presets]})} \ + ${configs[presets-nats]:+$(helmOptions set ${configs[presets-nats]})} \ + ${configs[sets]:+$(helmOptions set ${configs[sets]})} \ + ${configs[sets-nats]:+$(helmOptions set ${configs[sets-nats]})} \ + ${configs[setfiles]:+$(helmOptions set-file ${configs[setfiles]})} \ + ${configs[setfiles-nats]:+$(helmOptions set-file ${configs[setfiles-nats]})} \ + ${configs[dryrun]:+--dry-run --debug} + + # kubectl --namespace ${configs[namespace-nats]} wait --for=condition=Ready pod --selector=app=nats-surveyor \ + # --timeout=${configs[pause-kubectl]}s || error "nats-surveyor installation error" + } +} + +installDns() { + check ${FUNCNAME[0]} + + [[ "$(helm list --short ${configs[namespace-dns]}-dns)" = "${configs[namespace-dns]}-dns" ]] || \ + helm install ./helm/dns --name ${configs[namespace-dns]}-dns \ + ${configs[namespace-dns]:+--namespace ${configs[namespace-dns]}} \ + ${configs[values]:+$(helmOptions values ${configs[values]})} \ + ${configs[presets]:+$(helmOptions set ${configs[presets]})} \ + ${configs[sets]:+$(helmOptions set ${configs[sets]})} \ + ${configs[setfiles]:+$(helmOptions set-file ${configs[setfiles]})} \ + ${configs[dryrun]:+--dry-run --debug} +} + +installCurl() { + check ${FUNCNAME[0]} + + [[ "$(helm list --short ${configs[namespace-curl]}-curl)" = "${configs[namespace-curl]}-curl" ]] || \ + helm install ./helm/curl --name ${configs[namespace-curl]}-curl \ + ${configs[namespace-curl]:+--namespace ${configs[namespace-curl]}} \ + ${configs[values]:+$(helmOptions values ${configs[values]})} \ + ${configs[presets]:+$(helmOptions set ${configs[presets]})} \ + ${configs[sets]:+$(helmOptions set ${configs[sets]})} \ + ${configs[setfiles]:+$(helmOptions set-file ${configs[setfiles]})} \ + ${configs[dryrun]:+--dry-run --debug} +} + testUnit() { check ${FUNCNAME[0]} # todo: enable when tests don't block # yarn test - for c in $(find ${configs[home]}/helm -type d -depth 1); do - helm unittest ${configs[home]}/helm/$(basename ${c}) - done; unset c + helm unittest ${configs[home]}/helm/mds } testIntegration() { @@ -585,7 +1098,7 @@ testIntegration() { # todo: provide [ ui | cli ] option # yarn cypress open - yarn cypress run + # yarn cypress run } get() { @@ -595,13 +1108,15 @@ get() { } portForward() { + check ${FUNCNAME[0]} + fa=${apps[${1}]} fns=$(get ${fa} 2) fk=$(get ${fa} 3) fv=$(get ${fa} 4 ${1}) fp=$(get ${fa} 5 -1) - (( $(kubectl -n ${fns} get pod -l ${fk}=${fv} | wc -l) == 2 )) && { + (( $(kubectl -n ${fns} get pod -l ${fk}=${fv} --ignore-not-found=true | wc -l) == 2 )) && { fa=$(kubectl -n ${fns} get pod -l ${fk}=${fv} -o jsonpath='{.items[0].metadata.name}') [ ${fp} -eq "-1" ] && \ @@ -615,8 +1130,6 @@ portForward() { } forward() { - check ${FUNCNAME[0]} - for fa in ${@}; do [ ${apps[${fa}]+_} ] && portForward ${fa} || warn "unknown app: ${fa}" done; unset fa @@ -634,17 +1147,9 @@ unforward() { done; unset ua } -tokenDashboard() { - case "${configs[os]}" in - ${os[osx]}) kubectl -n dashboard describe secret $(kubectl -n dashboard get secret | \ - grep admin-user | awk '{print $1}') | grep ^token | cut -d: -f2 | tr -d '[:space:]' | \ - pbcopy;; - *) kubectl -n dashboard describe secret $(kubectl -n dashboard get secret | \ - grep admin-user | awk '{print $1}') | grep ^token | cut -d: -f2 | tr -d '[:space:]';; - esac -} - getApp() { + check ${FUNCNAME[0]} + ga=${apps[${1}]} gu=$(get ${ga} 1) gns=$(get ${ga} 2) @@ -653,7 +1158,7 @@ getApp() { gp=$(get ${ga} 5 -1) [ ${gp} -eq -1 ] && { - (( $(kubectl -n ${gns} get pod -l ${gk}=${gv} | wc -l) == 2 )) && + (( $(kubectl -n ${gns} get pod -l ${gk}=${gv} --ignore-not-found=true | wc -l) == 2 )) && gp=$(kubectl -n ${gns} get pod -l ${gk}=${gv} \ -o jsonpath='{.items[0].spec.containers[0].ports[0].containerPort}') } @@ -672,74 +1177,192 @@ app() { *) usage "unsupported os: ${configs[os]}";; esac else - usage "unknown application: ${a}" + invoke app ${a} fi done; unset a } cliPostgresql() { check ${FUNCNAME[0]} - pgcli postgres://mdsadmin@localhost:5432/mds + # pgcli postgres://mdsadmin@localhost:5432/mds + kubectl -n ${configs[namespace-mds]} run -it --image=dencold/pgcli --rm=true --attach pgcli \ + postgres://mdsadmin:$(urlEncode ${configs[presets-mds-database-password]})@${configs[namespace-mds]}-postgresql:5432/mds } cliRedis() { check ${FUNCNAME[0]} - redis-cli -u redis://localhost:6379/0 + # redis-cli -u redis://localhost:6379/0 + echo "issue command: redis-cli -u redis://${configs[namespace-mds]}-redis-master:6379/0" + kubectl -n ${configs[namespace-mds]} run -it --image=redis --rm=true --attach redis-cli \ + /bin/bash #redis-cli -u redis://${configs[namespace-mds]}-redis-master:6379/0 } uninstallNamespace() { + check ${FUNCNAME[0]} + for ns in ${@}; do kubectl get namespace ${1} > /dev/null 2>&1 && kubectl delete namespace ${ns} \ ${configs[dryrun]:+--dry-run} done; unset ns } -uninstallBookinfo() { - uninstallNamespace bookinfo-app +uninstallHelm() { + case "${configs[provider],,}" in + aws | gcp) uninstallTiller;; + *);; + esac } uninstallSimulator() { check ${FUNCNAME[0]} - (( $(kubectl get pod -l app=trackgen | wc -l) == 2 )) && - (cd ${configs[simulator]}; helm delete --purge trackgen) - [ -d ${configs[simulator]} ] && /bin/rm -rf ${configs[simulator]} + [[ "$(helm list --short ${configs[namespace-simulator]}-trackgen)" = "${configs[namespace-simulator]}-trackgen" ]] && { + helm del --purge ${configs[namespace-simulator]}-trackgen + + if [ -z "${configs[keep-cache]}" ]; then + [ -d ${configs[simulator]} ] && /bin/rm -rf ${configs[simulator]} + fi + } +} + +uninstallOperations() { + check ${FUNCNAME[0]} + + if [ -z "${configs[keep-cache]}" ]; then + [ -d ${configs[operations]} ] && /bin/rm -rf ${configs[operations]} + fi +} + +uninstallCredentials() { + check ${FUNCNAME[0]} + + # todo: not sure we want to rm local creds repo just yet + # if [ -z "${configs[keep-cache]}" ]; then + # [ -d ${configs[credentials]} ] && /bin/rm -rf ${configs[credentials]} + # fi } uninstallFallbackCertificate() { + check ${FUNCNAME[0]} + kubectl get secret gateway-fallback > /dev/null 2>&1 && kubectl delete secret gateway-fallback } uninstallIngressGatewayCertificate() { + check ${FUNCNAME[0]} + kubectl get secret istio-ingressgateway-certs > /dev/null 2>&1 && kubectl delete secret istio-ingressgateway-certs } uninstallMetricsAdapter() { + check ${FUNCNAME[0]} + helm status kube-metrics-adapter > /dev/null 2>&1 && helm delete --purge kube-metrics-adapter } +uninstallGrafana() { + (( $(helm list --short grafana | wc -l) == 1 )) && { + # todo: unforward + helm delete --purge grafana + } +} + uninstallMds() { check ${FUNCNAME[0]} - helm delete --purge mds ${configs[dryrun]:+--dry-run} - uninstallNamespace ${configs[namespace-mds]} + + [[ "$(helm list --short ${configs[namespace-mds]}-mds${configs[helm-upgrade-release]})" = "${configs[namespace-mds]}-mds${configs[helm-upgrade-release]}" ]] && { + helm delete --purge ${configs[namespace-mds]}-mds${configs[helm-upgrade-release]} ${configs[dryrun]:+--dry-run} + uninstallNamespace ${configs[namespace-mds]} + } +} + +uninstallSurveyor() { + for n in prometheus-operator nats-prometheus nats-surveyor-grafana nats-surveyor; do + [[ "$(helm list --short ${configs[namespace-nats]}-${n})" = "${configs[namespace-nats]}-${n}" ]] && \ + helm delete --purge ${configs[namespace-nats]}-${n} + done; unset n +} + +uninstallNatsbox() { + check ${FUNCNAME[0]} + + [[ "$(helm list --short ${configs[namespace-nats-box]}-nats-box)" = "${configs[namespace-nats-box]}-nats-box" ]] && \ + helm delete --purge ${configs[namespace-nats-box]}-nats-box +} + +uninstallNas() { + check ${FUNCNAME[0]} + + [[ "$(helm list --short ${configs[namespace-nats-account-server]}-nats-account-server)" = "${configs[namespace-nats-account-server]}-nats-account-server" ]] && \ + helm delete --purge ${configs[namespace-nats-account-server]}-nats-account-server + + kubectl --namespace ${configs[namespace-nats-account-server]} delete configmap nats-operator-data +} + +uninstallNatsInit() { + check ${FUNCNAME[0]} + + uninstallNas + + [[ "$(helm list --short ${configs[namespace-nats]}-nats-init)" = "${configs[namespace-nats]}-nats-init" ]] && \ + helm delete --purge ${configs[namespace-nats]}-nats-init + + for c in crd/natsclusters.nats.io crd/natsserviceroles.nats.io; do + kubectl delete ${c} + done; unset c +} + +uninstallNats() { + check ${FUNCNAME[0]} + + # (creds) uninstallNas + + [[ "$(helm list --short ${configs[namespace-nats]}-nats-server)" = "${configs[namespace-nats]}-nats-server" ]] && \ + helm delete --purge ${configs[namespace-nats]}-nats-server + + kubectl delete namespace ${configs[namespace-nats]} + # for n in enpoints/nats-server secret/default-token-* secret/istio.default serviceaccount/default do + # kubectl -n ${configs[namespace-nats]} delete ${n} + # done; unset n +} + +uninstallNsc() { + # todo: rm ~/{.nkeys,nsc} + if [ -z "${configs[keep-cache]}" ]; then + [[ -d ${configs[nats]} ]] && /bin/rm -rf ${configs[nats]} + fi +} + +uninstallDns() { + [[ "$(helm list --short ${configs[namespace-dns]}-dns)" = "${configs[namespace-dns]}-dns" ]] && \ + helm delete --purge ${configs[namespace-dns]}-dns +} + +uninstallCurl() { + [[ "$(helm list --short ${configs[namespace-curl]}-curl)" = "${configs[namespace-curl]}-curl" ]] && \ + helm delete --purge ${configs[namespace-curl]}-curl } uninstallMetrics() { check ${FUNCNAME[0]} + helm delete --purge metrics ${configs[dryrun]:+--dry-run} uninstallNamespace metrics } uninstallLogging() { check ${FUNCNAME[0]} + helm delete --purge logging ${configs[dryrun]:+--dry-run} # todo: ns/util may host more apps then logging #uninstallNamespace ${configs[namespace-logging]} } uninstallKiali() { + check ${FUNCNAME[0]} + unforward kiali helm delete --purge kiali uninstallNamespace kiali @@ -749,30 +1372,32 @@ uninstallKiali() { } uninstallIstio() { + check ${FUNCNAME[0]} + kubectl get namespace istio-system > /dev/null 2>&1 && { check ${FUNCNAME[0]} - helm template ${configs[istio]}/install/kubernetes/helm/istio \ - --name istio \ - --namespace istio-system \ - --values ${configs[istio]}/install/kubernetes/helm/istio/values-istio-demo.yaml | \ - kubectl delete -f - ${configs[dryrun]:+--dry-run} - kubectl delete namespace istio-system ${configs[dryrun]:+--dry-run} - kubectl delete -f ${configs[istio]}/install/kubernetes/helm/istio-init/files \ - ${configs[dryrun]:+--dry-run} > /dev/null + case "${config[provider]}" in + gcp) + helm template install/kubernetes/helm/istio --name istio --namespace istio-system | kubectl delete -f -;; + *) + helm delete --purge istio ${configs[dryrun]:+--dry-run} + helm delete --purge istio-init ${configs[dryrun]:+--dry-run} + kubectl delete namespace istio-system ${configs[dryrun]:+--dry-run} + kubectl delete -f ${configs[istio]}/install/kubernetes/helm/istio-init/files \ + ${configs[dryrun]:+--dry-run} > /dev/null;; + esac + kubectl delete namespace istio-system + unlabelNamespacesIstioInjection } - [ -d ${configs[istio]} ] && /bin/rm -rf ${configs[istio]} -} -uninstallDashboard() { - unforward dashboard - helm delete --purge dashboard - uninstallNamespace dashboard - kubectl delete secrets kubernetes-dashboard-key-holder -n kube-system ${configs[dryrun]:+--dry-run} - kubectl get namespace dashboard > /dev/null 2>&1 && kubectl delete namespace dashboard \ - ${configs[dryrun]:+--dry-run} + if [ -z "${configs[keep-cache]}" ]; then + [[ -d ${configs[istio]} && -z "${configs[dryrun]}" ]] && /bin/rm -rf ${configs[istio]} + fi } uninstallTiller() { + check ${FUNCNAME[0]} + if [ $(kubectl get serviceaccount tiller --namespace kube-system > /dev/null 2>&1; echo $?) == "0" ]; then kubectl delete serviceaccount tiller --namespace kube-system kubectl delete clusterrolebinding tiller-clusterrolebinding @@ -780,18 +1405,48 @@ uninstallTiller() { pause 3 helm init --upgrade fi -} - -#todo: purge -uninstallHelm() { - case "${configs[os]}" in - ${os[osx]}) ;; #brew uninstall kubernetes-helm;; - *) usage "unsupported os: ${configs[os]}";; + case "${configs[provider],,}" in + gcp) + if [ $(kubectl get serviceaccount tiller --namespace kube-system > /dev/null 2>&1; echo $?) == "0" ]; then + kubectl delete serviceaccount tiller --namespace kube-system + kubectl delete clusterrolebinding tiller-clusterrolebinding + helm reset --force + pause 3 + helm init --upgrade + fi;; + aws) ;& + *) + if [ $(kubectl get serviceaccount tiller --namespace kube-system > /dev/null 2>&1; echo $?) == "1" ]; then + cat << EOF | kubectl delete -f - +apiVersion: v1 +kind: ServiceAccount +metadata: + name: tiller + namespace: kube-system +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: tiller +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: cluster-admin +subjects: +- kind: ServiceAccount + name: tiller + namespace: kube-system +EOF + helm reset --force + pause 3 + helm init --upgrade + fi;; esac } getPort() { if [ ${apps[${1}]+_} ]; then + check ${FUNCNAME[0]} ga=${apps[${1}]} gns=$(get ${ga} 2) gk=$(get ${ga} 3) @@ -799,7 +1454,7 @@ getPort() { gp=$(get ${ga} 5 -1) [ ${gp} -eq -1 ] && { - (( $(kubectl -n ${gns} get pod -l ${gk}=${gv} | wc -l) == 2 )) && + (( $(kubectl -n ${gns} get pod -l ${gk}=${gv} --ignore-not-found=true | wc -l) == 2 )) && gp=$(kubectl -n ${gns} get pod -l ${gk}=${gv} \ -o jsonpath='{.items[0].spec.containers[0].ports[0].containerPort}') } @@ -812,7 +1467,7 @@ forwardIfNeeded() { gp=$(getPort ${1}) if [ ${gp} -eq "-1" ]; then - hash install${1^} > /dev/null 2>&1 && invoke install ${1} + [[ "$(exists install${1^})" == "0" ]] && invoke install ${1} pause 3 gp=$(getPort ${1}) fi @@ -824,43 +1479,65 @@ forwardIfNeeded() { unset gp } +localContext() { + [[ "$(lower $(kubectl config current-context))" =~ ^(${configs[local-context]})$ ]] && echo true || echo false +} + +context() { + [ -z ${context+x} ] && [ "$(localContext)" != "true" ] && { + v=yes + [[ "$(prompt "confirm you want to proceed: [${v}/no] ")" != "${v}" ]] && usage "opted to exit" || context=true + unset v + } +} + check() { + nc=("$(basename ${0})" bootstrap build installHelm) + [[ ! " ${nc[@]} " =~ " ${1} " ]] && context + case "${1}" in $(basename ${0})) for rc in ${configs[rc]}; do if [[ -f ${rc} && $(grep -q "\\\*${configs[home]}/bin\\\*" ${rc}; echo $?) != 0 ]]; then - echo "export PATH=${configs[home]}/bin:\${PATH}" >> ${rc} + echo "export PATH=${configs[home]}/bin:\${PATH}" >> ${rc} fi done; unset rc for d in docker kubectl; do - hash ${d} > /dev/null 2>&1 || usage "install: ${d}" - done; unset d;; + [[ "$(exists ${d})" != "0" ]] && usage "install: ${d}" + done; unset d # todo: include cluster connection check where needed if not globally - #kubectl cluster-info > /dev/null 2>&1 || usage "connect to kubernetes cluster" + kubectl cluster-info > /dev/null 2>&1 || usage "connect to kubernetes cluster";; build | testIntegration) - hash yarn > /dev/null 2>&1 || bootstrap;; - installHelm | installBookinfo) ;; - installDashboard | installMetrics | installMds) - kubectl get namespace istio-system > /dev/null 2>&1 || installIstio;& - installLogging | installMetricsAdapter) - hash helm > /dev/null 2>&1 && updateHelmDependencies || installHelm;; - installImages) - [ "${configs[image-repository]}" ] || \ - warn "configure an image-repository; eg ' ... -c:image-repository=[image-repository-url] ...'";; - installIstio | testUnit | \ - uninstallDashboard | uninstallMds | uninstallMetrics | uninstallLogging | uninstallIstio) - hash helm > /dev/null 2>&1 || installHelm;; + [[ "$(exists yarn)" != "0" ]] && bootstrap;; + installCredentials) ;; + installNsc) installCredentials;; + installNas) installNsc;; + installNatsInit) ;; + installNats) ;; # (creds) installNas; installNatsInit;; + # installImages) + # [ "${configs[image-repository]}" ] || \ + # warn "configure an image-repository; eg ' ... -c:image-repository=[image-repository-url] ...'";; + # installNas) installNats;; + installMds) + [[ "$(exists helm)" != "0" ]] && bootstrap + kubectl get namespace istio-system > /dev/null 2>&1 || installIstio + # fixme: test ${configs[helm-environment]} + # (creds) installNsc;; + [ "${configs[helm-environment]}" ] && installOperations + # fixme: better operations pre-check + #[ ! -d ${configs[operations]}/${configs[helm-cluster]} ] && usage "unspecified cluster" + ;; + installMetrics) kubectl get namespace istio-system > /dev/null 2>&1 || installIstio;& + installHelm | installIstio | installLogging | installMetricsAdapter | testUnit | \ + uninstallMds | uninstallMetrics | uninstallLogging | uninstallIstio) + [[ "$(exists helm)" != "0" ]] && bootstrap;; + uninstallNats) ;; # (creds) uninstallNas;; installSimulator) - [ ! "${configs[simulator-repository]}" ] && usage "unspecified simulator repository";; - forward) ;; + [[ ! -d ${configs[simulator]} && ! "${configs[simulator-repository]}" ]] && usage "unspecified simulator repository";; app:*) forwardIfNeeded ${1##*:};; - cliPostgresql) - hash pgcli > /dev/null 2>&1 || bootstrap - forwardIfNeeded mds-postgresql;; - cliRedis) - hash redis-cli > /dev/null 2>&1 || bootstrap - forwardIfNeeded mds-redis;; + cliPostgresql) ;; + cliRedis) ;; esac } @@ -870,6 +1547,12 @@ invoke() { done; unset ia } +invokeAny() { + for ia in ${1}; do + ${ia} || warn "${ia} error: ${ia}" + done; unset ia +} + normalize() { echo ${1} | cut -d ${2:-:} -f 2- | tr ',' ' ' } @@ -879,10 +1562,9 @@ configure() { for c in $(echo ${1} | tr ',' ' '); do k=$(echo ${c} | cut -d '=' -f 1) v=$(echo ${c} | cut -d '=' -f 2-) - [[ "${k}" == *"+" ]] && configs[${k%+}]+="${configs[${k%+}]:+,}${v}" || configs[${k}]="${v}" done; unset c k v - else + elif [[ ! -z "${1}" ]]; then configs[${1}]= fi } @@ -895,14 +1577,15 @@ preset() { is+="${is:+,}presets-mds+=apis.${s}.enabled=false" done; unset s;; local) - is+="${is:+,}presets-mds+=resourcesLimitsCpu=200m" - is+="${is:+,}presets-mds+=resourcesLimitsMemory=200Mi" - is+="${is:+,}presets-mds+=resourcesRequestsCpu=20m" - is+="${is:+,}presets-mds+=resourcesRequestsMemory=32Mi";; - no-events) - for s in $(services); do - is+="${is:+,}presets-mds+=apis.${s}.useEvents=false" - done; unset s;; + is+="${is:+,}presets+=resourcesLimitsCpu=200m" + is+="${is:+,}presets+=resourcesLimitsMemory=200Mi" + is+="${is:+,}presets+=resourcesRequestsCpu=20m" + is+="${is:+,}presets+=resourcesRequestsMemory=32Mi" + is+="${is:+,}presets-nats+=cluster.size=1" + is+="${is:+,}presets-nats+=maximumAge=4h" + is+="${is:+,}presets-nats+=maximumBytes=1GB" + is+="${is:+,}presets-nats+=cluster.size=1" + is+="${is:+,}presets-nats+=clusterSize=1";; no-persistence) is+="${is:+,}sets-mds+=postgresql.persistence.enabled=false" is+="${is:+,}sets-mds+=postgresql.volumePermissions.enabled=true" @@ -911,8 +1594,16 @@ preset() { is+="${is:+,}sets-mds+=redis.master.persistence.enabled=false" is+="${is:+,}sets-mds+=redis.slave.persistence.enabled=false";; minimal) - preset $(normalize "disable,no-events,local,no-persistence") + preset $(normalize "disable,local,no-persistence") is+="${is:+,}sets-mds+=apis.mds-agency.enabled=true";; + data-external) + is+="${is:+,}sets-mds+=postgresql.internal=false" + is+="${is:+,}sets-mds+=postgresql.host=host.docker.internal" + is+="${is:+,}sets-mds+=postgresql.hostReader=host.docker.internal" + is+="${is:+,}sets-mds+=redis.internal=false" + is+="${is:+,}sets-mds+=redis.host=host.docker.internal";; + helm-secrets) + is+="${is:+,}helm-secrets=true";; esac done; unset p @@ -920,14 +1611,16 @@ preset() { unset is } +[[ -z ${configs[profiles]} ]] & preset "$(normalize ${configs[profiles]})" +initialize check $(basename ${0}) while getopts ":w:s:c:p:q-:h-:" opt; do case "${opt}" in -) case "${OPTARG}" in - configure) configure "$(echo ${!OPTIND} | cut -d ':' -f 2-)"; OPTIND=$(( ${OPTIND} + 1 ));; - configure=*) configure "$(echo "${OPTARG#*=}" | cut -d ':' -f 2-)";; + configure) configure "$(echo ${!OPTIND})"; OPTIND=$(( ${OPTIND} + 1 ));; + configure=*) configure "$(echo ${OPTARG#*=})";; preset) preset "$(normalize ${!OPTIND})"; OPTIND=$(( ${OPTIND} + 1 ));; preset=*) preset "$(normalize ${OPTARG#*=})";; workdir) configs[workdir]="${!OPTIND}"; OPTIND=$(( ${OPTIND} + 1 ));; @@ -936,16 +1629,16 @@ while getopts ":w:s:c:p:q-:h-:" opt; do sleep=*) configs[pause]="${OPTARG#*=}";; quiet) confgs[quiet]=true;; help) usage;; - *) [ "${OPTERR}" = 1 ] && [ "${optspec:0:1}" != ":" ] && echo "unknown option --${OPTARG}";; + *) [ "${OPTERR}" = 1 ] && [ "${optspec:0:1}" != ":" ] && error "unknown option --${OPTARG}";; esac;; - c) configure "$(echo ${OPTARG} | cut -d ':' -f 2-)";; + c) configure "$(echo ${OPTARG})";; p) preset "$(normalize ${OPTARG})";; w) configs[workdir]=${OPTARG};; s) configs[pause]=${OPTARG};; q) configs[quiet]=true;; h) usage;; :) usage "option -${OPTARG} requires an argument" 1;; - \?) [ "${OPTERR}" != 1 ] || [ "${optspec:0:1}" = ":" ] && usage "unknown option -${OPTARG}" 1;; + \?) [ "${OPTERR}" != 1 ] || [ "${optspec:0:1}" = ":" ] && error "unknown option -${OPTARG}" 1;; esac done @@ -962,14 +1655,14 @@ for arg in "${@:$OPTIND}"; do install:namespace=*) installNamespace "$(normalize ${arg} =)";; install) arg="${configs[install]}";& install:*) invoke install "$(normalize ${arg})";; + cycle) s=$(services); arg="${s// /,},${p// /,}"; unset s p;& + cycle:*) cycle "$(normalize ${arg})";; test) arg="${configs[test]}";& test:*) invoke test "$(normalize ${arg})";; forward) arg="${configs[forward]}";& forward:*) forward "$(normalize ${arg})";; unforward) unforward;; unforward:*) unforward "$(normalize ${arg})";; - token) arg="${configs[token]}";& - token:*) invoke token "$(normalize ${arg})";; open) usage "specify an application(s) to open";; open:*) app "$(normalize ${arg})";; cli:*) invoke cli "$(normalize ${arg})";; @@ -982,6 +1675,6 @@ for arg in "${@:$OPTIND}"; do invoke install "$(normalize ${arg})";; home) echo ${configs[home]};; completion) cat ${configs[home]}/bin/mdsctl-completion.bash;; - *) usage "unknown command: ${arg}" + *) invokeAny "$(normalize ${arg})";; #error "unknown command: ${arg}" esac done; unset arg diff --git a/container-images/env-inject/index.ts b/container-images/env-inject/index.ts deleted file mode 100644 index 9e9bb69a9..000000000 --- a/container-images/env-inject/index.ts +++ /dev/null @@ -1,31 +0,0 @@ -/* - Copyright 2019 City of Los Angeles. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - */ - -// These global variables will be set by webpack -declare const NPM_PACKAGE_NAME: string -declare const NPM_PACKAGE_VERSION: string -declare const NPM_PACKAGE_GIT_BRANCH: string -declare const NPM_PACKAGE_GIT_COMMIT: string -declare const NPM_PACKAGE_BUILD_DATE: string - -export const env = () => - Object.assign(process.env, { - npm_package_name: NPM_PACKAGE_NAME, - npm_package_version: NPM_PACKAGE_VERSION, - npm_package_git_branch: NPM_PACKAGE_GIT_BRANCH, - npm_package_git_commit: NPM_PACKAGE_GIT_COMMIT, - npm_package_build_date: NPM_PACKAGE_BUILD_DATE - }) diff --git a/container-images/env-inject/package.json b/container-images/env-inject/package.json deleted file mode 100644 index 9b1dad9ec..000000000 --- a/container-images/env-inject/package.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "name": "@container-images/env-inject", - "version": "0.1.17", - "description": "Set process env variables.", - "main": "dist/index.js", - "types": "dist/index.d.ts", - "files": [ - "dist/" - ], - "scripts": { - "build": "tsc --build tsconfig.build.json", - "test": "yarn test:eslint && yarn test:unit", - "test:eslint": "eslint --fix --ignore-path ../../.gitignore '**/*.ts'", - "test:unit": "exit 0" - }, - "keywords": [ - "mds" - ], - "author": "City of Los Angeles", - "license": "Apache-2.0" -} diff --git a/container-images/mds-agency/Dockerfile b/container-images/mds-agency/Dockerfile index be4ee0996..ee966ebdd 100644 --- a/container-images/mds-agency/Dockerfile +++ b/container-images/mds-agency/Dockerfile @@ -1,6 +1,6 @@ -FROM node:10.16.3-alpine +FROM node:14.2.0-alpine -RUN apk add --no-cache tini postgresql-client +RUN apk add --no-cache tini RUN mkdir /mds @@ -8,4 +8,4 @@ COPY dist/* /mds/ WORKDIR /mds -ENTRYPOINT ["/sbin/tini", "node", "--no-deprecation", "index.js"] +ENTRYPOINT ["/sbin/tini", "node", "--no-deprecation", "server.js"] diff --git a/container-images/mds-agency/package.json b/container-images/mds-agency/package.json index a10a5aa07..f83c5d8ff 100644 --- a/container-images/mds-agency/package.json +++ b/container-images/mds-agency/package.json @@ -1,28 +1,25 @@ { "name": "@container-images/mds-agency", - "version": "0.1.18", + "version": "0.1.26", "description": "Docker Image for MDS Agency API", + "private": true, "main": "dist/index.js", "files": [ "dist/" ], "scripts": { - "build": "tsc --build tsconfig.build.json", - "bundle": "yarn build && webpack --mode=production --env.npm_package_name=${npm_package_name} --env.npm_package_version=${npm_package_version}", - "image": "yarn bundle && ../../bin/mdsctl -c:image-name=mds-agency:${npm_package_version} buildImage", - "start": "yarn watch server", - "test": "yarn test:eslint && yarn test:unit", - "test:eslint": "eslint --fix --ignore-path ../../.gitignore '**/*.ts'", - "test:unit": "exit 0" + "bundle": "webpack --mode=production --env.npm_package_name=${npm_package_name} --env.npm_package_version=${npm_package_version}", + "image": "yarn bundle && ../../bin/mdsctl -c image-name=mds-agency:${npm_package_version} buildImage", + "test": "yarn test:eslint", + "test:eslint": "eslint --ignore-path ../../.gitignore '**/*.ts'" }, "keywords": [ "mds" ], "author": "City of Los Angeles", + "license": "Apache-2.0", "dependencies": { - "@container-images/env-inject": "0.1.17", - "@mds-core/mds-agency": "0.0.20", - "@mds-core/mds-api-server": "0.1.18" - }, - "license": "Apache-2.0" + "@mds-core/mds-agency": "0.0.28", + "@mds-core/mds-webpack-config": "0.1.0" + } } diff --git a/container-images/mds-agency/tsconfig.build.json b/container-images/mds-agency/tsconfig.build.json deleted file mode 100644 index 49d6c754c..000000000 --- a/container-images/mds-agency/tsconfig.build.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "extends": "../../tsconfig.build.json", - "compilerOptions": { - "outDir": "dist" - }, - "references": [ - { "path": "../../container-images/env-inject/tsconfig.build.json" }, - { "path": "../../packages/mds-agency/tsconfig.build.json" }, - { "path": "../../packages/mds-api-server/tsconfig.build.json" } - ] -} diff --git a/container-images/mds-agency/tsconfig.eslint.json b/container-images/mds-agency/tsconfig.eslint.json new file mode 100644 index 000000000..2a400942a --- /dev/null +++ b/container-images/mds-agency/tsconfig.eslint.json @@ -0,0 +1 @@ +{ "extends": "../../tsconfig.eslint.json" } diff --git a/container-images/mds-agency/webpack.config.js b/container-images/mds-agency/webpack.config.js deleted file mode 100644 index a2c4aff85..000000000 --- a/container-images/mds-agency/webpack.config.js +++ /dev/null @@ -1,6 +0,0 @@ -module.exports = (env, argv) => require('../webpack.config')({ - env, - argv, - dirname: require('path').resolve(__dirname), - bundles: ['index'] -}) diff --git a/container-images/mds-agency/webpack.config.ts b/container-images/mds-agency/webpack.config.ts new file mode 100644 index 000000000..f00f079f2 --- /dev/null +++ b/container-images/mds-agency/webpack.config.ts @@ -0,0 +1,3 @@ +import webpack from '@mds-core/mds-webpack-config' + +export default webpack.Bundle('../../packages/mds-agency/server').UsingDefaultConfig() diff --git a/container-images/mds-audit/Dockerfile b/container-images/mds-audit/Dockerfile index f722e28a3..5dca861d2 100644 --- a/container-images/mds-audit/Dockerfile +++ b/container-images/mds-audit/Dockerfile @@ -1,11 +1,14 @@ -FROM node:10.16.3-alpine +FROM node:14.2.0-alpine RUN apk add --no-cache tini RUN mkdir /mds +#Compile Vips and Sharp +RUN npm install sharp + COPY dist/* /mds/ WORKDIR /mds -ENTRYPOINT ["/sbin/tini", "node", "--no-deprecation", "index.js"] +ENTRYPOINT ["/sbin/tini", "node", "--no-deprecation", "server.js"] diff --git a/container-images/mds-audit/package.json b/container-images/mds-audit/package.json index 88c7b5d54..da4d77062 100644 --- a/container-images/mds-audit/package.json +++ b/container-images/mds-audit/package.json @@ -1,28 +1,25 @@ { "name": "@container-images/mds-audit", - "version": "0.1.19", + "version": "0.1.27", "description": "Docker Image for MDS Audit API", + "private": true, "main": "dist/index.js", "files": [ "dist/" ], "scripts": { - "build": "tsc --build tsconfig.build.json", - "bundle": "yarn build && webpack --mode=production --env.npm_package_name=${npm_package_name} --env.npm_package_version=${npm_package_version}", - "image": "yarn bundle && ../../bin/mdsctl -c:image-name=mds-audit:${npm_package_version} buildImage", - "start": "yarn watch server", - "test": "yarn test:eslint && yarn test:unit", - "test:eslint": "eslint --fix --ignore-path ../../.gitignore '**/*.ts'", - "test:unit": "exit 0" + "bundle": "webpack --mode=production --env.npm_package_name=${npm_package_name} --env.npm_package_version=${npm_package_version}", + "image": "yarn bundle && ../../bin/mdsctl -c image-name=mds-audit:${npm_package_version} buildImage", + "test": "yarn test:eslint", + "test:eslint": "eslint --ignore-path ../../.gitignore '**/*.ts'" }, "keywords": [ "mds" ], "author": "City of Los Angeles", + "license": "Apache-2.0", "dependencies": { - "@container-images/env-inject": "0.1.17", - "@mds-core/mds-api-server": "0.1.18", - "@mds-core/mds-audit": "0.1.30" - }, - "license": "Apache-2.0" + "@mds-core/mds-audit": "0.1.38", + "@mds-core/mds-webpack-config": "0.1.0" + } } diff --git a/container-images/mds-audit/tsconfig.build.json b/container-images/mds-audit/tsconfig.build.json deleted file mode 100644 index aeecab8d3..000000000 --- a/container-images/mds-audit/tsconfig.build.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "extends": "../../tsconfig.build.json", - "compilerOptions": { - "outDir": "dist" - }, - "references": [ - { "path": "../../container-images/env-inject/tsconfig.build.json" }, - { "path": "../../packages/mds-audit/tsconfig.build.json" }, - { "path": "../../packages/mds-api-server/tsconfig.build.json" } - ] -} diff --git a/container-images/mds-audit/tsconfig.eslint.json b/container-images/mds-audit/tsconfig.eslint.json new file mode 100644 index 000000000..2a400942a --- /dev/null +++ b/container-images/mds-audit/tsconfig.eslint.json @@ -0,0 +1 @@ +{ "extends": "../../tsconfig.eslint.json" } diff --git a/container-images/mds-audit/webpack.config.js b/container-images/mds-audit/webpack.config.js deleted file mode 100644 index a2c4aff85..000000000 --- a/container-images/mds-audit/webpack.config.js +++ /dev/null @@ -1,6 +0,0 @@ -module.exports = (env, argv) => require('../webpack.config')({ - env, - argv, - dirname: require('path').resolve(__dirname), - bundles: ['index'] -}) diff --git a/container-images/mds-audit/webpack.config.ts b/container-images/mds-audit/webpack.config.ts new file mode 100644 index 000000000..8df16cc06 --- /dev/null +++ b/container-images/mds-audit/webpack.config.ts @@ -0,0 +1,3 @@ +import webpack from '@mds-core/mds-webpack-config' + +export default webpack.Bundle('../../packages/mds-audit/server').UsingDefaultConfig() diff --git a/container-images/mds-compliance/Dockerfile b/container-images/mds-compliance/Dockerfile index f722e28a3..ee966ebdd 100644 --- a/container-images/mds-compliance/Dockerfile +++ b/container-images/mds-compliance/Dockerfile @@ -1,4 +1,4 @@ -FROM node:10.16.3-alpine +FROM node:14.2.0-alpine RUN apk add --no-cache tini @@ -8,4 +8,4 @@ COPY dist/* /mds/ WORKDIR /mds -ENTRYPOINT ["/sbin/tini", "node", "--no-deprecation", "index.js"] +ENTRYPOINT ["/sbin/tini", "node", "--no-deprecation", "server.js"] diff --git a/container-images/mds-compliance/index.ts b/container-images/mds-compliance/index.ts deleted file mode 100644 index 102634700..000000000 --- a/container-images/mds-compliance/index.ts +++ /dev/null @@ -1,28 +0,0 @@ -/* - Copyright 2019 City of Los Angeles. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - */ - -// Express local -import { ApiServer } from '@mds-core/mds-api-server' -import { api } from '@mds-core/mds-compliance' -import { env } from '@container-images/env-inject' - -const { npm_package_name, npm_package_version, npm_package_git_commit, PORT = 4004 } = env() - -ApiServer(api).listen(PORT, () => - /* eslint-reason avoids import of logger */ - /* eslint-disable-next-line no-console */ - console.log(`${npm_package_name} v${npm_package_version} (${npm_package_git_commit}) running on port ${PORT}`) -) diff --git a/container-images/mds-compliance/package.json b/container-images/mds-compliance/package.json index bb72e5b79..741088a51 100644 --- a/container-images/mds-compliance/package.json +++ b/container-images/mds-compliance/package.json @@ -1,28 +1,25 @@ { "name": "@container-images/mds-compliance", - "version": "0.1.18", + "version": "0.1.26", "description": "Docker Image for MDS Compliance API", + "private": true, "main": "dist/index.js", "files": [ "dist/" ], "scripts": { - "build": "tsc --build tsconfig.build.json", - "bundle": "yarn build && webpack --mode=production --env.npm_package_name=${npm_package_name} --env.npm_package_version=${npm_package_version}", - "image": "yarn bundle && ../../bin/mdsctl -c:image-name=mds-compliance:${npm_package_version} buildImage", - "start": "yarn watch server", - "test": "yarn test:eslint && yarn test:unit", - "test:eslint": "eslint --fix --ignore-path ../../.gitignore '**/*.ts'", - "test:unit": "exit 0" + "bundle": "webpack --mode=production --env.npm_package_name=${npm_package_name} --env.npm_package_version=${npm_package_version}", + "image": "yarn bundle && ../../bin/mdsctl -c image-name=mds-compliance:${npm_package_version} buildImage", + "test": "yarn test:eslint", + "test:eslint": "eslint --ignore-path ../../.gitignore '**/*.ts'" }, "keywords": [ "mds" ], "author": "City of Los Angeles", + "license": "Apache-2.0", "dependencies": { - "@container-images/env-inject": "0.1.17", - "@mds-core/mds-api-server": "0.1.18", - "@mds-core/mds-compliance": "0.1.19" - }, - "license": "Apache-2.0" + "@mds-core/mds-compliance": "0.1.27", + "@mds-core/mds-webpack-config": "0.1.0" + } } diff --git a/container-images/mds-compliance/tsconfig.build.json b/container-images/mds-compliance/tsconfig.build.json deleted file mode 100644 index 20053634b..000000000 --- a/container-images/mds-compliance/tsconfig.build.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "extends": "../../tsconfig.build.json", - "compilerOptions": { - "outDir": "dist" - }, - "references": [ - { "path": "../../container-images/env-inject/tsconfig.build.json" }, - { "path": "../../packages/mds-compliance/tsconfig.build.json" }, - { "path": "../../packages/mds-api-server/tsconfig.build.json" } - ] -} diff --git a/container-images/mds-compliance/tsconfig.eslint.json b/container-images/mds-compliance/tsconfig.eslint.json new file mode 100644 index 000000000..2a400942a --- /dev/null +++ b/container-images/mds-compliance/tsconfig.eslint.json @@ -0,0 +1 @@ +{ "extends": "../../tsconfig.eslint.json" } diff --git a/container-images/mds-compliance/webpack.config.js b/container-images/mds-compliance/webpack.config.js deleted file mode 100644 index a2c4aff85..000000000 --- a/container-images/mds-compliance/webpack.config.js +++ /dev/null @@ -1,6 +0,0 @@ -module.exports = (env, argv) => require('../webpack.config')({ - env, - argv, - dirname: require('path').resolve(__dirname), - bundles: ['index'] -}) diff --git a/container-images/mds-compliance/webpack.config.ts b/container-images/mds-compliance/webpack.config.ts new file mode 100644 index 000000000..b7406959e --- /dev/null +++ b/container-images/mds-compliance/webpack.config.ts @@ -0,0 +1,3 @@ +import webpack from '@mds-core/mds-webpack-config' + +export default webpack.Bundle('../../packages/mds-compliance/server').UsingDefaultConfig() diff --git a/container-images/mds-daily/Dockerfile b/container-images/mds-daily/Dockerfile index f722e28a3..ee966ebdd 100644 --- a/container-images/mds-daily/Dockerfile +++ b/container-images/mds-daily/Dockerfile @@ -1,4 +1,4 @@ -FROM node:10.16.3-alpine +FROM node:14.2.0-alpine RUN apk add --no-cache tini @@ -8,4 +8,4 @@ COPY dist/* /mds/ WORKDIR /mds -ENTRYPOINT ["/sbin/tini", "node", "--no-deprecation", "index.js"] +ENTRYPOINT ["/sbin/tini", "node", "--no-deprecation", "server.js"] diff --git a/container-images/mds-daily/package.json b/container-images/mds-daily/package.json index 05d9165ae..a7e825c5f 100644 --- a/container-images/mds-daily/package.json +++ b/container-images/mds-daily/package.json @@ -1,28 +1,25 @@ { "name": "@container-images/mds-daily", - "version": "0.1.18", + "version": "0.1.26", "description": "MDS daily-reports prototype", + "private": true, "main": "dist/index.js", "files": [ "dist/" ], "scripts": { - "build": "tsc --build tsconfig.build.json", - "bundle": "yarn build && webpack --mode=production --env.npm_package_name=${npm_package_name} --env.npm_package_version=${npm_package_version}", - "image": "yarn bundle && ../../bin/mdsctl -c:image-name=mds-daily:${npm_package_version} buildImage", - "start": "yarn watch server", - "test": "yarn test:eslint && yarn test:unit", - "test:eslint": "eslint --fix --ignore-path ../../.gitignore '**/*.ts'", - "test:unit": "exit 0" + "bundle": "webpack --mode=production --env.npm_package_name=${npm_package_name} --env.npm_package_version=${npm_package_version}", + "image": "yarn bundle && ../../bin/mdsctl -c image-name=mds-daily:${npm_package_version} buildImage", + "test": "yarn test:eslint", + "test:eslint": "eslint --ignore-path ../../.gitignore '**/*.ts'" }, "keywords": [ "mds" ], "author": "City of Los Angeles", + "license": "Apache-2.0", "dependencies": { - "@container-images/env-inject": "0.1.17", - "@mds-core/mds-api-server": "0.1.18", - "@mds-core/mds-daily": "0.0.19" - }, - "license": "Apache-2.0" + "@mds-core/mds-daily": "0.0.27", + "@mds-core/mds-webpack-config": "0.1.0" + } } diff --git a/container-images/mds-daily/tsconfig.build.json b/container-images/mds-daily/tsconfig.build.json deleted file mode 100644 index 27881d42c..000000000 --- a/container-images/mds-daily/tsconfig.build.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "extends": "../../tsconfig.build.json", - "compilerOptions": { - "outDir": "dist" - }, - "references": [ - { "path": "../../container-images/env-inject/tsconfig.build.json" }, - { "path": "../../packages/mds-daily/tsconfig.build.json" }, - { "path": "../../packages/mds-api-server/tsconfig.build.json" } - ] -} diff --git a/container-images/mds-daily/tsconfig.eslint.json b/container-images/mds-daily/tsconfig.eslint.json new file mode 100644 index 000000000..2a400942a --- /dev/null +++ b/container-images/mds-daily/tsconfig.eslint.json @@ -0,0 +1 @@ +{ "extends": "../../tsconfig.eslint.json" } diff --git a/container-images/mds-daily/webpack.config.js b/container-images/mds-daily/webpack.config.js deleted file mode 100644 index a2c4aff85..000000000 --- a/container-images/mds-daily/webpack.config.js +++ /dev/null @@ -1,6 +0,0 @@ -module.exports = (env, argv) => require('../webpack.config')({ - env, - argv, - dirname: require('path').resolve(__dirname), - bundles: ['index'] -}) diff --git a/container-images/mds-daily/webpack.config.ts b/container-images/mds-daily/webpack.config.ts new file mode 100644 index 000000000..216fe57cc --- /dev/null +++ b/container-images/mds-daily/webpack.config.ts @@ -0,0 +1,3 @@ +import webpack from '@mds-core/mds-webpack-config' + +export default webpack.Bundle('../../packages/mds-daily/server').UsingDefaultConfig() diff --git a/container-images/mds-geography-author/Dockerfile b/container-images/mds-geography-author/Dockerfile new file mode 100644 index 000000000..ee966ebdd --- /dev/null +++ b/container-images/mds-geography-author/Dockerfile @@ -0,0 +1,11 @@ +FROM node:14.2.0-alpine + +RUN apk add --no-cache tini + +RUN mkdir /mds + +COPY dist/* /mds/ + +WORKDIR /mds + +ENTRYPOINT ["/sbin/tini", "node", "--no-deprecation", "server.js"] diff --git a/container-images/mds-geography-author/package.json b/container-images/mds-geography-author/package.json new file mode 100644 index 000000000..1c8863534 --- /dev/null +++ b/container-images/mds-geography-author/package.json @@ -0,0 +1,25 @@ +{ + "name": "@container-images/mds-geography-author", + "version": "0.0.1", + "description": "Docker Image for MDS Geography API", + "private": true, + "main": "dist/index.js", + "files": [ + "dist/" + ], + "scripts": { + "bundle": "webpack --mode=production --env.npm_package_name=${npm_package_name} --env.npm_package_version=${npm_package_version}", + "image": "yarn bundle && ../../bin/mdsctl -c image-name=mds-geography-author:${npm_package_version} buildImage", + "test": "yarn test:eslint", + "test:eslint": "eslint --ignore-path ../../.gitignore '**/*.ts'" + }, + "keywords": [ + "mds" + ], + "author": "City of Los Angeles", + "license": "Apache-2.0", + "dependencies": { + "@mds-core/mds-geography-author": "0.0.1", + "@mds-core/mds-webpack-config": "0.1.0" + } +} diff --git a/container-images/mds-geography-author/tsconfig.eslint.json b/container-images/mds-geography-author/tsconfig.eslint.json new file mode 100644 index 000000000..2a400942a --- /dev/null +++ b/container-images/mds-geography-author/tsconfig.eslint.json @@ -0,0 +1 @@ +{ "extends": "../../tsconfig.eslint.json" } diff --git a/container-images/mds-geography-author/webpack.config.ts b/container-images/mds-geography-author/webpack.config.ts new file mode 100644 index 000000000..2d0ba7369 --- /dev/null +++ b/container-images/mds-geography-author/webpack.config.ts @@ -0,0 +1,3 @@ +import webpack from '@mds-core/mds-webpack-config' + +export default webpack.Bundle('../../packages/mds-geography-author/server').UsingDefaultConfig() diff --git a/container-images/mds-geography/Dockerfile b/container-images/mds-geography/Dockerfile new file mode 100644 index 000000000..ee966ebdd --- /dev/null +++ b/container-images/mds-geography/Dockerfile @@ -0,0 +1,11 @@ +FROM node:14.2.0-alpine + +RUN apk add --no-cache tini + +RUN mkdir /mds + +COPY dist/* /mds/ + +WORKDIR /mds + +ENTRYPOINT ["/sbin/tini", "node", "--no-deprecation", "server.js"] diff --git a/container-images/mds-geography/package.json b/container-images/mds-geography/package.json new file mode 100644 index 000000000..bd47c6974 --- /dev/null +++ b/container-images/mds-geography/package.json @@ -0,0 +1,25 @@ +{ + "name": "@container-images/mds-geography", + "version": "0.0.1", + "description": "Docker Image for MDS Geography API", + "private": true, + "main": "dist/index.js", + "files": [ + "dist/" + ], + "scripts": { + "bundle": "webpack --mode=production --env.npm_package_name=${npm_package_name} --env.npm_package_version=${npm_package_version}", + "image": "yarn bundle && ../../bin/mdsctl -c image-name=mds-geography:${npm_package_version} buildImage", + "test": "yarn test:eslint", + "test:eslint": "eslint --ignore-path ../../.gitignore '**/*.ts'" + }, + "keywords": [ + "mds" + ], + "author": "City of Los Angeles", + "license": "Apache-2.0", + "dependencies": { + "@mds-core/mds-geography": "0.0.1", + "@mds-core/mds-webpack-config": "0.1.0" + } +} diff --git a/container-images/mds-geography/tsconfig.eslint.json b/container-images/mds-geography/tsconfig.eslint.json new file mode 100644 index 000000000..2a400942a --- /dev/null +++ b/container-images/mds-geography/tsconfig.eslint.json @@ -0,0 +1 @@ +{ "extends": "../../tsconfig.eslint.json" } diff --git a/container-images/mds-geography/webpack.config.ts b/container-images/mds-geography/webpack.config.ts new file mode 100644 index 000000000..2d8a6ae20 --- /dev/null +++ b/container-images/mds-geography/webpack.config.ts @@ -0,0 +1,3 @@ +import webpack from '@mds-core/mds-webpack-config' + +export default webpack.Bundle('../../packages/mds-geography/server').UsingDefaultConfig() diff --git a/container-images/mds-jurisdiction-service/Dockerfile b/container-images/mds-jurisdiction-service/Dockerfile new file mode 100644 index 000000000..ee966ebdd --- /dev/null +++ b/container-images/mds-jurisdiction-service/Dockerfile @@ -0,0 +1,11 @@ +FROM node:14.2.0-alpine + +RUN apk add --no-cache tini + +RUN mkdir /mds + +COPY dist/* /mds/ + +WORKDIR /mds + +ENTRYPOINT ["/sbin/tini", "node", "--no-deprecation", "server.js"] diff --git a/container-images/mds-jurisdiction-service/package.json b/container-images/mds-jurisdiction-service/package.json new file mode 100644 index 000000000..e20b0b37d --- /dev/null +++ b/container-images/mds-jurisdiction-service/package.json @@ -0,0 +1,25 @@ +{ + "name": "@container-images/mds-jurisdiction-service", + "version": "0.0.1", + "description": "Docker Image for MDS Jurisdiction Service", + "private": true, + "main": "dist/index.js", + "files": [ + "dist/" + ], + "scripts": { + "bundle": "webpack --mode=production --env.npm_package_name=${npm_package_name} --env.npm_package_version=${npm_package_version}", + "image": "yarn bundle && ../../bin/mdsctl -c image-name=mds-jurisdiction-service:${npm_package_version} buildImage", + "test": "yarn test:eslint", + "test:eslint": "eslint --ignore-path ../../.gitignore '**/*.ts'" + }, + "keywords": [ + "mds" + ], + "author": "City of Los Angeles", + "license": "Apache-2.0", + "dependencies": { + "@mds-core/mds-jurisdiction-service": "0.1.0", + "@mds-core/mds-webpack-config": "0.1.0" + } +} diff --git a/container-images/mds-jurisdiction-service/tsconfig.eslint.json b/container-images/mds-jurisdiction-service/tsconfig.eslint.json new file mode 100644 index 000000000..2a400942a --- /dev/null +++ b/container-images/mds-jurisdiction-service/tsconfig.eslint.json @@ -0,0 +1 @@ +{ "extends": "../../tsconfig.eslint.json" } diff --git a/container-images/mds-jurisdiction-service/webpack.config.ts b/container-images/mds-jurisdiction-service/webpack.config.ts new file mode 100644 index 000000000..600fc2277 --- /dev/null +++ b/container-images/mds-jurisdiction-service/webpack.config.ts @@ -0,0 +1,3 @@ +import webpack from '@mds-core/mds-webpack-config' + +export default webpack.Bundle('../../packages/mds-jurisdiction-service/server').UsingDefaultConfig() diff --git a/container-images/mds-jurisdiction/Dockerfile b/container-images/mds-jurisdiction/Dockerfile new file mode 100644 index 000000000..ee966ebdd --- /dev/null +++ b/container-images/mds-jurisdiction/Dockerfile @@ -0,0 +1,11 @@ +FROM node:14.2.0-alpine + +RUN apk add --no-cache tini + +RUN mkdir /mds + +COPY dist/* /mds/ + +WORKDIR /mds + +ENTRYPOINT ["/sbin/tini", "node", "--no-deprecation", "server.js"] diff --git a/container-images/mds-jurisdiction/package.json b/container-images/mds-jurisdiction/package.json new file mode 100644 index 000000000..21ee416c3 --- /dev/null +++ b/container-images/mds-jurisdiction/package.json @@ -0,0 +1,25 @@ +{ + "name": "@container-images/mds-jurisdiction", + "version": "0.0.1", + "description": "Docker Image for MDS Jurisdiction API", + "private": true, + "main": "dist/index.js", + "files": [ + "dist/" + ], + "scripts": { + "bundle": "webpack --mode=production --env.npm_package_name=${npm_package_name} --env.npm_package_version=${npm_package_version}", + "image": "yarn bundle && ../../bin/mdsctl -c image-name=mds-jurisdiction:${npm_package_version} buildImage", + "test": "yarn test:eslint", + "test:eslint": "eslint --ignore-path ../../.gitignore '**/*.ts'" + }, + "keywords": [ + "mds" + ], + "author": "City of Los Angeles", + "license": "Apache-2.0", + "dependencies": { + "@mds-core/mds-jurisdiction": "0.1.0", + "@mds-core/mds-webpack-config": "0.1.0" + } +} diff --git a/container-images/mds-jurisdiction/tsconfig.eslint.json b/container-images/mds-jurisdiction/tsconfig.eslint.json new file mode 100644 index 000000000..2a400942a --- /dev/null +++ b/container-images/mds-jurisdiction/tsconfig.eslint.json @@ -0,0 +1 @@ +{ "extends": "../../tsconfig.eslint.json" } diff --git a/container-images/mds-jurisdiction/webpack.config.ts b/container-images/mds-jurisdiction/webpack.config.ts new file mode 100644 index 000000000..5a04c972a --- /dev/null +++ b/container-images/mds-jurisdiction/webpack.config.ts @@ -0,0 +1,3 @@ +import webpack from '@mds-core/mds-webpack-config' + +export default webpack.Bundle('../../packages/mds-jurisdiction/server').UsingDefaultConfig() diff --git a/container-images/mds-metrics-sheet/Dockerfile b/container-images/mds-metrics-sheet/Dockerfile index d4cf7e4df..f352d01bd 100644 --- a/container-images/mds-metrics-sheet/Dockerfile +++ b/container-images/mds-metrics-sheet/Dockerfile @@ -1,4 +1,4 @@ -FROM node:10.16.3-alpine +FROM node:14.2.0-alpine RUN apk add --no-cache tini diff --git a/container-images/mds-metrics-sheet/package.json b/container-images/mds-metrics-sheet/package.json index 49c51e7e3..564281b3c 100644 --- a/container-images/mds-metrics-sheet/package.json +++ b/container-images/mds-metrics-sheet/package.json @@ -1,25 +1,25 @@ { "name": "@container-images/mds-metrics-sheet", - "version": "0.1.19", + "version": "0.1.27", "description": "Docker Image for MDS Metrics Sheet", + "private": true, "main": "dist/index.js", "files": [ "dist/" ], "scripts": { - "build": "tsc --build tsconfig.build.json", - "bundle": "yarn build && webpack --mode=production --env.npm_package_name=${npm_package_name} --env.npm_package_version=${npm_package_version}", - "image": "yarn bundle && ../../bin/mdsctl -c:image-name=mds-metrics-sheet:${npm_package_version} buildImage", - "test": "yarn test:eslint && yarn test:unit", - "test:eslint": "eslint --fix --ignore-path ../../.gitignore '**/*.ts'", - "test:unit": "exit 0" + "bundle": "webpack --mode=production --env.npm_package_name=${npm_package_name} --env.npm_package_version=${npm_package_version}", + "image": "yarn bundle && ../../bin/mdsctl -c image-name=mds-metrics-sheet:${npm_package_version} buildImage", + "test": "yarn test:eslint", + "test:eslint": "eslint --ignore-path ../../.gitignore '**/*.ts'" }, "keywords": [ "mds" ], "author": "City of Los Angeles", + "license": "Apache-2.0", "dependencies": { - "@mds-core/mds-metrics-sheet": "0.0.19" - }, - "license": "Apache-2.0" + "@mds-core/mds-metrics-sheet": "0.0.27", + "@mds-core/mds-webpack-config": "0.1.0" + } } diff --git a/container-images/mds-metrics-sheet/tsconfig.build.json b/container-images/mds-metrics-sheet/tsconfig.build.json deleted file mode 100644 index 0619bae7f..000000000 --- a/container-images/mds-metrics-sheet/tsconfig.build.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "extends": "../../tsconfig.build.json", - "compilerOptions": { - "outDir": "dist" - }, - "references": [{ "path": "../../packages/mds-metrics-sheet/tsconfig.build.json" }] -} diff --git a/container-images/mds-metrics-sheet/tsconfig.eslint.json b/container-images/mds-metrics-sheet/tsconfig.eslint.json new file mode 100644 index 000000000..2a400942a --- /dev/null +++ b/container-images/mds-metrics-sheet/tsconfig.eslint.json @@ -0,0 +1 @@ +{ "extends": "../../tsconfig.eslint.json" } diff --git a/container-images/mds-metrics-sheet/webpack.config.js b/container-images/mds-metrics-sheet/webpack.config.js deleted file mode 100644 index 04d18abd0..000000000 --- a/container-images/mds-metrics-sheet/webpack.config.js +++ /dev/null @@ -1,6 +0,0 @@ -module.exports = (env, argv) => require('../webpack.config')({ - env, - argv, - dirname: require('path').resolve(__dirname), - bundles: ['metrics-log', 'vehicle-counts'] -}) diff --git a/container-images/mds-metrics-sheet/webpack.config.ts b/container-images/mds-metrics-sheet/webpack.config.ts new file mode 100644 index 000000000..1676e3e28 --- /dev/null +++ b/container-images/mds-metrics-sheet/webpack.config.ts @@ -0,0 +1,3 @@ +import webpack from '@mds-core/mds-webpack-config' + +export default webpack.Bundle('./metrics-log').UsingDefaultConfig() diff --git a/container-images/mds-native/Dockerfile b/container-images/mds-native/Dockerfile deleted file mode 100644 index f722e28a3..000000000 --- a/container-images/mds-native/Dockerfile +++ /dev/null @@ -1,11 +0,0 @@ -FROM node:10.16.3-alpine - -RUN apk add --no-cache tini - -RUN mkdir /mds - -COPY dist/* /mds/ - -WORKDIR /mds - -ENTRYPOINT ["/sbin/tini", "node", "--no-deprecation", "index.js"] diff --git a/container-images/mds-native/index.ts b/container-images/mds-native/index.ts deleted file mode 100644 index 1ca3f290b..000000000 --- a/container-images/mds-native/index.ts +++ /dev/null @@ -1,28 +0,0 @@ -/* - Copyright 2019 City of Los Angeles. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - */ - -// Express local -import { ApiServer } from '@mds-core/mds-api-server' -import { api } from '@mds-core/mds-native' -import { env } from '@container-images/env-inject' - -const { npm_package_name, npm_package_version, npm_package_git_commit, PORT = 4006 } = env() - -ApiServer(api).listen(PORT, () => - /* eslint-reason avoids import of logger */ - /* eslint-disable-next-line no-console */ - console.log(`${npm_package_name} v${npm_package_version} (${npm_package_git_commit}) running on port ${PORT}`) -) diff --git a/container-images/mds-native/package.json b/container-images/mds-native/package.json deleted file mode 100644 index 01190acf2..000000000 --- a/container-images/mds-native/package.json +++ /dev/null @@ -1,28 +0,0 @@ -{ - "name": "@container-images/mds-native", - "version": "0.0.16", - "description": "Docker Image for MDS Native API", - "main": "dist/index.js", - "files": [ - "dist/" - ], - "scripts": { - "build": "tsc --build tsconfig.build.json", - "bundle": "yarn build && webpack --mode=production --env.npm_package_name=${npm_package_name} --env.npm_package_version=${npm_package_version}", - "image": "yarn bundle && ../../bin/mdsctl -c:image-name=mds-native:${npm_package_version} buildImage", - "start": "yarn watch server", - "test": "yarn test:eslint && yarn test:unit", - "test:eslint": "eslint --fix --ignore-path ../../.gitignore '**/*.ts'", - "test:unit": "exit 0" - }, - "keywords": [ - "mds" - ], - "author": "City of Los Angeles", - "dependencies": { - "@container-images/env-inject": "0.1.17", - "@mds-core/mds-api-server": "0.1.18", - "@mds-core/mds-native": "0.0.16" - }, - "license": "Apache-2.0" -} diff --git a/container-images/mds-native/tsconfig.build.json b/container-images/mds-native/tsconfig.build.json deleted file mode 100644 index 03f8c12b8..000000000 --- a/container-images/mds-native/tsconfig.build.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "extends": "../../tsconfig.build.json", - "compilerOptions": { - "outDir": "dist" - }, - "references": [ - { "path": "../../container-images/env-inject/tsconfig.build.json" }, - { "path": "../../packages/mds-native/tsconfig.build.json" }, - { "path": "../../packages/mds-api-server/tsconfig.build.json" } - ] -} diff --git a/container-images/mds-native/webpack.config.js b/container-images/mds-native/webpack.config.js deleted file mode 100644 index a2c4aff85..000000000 --- a/container-images/mds-native/webpack.config.js +++ /dev/null @@ -1,6 +0,0 @@ -module.exports = (env, argv) => require('../webpack.config')({ - env, - argv, - dirname: require('path').resolve(__dirname), - bundles: ['index'] -}) diff --git a/container-images/mds-policy-author/Dockerfile b/container-images/mds-policy-author/Dockerfile index f722e28a3..ee966ebdd 100644 --- a/container-images/mds-policy-author/Dockerfile +++ b/container-images/mds-policy-author/Dockerfile @@ -1,4 +1,4 @@ -FROM node:10.16.3-alpine +FROM node:14.2.0-alpine RUN apk add --no-cache tini @@ -8,4 +8,4 @@ COPY dist/* /mds/ WORKDIR /mds -ENTRYPOINT ["/sbin/tini", "node", "--no-deprecation", "index.js"] +ENTRYPOINT ["/sbin/tini", "node", "--no-deprecation", "server.js"] diff --git a/container-images/mds-policy-author/index.ts b/container-images/mds-policy-author/index.ts deleted file mode 100644 index c9279359e..000000000 --- a/container-images/mds-policy-author/index.ts +++ /dev/null @@ -1,28 +0,0 @@ -/* - Copyright 2019 City of Los Angeles. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - */ - -// Express local -import { ApiServer } from '@mds-core/mds-api-server' -import { api } from '@mds-core/mds-policy-author' -import { env } from '@container-images/env-inject' - -const { npm_package_name, npm_package_version, npm_package_git_commit, PORT = 4007 } = env() - -ApiServer(api).listen(PORT, () => - /* eslint-reason avoids import of logger */ - /* eslint-disable-next-line no-console */ - console.log(`${npm_package_name} v${npm_package_version} (${npm_package_git_commit}) running on port ${PORT}`) -) diff --git a/container-images/mds-policy-author/package.json b/container-images/mds-policy-author/package.json index 846b022f8..76a555e63 100644 --- a/container-images/mds-policy-author/package.json +++ b/container-images/mds-policy-author/package.json @@ -1,28 +1,25 @@ { "name": "@container-images/mds-policy-author", - "version": "0.1.18", + "version": "0.1.26", "description": "Docker Image for MDS policy-author API", + "private": true, "main": "dist/index.js", "files": [ "dist/" ], "scripts": { - "build": "tsc --build tsconfig.build.json", - "bundle": "yarn build && webpack --mode=production --env.npm_package_name=${npm_package_name} --env.npm_package_version=${npm_package_version}", - "image": "yarn bundle && ../../bin/mdsctl -c:image-name=mds-policy-author:${npm_package_version} buildImage", - "start": "yarn watch server", - "test": "yarn test:eslint && yarn test:unit", - "test:eslint": "eslint --fix --ignore-path ../../.gitignore '**/*.ts'", - "test:unit": "exit 0" + "bundle": "webpack --mode=production --env.npm_package_name=${npm_package_name} --env.npm_package_version=${npm_package_version}", + "image": "yarn bundle && ../../bin/mdsctl -c image-name=mds-policy-author:${npm_package_version} buildImage", + "test": "yarn test:eslint", + "test:eslint": "eslint --ignore-path ../../.gitignore '**/*.ts'" }, "keywords": [ "mds" ], "author": "City of Los Angeles", + "license": "Apache-2.0", "dependencies": { - "@container-images/env-inject": "0.1.17", - "@mds-core/mds-api-server": "0.1.18", - "@mds-core/mds-policy-author": "0.0.15" - }, - "license": "Apache-2.0" + "@mds-core/mds-policy-author": "0.0.23", + "@mds-core/mds-webpack-config": "0.1.0" + } } diff --git a/container-images/mds-policy-author/tsconfig.build.json b/container-images/mds-policy-author/tsconfig.build.json deleted file mode 100644 index fc79de3e6..000000000 --- a/container-images/mds-policy-author/tsconfig.build.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "extends": "../../tsconfig.build.json", - "compilerOptions": { - "outDir": "dist" - }, - "references": [ - { "path": "../../container-images/env-inject/tsconfig.build.json" }, - { "path": "../../packages/mds-policy-author/tsconfig.build.json" }, - { "path": "../../packages/mds-api-server/tsconfig.build.json" } - ] -} diff --git a/container-images/mds-policy-author/tsconfig.eslint.json b/container-images/mds-policy-author/tsconfig.eslint.json new file mode 100644 index 000000000..2a400942a --- /dev/null +++ b/container-images/mds-policy-author/tsconfig.eslint.json @@ -0,0 +1 @@ +{ "extends": "../../tsconfig.eslint.json" } diff --git a/container-images/mds-policy-author/webpack.config.js b/container-images/mds-policy-author/webpack.config.js deleted file mode 100644 index a2c4aff85..000000000 --- a/container-images/mds-policy-author/webpack.config.js +++ /dev/null @@ -1,6 +0,0 @@ -module.exports = (env, argv) => require('../webpack.config')({ - env, - argv, - dirname: require('path').resolve(__dirname), - bundles: ['index'] -}) diff --git a/container-images/mds-policy-author/webpack.config.ts b/container-images/mds-policy-author/webpack.config.ts new file mode 100644 index 000000000..6aea769e8 --- /dev/null +++ b/container-images/mds-policy-author/webpack.config.ts @@ -0,0 +1,3 @@ +import webpack from '@mds-core/mds-webpack-config' + +export default webpack.Bundle('../../packages/mds-policy-author/server').UsingDefaultConfig() diff --git a/container-images/mds-policy/Dockerfile b/container-images/mds-policy/Dockerfile index f722e28a3..ee966ebdd 100644 --- a/container-images/mds-policy/Dockerfile +++ b/container-images/mds-policy/Dockerfile @@ -1,4 +1,4 @@ -FROM node:10.16.3-alpine +FROM node:14.2.0-alpine RUN apk add --no-cache tini @@ -8,4 +8,4 @@ COPY dist/* /mds/ WORKDIR /mds -ENTRYPOINT ["/sbin/tini", "node", "--no-deprecation", "index.js"] +ENTRYPOINT ["/sbin/tini", "node", "--no-deprecation", "server.js"] diff --git a/container-images/mds-policy/index.ts b/container-images/mds-policy/index.ts deleted file mode 100644 index 8799b7336..000000000 --- a/container-images/mds-policy/index.ts +++ /dev/null @@ -1,28 +0,0 @@ -/* - Copyright 2019 City of Los Angeles. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - */ - -// Express local -import { ApiServer } from '@mds-core/mds-api-server' -import { api } from '@mds-core/mds-policy' -import { env } from '@container-images/env-inject' - -const { npm_package_name, npm_package_version, npm_package_git_commit, PORT = 4003 } = env() - -ApiServer(api).listen(PORT, () => - /* eslint-reason avoids import of logger */ - /* eslint-disable-next-line no-console */ - console.log(`${npm_package_name} v${npm_package_version} (${npm_package_git_commit}) running on port ${PORT}`) -) diff --git a/container-images/mds-policy/package.json b/container-images/mds-policy/package.json index 6378f18b3..523dafd50 100644 --- a/container-images/mds-policy/package.json +++ b/container-images/mds-policy/package.json @@ -1,28 +1,25 @@ { "name": "@container-images/mds-policy", - "version": "0.1.18", + "version": "0.1.26", "description": "Docker Image for MDS Policy API", + "private": true, "main": "dist/index.js", "files": [ "dist/" ], "scripts": { - "build": "tsc --build tsconfig.build.json", - "bundle": "yarn build && webpack --mode=production --env.npm_package_name=${npm_package_name} --env.npm_package_version=${npm_package_version}", - "image": "yarn bundle && ../../bin/mdsctl -c:image-name=mds-policy:${npm_package_version} buildImage", - "start": "yarn watch server", - "test": "yarn test:eslint && yarn test:unit", - "test:eslint": "eslint --fix --ignore-path ../../.gitignore '**/*.ts'", - "test:unit": "exit 0" + "bundle": "webpack --mode=production --env.npm_package_name=${npm_package_name} --env.npm_package_version=${npm_package_version}", + "image": "yarn bundle && ../../bin/mdsctl -c image-name=mds-policy:${npm_package_version} buildImage", + "test": "yarn test:eslint", + "test:eslint": "eslint --ignore-path ../../.gitignore '**/*.ts'" }, "keywords": [ "mds" ], "author": "City of Los Angeles", + "license": "Apache-2.0", "dependencies": { - "@container-images/env-inject": "0.1.17", - "@mds-core/mds-api-server": "0.1.18", - "@mds-core/mds-policy": "0.0.19" - }, - "license": "Apache-2.0" + "@mds-core/mds-policy": "0.0.27", + "@mds-core/mds-webpack-config": "0.1.0" + } } diff --git a/container-images/mds-policy/tsconfig.build.json b/container-images/mds-policy/tsconfig.build.json deleted file mode 100644 index 758cef4a3..000000000 --- a/container-images/mds-policy/tsconfig.build.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "extends": "../../tsconfig.build.json", - "compilerOptions": { - "outDir": "dist" - }, - "references": [ - { "path": "../../container-images/env-inject/tsconfig.build.json" }, - { "path": "../../packages/mds-policy/tsconfig.build.json" }, - { "path": "../../packages/mds-api-server/tsconfig.build.json" } - ] -} diff --git a/container-images/mds-policy/tsconfig.eslint.json b/container-images/mds-policy/tsconfig.eslint.json new file mode 100644 index 000000000..2a400942a --- /dev/null +++ b/container-images/mds-policy/tsconfig.eslint.json @@ -0,0 +1 @@ +{ "extends": "../../tsconfig.eslint.json" } diff --git a/container-images/mds-policy/webpack.config.js b/container-images/mds-policy/webpack.config.js deleted file mode 100644 index a2c4aff85..000000000 --- a/container-images/mds-policy/webpack.config.js +++ /dev/null @@ -1,6 +0,0 @@ -module.exports = (env, argv) => require('../webpack.config')({ - env, - argv, - dirname: require('path').resolve(__dirname), - bundles: ['index'] -}) diff --git a/container-images/mds-policy/webpack.config.ts b/container-images/mds-policy/webpack.config.ts new file mode 100644 index 000000000..5afc1fdef --- /dev/null +++ b/container-images/mds-policy/webpack.config.ts @@ -0,0 +1,3 @@ +import webpack from '@mds-core/mds-webpack-config' + +export default webpack.Bundle('../../packages/mds-policy/server').UsingDefaultConfig() diff --git a/container-images/mds-web-sockets/Dockerfile b/container-images/mds-web-sockets/Dockerfile new file mode 100644 index 000000000..ee966ebdd --- /dev/null +++ b/container-images/mds-web-sockets/Dockerfile @@ -0,0 +1,11 @@ +FROM node:14.2.0-alpine + +RUN apk add --no-cache tini + +RUN mkdir /mds + +COPY dist/* /mds/ + +WORKDIR /mds + +ENTRYPOINT ["/sbin/tini", "node", "--no-deprecation", "server.js"] diff --git a/container-images/mds-web-sockets/package.json b/container-images/mds-web-sockets/package.json new file mode 100644 index 000000000..59edbd536 --- /dev/null +++ b/container-images/mds-web-sockets/package.json @@ -0,0 +1,25 @@ +{ + "name": "@container-images/mds-web-sockets", + "version": "0.0.1", + "description": "Docker Image for MDS Web Sockets", + "private": true, + "main": "dist/index.js", + "files": [ + "dist/" + ], + "scripts": { + "bundle": "webpack --mode=production --env.npm_package_name=${npm_package_name} --env.npm_package_version=${npm_package_version}", + "image": "yarn bundle && ../../bin/mdsctl -c image-name=mds-web-sockets:${npm_package_version} buildImage", + "test": "yarn test:eslint", + "test:eslint": "eslint --ignore-path ../../.gitignore '**/*.ts'" + }, + "keywords": [ + "mds" + ], + "author": "City of Los Angeles", + "license": "Apache-2.0", + "dependencies": { + "@mds-core/mds-web-sockets": "0.0.1", + "@mds-core/mds-webpack-config": "0.1.0" + } +} diff --git a/container-images/mds-web-sockets/tsconfig.eslint.json b/container-images/mds-web-sockets/tsconfig.eslint.json new file mode 100644 index 000000000..2a400942a --- /dev/null +++ b/container-images/mds-web-sockets/tsconfig.eslint.json @@ -0,0 +1 @@ +{ "extends": "../../tsconfig.eslint.json" } diff --git a/container-images/mds-web-sockets/webpack.config.ts b/container-images/mds-web-sockets/webpack.config.ts new file mode 100644 index 000000000..0b34b2bc9 --- /dev/null +++ b/container-images/mds-web-sockets/webpack.config.ts @@ -0,0 +1,3 @@ +import webpack from '@mds-core/mds-webpack-config' + +export default webpack.Bundle('../../packages/mds-web-sockets/server').UsingDefaultConfig() diff --git a/container-images/webpack.config.js b/container-images/webpack.config.js deleted file mode 100644 index 7614d778a..000000000 --- a/container-images/webpack.config.js +++ /dev/null @@ -1,65 +0,0 @@ -const webpack = require('webpack') -const GitRevisionPlugin = require('git-revision-webpack-plugin') - -const gitRevisionPlugin = new GitRevisionPlugin({ - commithashCommand: 'rev-parse --short HEAD' -}) - -module.exports = ({ env, argv, dirname, bundles }) => { - const { npm_package_name, npm_package_version } = env - const [, package] = npm_package_name.split('/') - const dist = `${dirname}/dist` - return bundles.map(bundle => ({ - entry: { [bundle]: `${dirname}/${bundle}.ts` }, - output: { path: dist, filename: `${bundle}.js` }, - module: { - rules: [ - { - test: /\.ts$/, - use: 'ts-loader', - exclude: /node_modules/ - } - ] - }, - plugins: [ - // Ignore optional Postgres dependency - new webpack.IgnorePlugin(/^pg-native$/), - // Ignore optional Redis dependency - new webpack.IgnorePlugin(/^hiredis$/), - // Ignore optional bufferutil/utf-8-validate dependencies - // https://github.com/adieuadieu/serverless-chrome/issues/103#issuecomment-358261003 - new webpack.IgnorePlugin(/^bufferutil$/), - new webpack.IgnorePlugin(/^utf-8-validate$/), - // Ignore Critical Dependency Warnings - // https://medium.com/tomincode/hiding-critical-dependency-warnings-from-webpack-c76ccdb1f6c1 - new webpack.ContextReplacementPlugin( - /node_modules\/express\/lib|node_modules\/optional|node_modules\/google-spreadsheet/, - data => { - delete data.dependencies[0].critical - return data - } - ), - // Make npm package name/version available to bundle - new webpack.DefinePlugin({ - NPM_PACKAGE_NAME: JSON.stringify(npm_package_name), - NPM_PACKAGE_VERSION: JSON.stringify(npm_package_version), - NPM_PACKAGE_GIT_BRANCH: JSON.stringify(gitRevisionPlugin.branch()), - NPM_PACKAGE_GIT_COMMIT: JSON.stringify(gitRevisionPlugin.commithash()), - NPM_PACKAGE_BUILD_DATE: JSON.stringify(new Date().toISOString()) - }) - ], - resolve: { - extensions: ['.ts', '.js'] - }, - output: { - libraryTarget: 'commonjs' - }, - target: 'node', - stats: { - all: false, - assets: true, - errors: true, - warnings: true - } - })) -} diff --git a/cypress.json b/cypress.json deleted file mode 100644 index 1833c7e16..000000000 --- a/cypress.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "video": false, - "testFiles": "**/*.spec.js" -} \ No newline at end of file diff --git a/cypress/integration/agency.spec.js b/cypress/integration/agency.spec.js deleted file mode 100644 index cd260a3fe..000000000 --- a/cypress/integration/agency.spec.js +++ /dev/null @@ -1,18 +0,0 @@ -var Base64={_keyStr:"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=",encode:function(e){var t="";var n,r,i,s,o,u,a;var f=0;e=Base64._utf8_encode(e);while(f>2;o=(n&3)<<4|r>>4;u=(r&15)<<2|i>>6;a=i&63;if(isNaN(r)){u=a=64}else if(isNaN(i)){a=64}t=t+this._keyStr.charAt(s)+this._keyStr.charAt(o)+this._keyStr.charAt(u)+this._keyStr.charAt(a)}return t},decode:function(e){var t="";var n,r,i;var s,o,u,a;var f=0;e=e.replace(/++[++^A-Za-z0-9+/=]/g,"");while(f>4;r=(o&15)<<4|u>>2;i=(u&3)<<6|a;t=t+String.fromCharCode(n);if(u!=64){t=t+String.fromCharCode(r)}if(a!=64){t=t+String.fromCharCode(i)}}t=Base64._utf8_decode(t);return t},_utf8_encode:function(e){e=e.replace(/\r\n/g,"n");var t="";for(var n=0;n127&&r<2048){t+=String.fromCharCode(r>>6|192);t+=String.fromCharCode(r&63|128)}else{t+=String.fromCharCode(r>>12|224);t+=String.fromCharCode(r>>6&63|128);t+=String.fromCharCode(r&63|128)}}return t},_utf8_decode:function(e){var t="";var n=0;var r=c1=c2=0;while(n191&&r<224){c2=e.charCodeAt(n+1);t+=String.fromCharCode((r&31)<<6|c2&63);n+=2}else{c2=e.charCodeAt(n+1);c3=e.charCodeAt(n+2);t+=String.fromCharCode((r&15)<<12|(c2&63)<<6|c3&63);n+=3}}return t}} - -describe('dbInit', function() { - it('successfully initializes', function() { - cy.request({ - url: 'http://localhost/agency', - auth: { - bearer: "." + Base64.encode("{\"scope\": \"admin:all test:all\"}") + ".", - }, - }) - .then((resp) => { - expect(resp.status).to.eq(200) - expect(resp.headers['content-type']).to.eq('application/json; charset=utf-8'); - expect(resp.headers['server']).to.eq('istio-envoy'); - expect(resp.body).to.deep.eq({"name":"@container-images/mds-agency","version":"0.1.9","build":{"date":"2019-09-20T00:15:25.778Z","branch":"alex/helm","commit":"76ad82f"},"node":"8.16.1","status":"Running"}); - }) - }) -}) diff --git a/cypress/integration/cors-delete.no-spec.js b/cypress/integration/cors-delete.no-spec.js deleted file mode 100644 index 2b76e3bac..000000000 --- a/cypress/integration/cors-delete.no-spec.js +++ /dev/null @@ -1,19 +0,0 @@ -describe('cors-delete', function() { - it('successfully initializes', function() { - cy.request({ - url: 'http://localhost/policy-author/policies/0079b462-a622-4d92-8af0-49b63a70f062', - method: 'OPTIONS', - headers: { - 'Sec-Fetch-Mode': 'no-cors', - 'Access-Control-Request': 'DELETE', - 'Origin': 'https://dev.lacuna.city', - 'Referer': 'https://dev.lacuna.city/', - 'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.132 Safari/537.36', - 'Access-Control-Request-Headers': 'authorization,content-type' - } - }) - .then((resp) => { - // todo: write verification - }) - }) -}) diff --git a/cypress/support/commands.js b/cypress/support/commands.js deleted file mode 100644 index c1f5a772e..000000000 --- a/cypress/support/commands.js +++ /dev/null @@ -1,25 +0,0 @@ -// *********************************************** -// This example commands.js shows you how to -// create various custom commands and overwrite -// existing commands. -// -// For more comprehensive examples of custom -// commands please read more here: -// https://on.cypress.io/custom-commands -// *********************************************** -// -// -// -- This is a parent command -- -// Cypress.Commands.add("login", (email, password) => { ... }) -// -// -// -- This is a child command -- -// Cypress.Commands.add("drag", { prevSubject: 'element'}, (subject, options) => { ... }) -// -// -// -- This is a dual command -- -// Cypress.Commands.add("dismiss", { prevSubject: 'optional'}, (subject, options) => { ... }) -// -// -// -- This is will overwrite an existing command -- -// Cypress.Commands.overwrite("visit", (originalFn, url, options) => { ... }) diff --git a/cypress/support/index.js b/cypress/support/index.js deleted file mode 100644 index d68db96df..000000000 --- a/cypress/support/index.js +++ /dev/null @@ -1,20 +0,0 @@ -// *********************************************************** -// This example support/index.js is processed and -// loaded automatically before your test files. -// -// This is a great place to put global configuration and -// behavior that modifies Cypress. -// -// You can change the location of this file or turn off -// automatically serving support files with the -// 'supportFile' configuration option. -// -// You can read more here: -// https://on.cypress.io/configuration -// *********************************************************** - -// Import commands.js using ES2015 syntax: -import './commands' - -// Alternatively you can use CommonJS syntax: -// require('./commands') diff --git a/docker-compose.yml.tpl b/docker-compose.yml.tpl deleted file mode 100644 index efb07e370..000000000 --- a/docker-compose.yml.tpl +++ /dev/null @@ -1,116 +0,0 @@ -version: "3.7" -services: - - agency: - image: mds-agency:${AGENCY_VERSION} - ports: - - "4001" - environment: - PATH_PREFIX: /agency - PG_HOST: postgres - PG_NAME: mds - PG_USER: mdsadmin - PG_PASS: "Password123#" - PG_MIGRATIONS: "true" - REDIS_HOST: redis - - audit: - image: mds-audit:$AUDIT_VERSION - ports: - - "4002" - environment: - PATH_PREFIX: /audit - PG_HOST: postgres - PG_NAME: mds - PG_USER: mdsadmin - PG_PASS: "Password123#" - PG_MIGRATIONS: "true" - REDIS_HOST: redis - - policy: - image: mds-policy:$POLICY_VERSION - ports: - - "4003" - environment: - PATH_PREFIX: /policy - PG_HOST: postgres - PG_NAME: mds - PG_USER: mdsadmin - PG_PASS: "Password123#" - PG_MIGRATIONS: "true" - REDIS_HOST: redis - - compliance: - image: mds-compliance:$COMPLIANCE_VERSION - ports: - - "4004" - environment: - PATH_PREFIX: /compliance - PG_HOST: postgres - PG_NAME: mds - PG_USER: mdsadmin - PG_PASS: "Password123#" - PG_MIGRATIONS: "true" - REDIS_HOST: redis - - daily: - image: mds-daily:$DAILY_VERSION - ports: - - "4005" - environment: - PATH_PREFIX: /daily - PG_HOST: postgres - PG_NAME: mds - PG_USER: mdsadmin - PG_PASS: "Password123#" - PG_MIGRATIONS: "true" - REDIS_HOST: redis - - native: - image: mds-native:$NATIVE_VERSION - ports: - - "4006" - environment: - PATH_PREFIX: /native - PG_HOST: postgres - PG_NAME: mds - PG_USER: mdsadmin - PG_PASS: "Password123#" - PG_MIGRATIONS: "true" - REDIS_HOST: redis - - policy-author: - image: mds-policy-author:$POLICY_AUTHOR_VERSION - ports: - - "4007" - environment: - PATH_PREFIX: /policy-author - PG_HOST: postgres - PG_NAME: mds - PG_USER: mdsadmin - PG_PASS: "Password123#" - PG_MIGRATIONS: "true" - REDIS_HOST: redis - - gateway: - build: - context: ./nginx - ports: - - "80:80" - - redis: - image: redis:5 - ports: - - "6379" - - postgres: - image: postgres:10 - ports: - - "5432:5432" - environment: - POSTGRES_USER: mdsadmin - POSTGRES_PASSWORD: "Password123#" - POSTGRES_DB: mds - -networks: - default: diff --git a/helm/curl/Chart.yaml b/helm/curl/Chart.yaml new file mode 100644 index 000000000..851691e8c --- /dev/null +++ b/helm/curl/Chart.yaml @@ -0,0 +1,5 @@ +apiVersion: v1 +appVersion: '1.0.0' +description: Curl +name: curl +version: 1.0.0 diff --git a/helm/curl/templates/pod.yaml b/helm/curl/templates/pod.yaml new file mode 100644 index 000000000..107e9e71e --- /dev/null +++ b/helm/curl/templates/pod.yaml @@ -0,0 +1,24 @@ +apiVersion: v1 +kind: Pod +metadata: + labels: + run: curl + name: curl +spec: + containers: + # This could be any image that we can SSH into and has curl. + - image: radial/busyboxplus:curl + imagePullPolicy: IfNotPresent + resources: + limits: + cpu: {{ $.Values.resourcesLimitsCpu | default "500m" }} + memory: {{ $.Values.resourcesLimitsMemory | default "512Mi" }} + requests: + cpu: {{ $.Values.resourcesRequestsCpu | default "50m" }} + memory: {{ $.Values.resourcesRequestsMemory | default "128Mi" }} + name: curl + resources: {} + stdin: true + terminationMessagePath: /dev/termination-log + terminationMessagePolicy: File + tty: true diff --git a/helm/dashboard/Chart.yaml b/helm/dashboard/Chart.yaml deleted file mode 100644 index fd629fee1..000000000 --- a/helm/dashboard/Chart.yaml +++ /dev/null @@ -1,5 +0,0 @@ -apiVersion: v1 -appVersion: "1.10.1" -description: Dashboard -name: kubernetes-dashboard -version: 1.10.1 diff --git a/helm/dashboard/templates/kubernetes-dashboard.yaml b/helm/dashboard/templates/kubernetes-dashboard.yaml deleted file mode 100644 index 2eda602da..000000000 --- a/helm/dashboard/templates/kubernetes-dashboard.yaml +++ /dev/null @@ -1,162 +0,0 @@ -# Copyright 2017 The Kubernetes Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# ------------------- Dashboard Secret ------------------- # - -apiVersion: v1 -kind: Secret -metadata: - labels: - k8s-app: kubernetes-dashboard - name: kubernetes-dashboard-certs - namespace: {{ $.Release.Namespace }} -type: Opaque - ---- -# ------------------- Dashboard Service Account ------------------- # - -apiVersion: v1 -kind: ServiceAccount -metadata: - labels: - k8s-app: kubernetes-dashboard - name: kubernetes-dashboard - namespace: {{ $.Release.Namespace }} - ---- -# ------------------- Dashboard Role & Role Binding ------------------- # - -kind: Role -apiVersion: rbac.authorization.k8s.io/v1 -metadata: - name: kubernetes-dashboard-minimal - namespace: {{ $.Release.Namespace }} -rules: - # Allow Dashboard to create 'kubernetes-dashboard-key-holder' secret. -- apiGroups: [""] - resources: ["secrets"] - verbs: ["create"] - # Allow Dashboard to create 'kubernetes-dashboard-settings' config map. -- apiGroups: [""] - resources: ["configmaps"] - verbs: ["create"] - # Allow Dashboard to get, update and delete Dashboard exclusive secrets. -- apiGroups: [""] - resources: ["secrets"] - resourceNames: ["kubernetes-dashboard-key-holder", "kubernetes-dashboard-certs"] - verbs: ["get", "update", "delete"] - # Allow Dashboard to get and update 'kubernetes-dashboard-settings' config map. -- apiGroups: [""] - resources: ["configmaps"] - resourceNames: ["kubernetes-dashboard-settings"] - verbs: ["get", "update"] - # Allow Dashboard to get metrics from heapster. -- apiGroups: [""] - resources: ["services"] - resourceNames: ["heapster"] - verbs: ["proxy"] -- apiGroups: [""] - resources: ["services/proxy"] - resourceNames: ["heapster", "http:heapster:", "https:heapster:"] - verbs: ["get"] - ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: RoleBinding -metadata: - name: kubernetes-dashboard-minimal - namespace: {{ $.Release.Namespace }} -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: Role - name: kubernetes-dashboard-minimal -subjects: -- kind: ServiceAccount - name: kubernetes-dashboard - namespace: {{ $.Release.Namespace }} - ---- -# ------------------- Dashboard Deployment ------------------- # - -kind: Deployment -apiVersion: apps/v1 -metadata: - labels: - k8s-app: kubernetes-dashboard - name: kubernetes-dashboard - namespace: {{ $.Release.Namespace }} -spec: - replicas: 1 - revisionHistoryLimit: 10 - selector: - matchLabels: - k8s-app: kubernetes-dashboard - template: - metadata: - labels: - k8s-app: kubernetes-dashboard - spec: - containers: - - name: kubernetes-dashboard - image: k8s.gcr.io/kubernetes-dashboard-amd64:v1.10.1 - ports: - - containerPort: 8443 - protocol: TCP - args: - - --auto-generate-certificates - # Uncomment the following line to manually specify Kubernetes API server Host - # If not specified, Dashboard will attempt to auto discover the API server and connect - # to it. Uncomment only if the default does not work. - # - --apiserver-host=http://my-address:port - volumeMounts: - - name: kubernetes-dashboard-certs - mountPath: /certs - # Create on-disk volume to store exec logs - - mountPath: /tmp - name: tmp-volume - livenessProbe: - httpGet: - scheme: HTTPS - path: / - port: 8443 - initialDelaySeconds: 30 - timeoutSeconds: 30 - volumes: - - name: kubernetes-dashboard-certs - secret: - secretName: kubernetes-dashboard-certs - - name: tmp-volume - emptyDir: {} - serviceAccountName: kubernetes-dashboard - # Comment the following tolerations if Dashboard must not be deployed on master - tolerations: - - key: node-role.kubernetes.io/master - effect: NoSchedule - ---- -# ------------------- Dashboard Service ------------------- # - -kind: Service -apiVersion: v1 -metadata: - labels: - k8s-app: kubernetes-dashboard - name: kubernetes-dashboard - namespace: {{ $.Release.Namespace }} -spec: - ports: - - port: 443 - targetPort: 8443 - selector: - k8s-app: kubernetes-dashboard diff --git a/helm/dashboard/templates/service-account.yaml b/helm/dashboard/templates/service-account.yaml deleted file mode 100644 index 89b9d34a7..000000000 --- a/helm/dashboard/templates/service-account.yaml +++ /dev/null @@ -1,18 +0,0 @@ -apiVersion: v1 -kind: ServiceAccount -metadata: - name: admin-user - namespace: {{ $.Release.Namespace }} ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRoleBinding -metadata: - name: admin-user -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: ClusterRole - name: cluster-admin -subjects: -- kind: ServiceAccount - name: admin-user - namespace: {{ $.Release.Namespace }} diff --git a/helm/dns/Chart.yaml b/helm/dns/Chart.yaml new file mode 100644 index 000000000..15e6cbfb1 --- /dev/null +++ b/helm/dns/Chart.yaml @@ -0,0 +1,5 @@ +apiVersion: v1 +appVersion: '1.0.0' +description: DNS +name: dns +version: 1.0.0 diff --git a/helm/dns/templates/pod.yaml b/helm/dns/templates/pod.yaml new file mode 100644 index 000000000..4b57b0d03 --- /dev/null +++ b/helm/dns/templates/pod.yaml @@ -0,0 +1,20 @@ +apiVersion: v1 +kind: Pod +metadata: + name: dns +spec: + containers: + - name: busybox + image: busybox:1.28 + resources: + limits: + cpu: {{ $.Values.resourcesLimitsCpu | default "500m" }} + memory: {{ $.Values.resourcesLimitsMemory | default "512Mi" }} + requests: + cpu: {{ $.Values.resourcesRequestsCpu | default "50m" }} + memory: {{ $.Values.resourcesRequestsMemory | default "128Mi" }} + command: + - sleep + - "3600" + imagePullPolicy: IfNotPresent + restartPolicy: Always diff --git a/helm/grafana/Chart.yaml b/helm/grafana/Chart.yaml new file mode 100755 index 000000000..ef8dad2de --- /dev/null +++ b/helm/grafana/Chart.yaml @@ -0,0 +1,19 @@ +apiVersion: v1 +appVersion: 6.5.2 +description: Grafana is an open source, feature rich metrics dashboard and graph editor + for Graphite, Elasticsearch, OpenTSDB, Prometheus and InfluxDB. +engine: gotpl +home: https://grafana.com/ +icon: https://bitnami.com/assets/stacks/grafana/img/grafana-stack-220x234.png +keywords: +- analitics +- monitoring +- metrics +- logs +maintainers: +- email: containers@bitnami.com + name: Bitnami +name: grafana +sources: +- https://github.com/bitnami/bitnami-docker-grafana +version: 1.2.0 diff --git a/helm/grafana/README.md b/helm/grafana/README.md new file mode 100755 index 000000000..408c46330 --- /dev/null +++ b/helm/grafana/README.md @@ -0,0 +1,301 @@ +# Grafana + +[Grafana](https://grafana.com/) is an open source, feature rich metrics dashboard and graph editor for Graphite, Elasticsearch, OpenTSDB, Prometheus and InfluxDB. + +## TL;DR; + +```console +$ helm repo add bitnami https://charts.bitnami.com/bitnami +$ helm install bitnami/grafana +``` + +## Introduction + +This chart bootstraps a [grafana](https://github.com/bitnami/bitnami-docker-grafana) deployment on a [Kubernetes](http://kubernetes.io) cluster using the [Helm](https://helm.sh) package manager. + +Bitnami charts can be used with [Kubeapps](https://kubeapps.com/) for deployment and management of Helm Charts in clusters. + +## Prerequisites + +- Kubernetes 1.12+ +- Helm 2.11+ or Helm 3.0-beta3+ +- PV provisioner support in the underlying infrastructure +- ReadWriteMany volumes for deployment scaling + +## Installing the Chart + +To install the chart with the release name `my-release`: + +```console +$ helm repo add bitnami https://charts.bitnami.com/bitnami +$ helm install --name my-release bitnami/grafana +``` + +These commands deploy grafana on the Kubernetes cluster in the default configuration. The [Parameters](#parameters) section lists the parameters that can be configured during installation. + +> **Tip**: List all releases using `helm list` + +## Uninstalling the Chart + +To uninstall/delete the `my-release` statefulset: + +```console +$ helm delete my-release +``` + +The command removes all the Kubernetes components associated with the chart and deletes the release. Use the option `--purge` to delete all persistent volumes too. + +## Parameters + +The following tables lists the configurable parameters of the grafana chart and their default values. + +| Parameter | Description | Default | +|----------------------------------------|--------------------------------------------------------------------------------------------------------|---------------------------------------------------------| +| `global.imageRegistry` | Global Docker image registry | `nil` | +| `global.imagePullSecrets` | Global Docker registry secret names as an array | `[]` (does not add image pull secrets to deployed pods) | +| `image.registry` | Grafana image registry | `docker.io` | +| `image.repository` | Grafana image name | `bitnami/grafana` | +| `image.tag` | Grafana image tag | `{TAG_NAME}` | +| `image.pullPolicy` | Grafana image pull policy | `IfNotPresent` | +| `image.pullSecrets` | Specify docker-registry secret names as an array | `[]` (does not add image pull secrets to deployed pods) | +| `nameOverride` | String to partially override grafana.fullname template with a string (will prepend the release name) | `nil` | +| `fullnameOverride` | String to fully override grafana.fullname template with a string | `nil` | +| `replicaCount` | Number of replicas of the Grafana Pod | `1` | +| `updateStrategy` | Update strategy for deployment | `{type: "RollingUpdate"}` | +| `schedulerName` | Alternative scheduler | `nil` | +| `admin.user` | Grafana admin username | `admin` | +| `admin.password` | Grafana admin password | Randomly generated | +| `smtp.enabled` | Enable SMTP configuration | `false` | +| `smtp.existingSecret` | Secret with SMTP credentials | `nil` | +| `smtp.existingSecretUserKey` | Key which value is the SMTP user in the SMTP secret | `user` | +| `smtp.existingSecretPasswordKey` | Key which values is the SMTP password in the SMTP secret | `password` | +| `plugins` | Grafana plugins to be installed in deployment time separated by commas | `nil` | +| `ldap.enabled` | Enable LDAP for Grafana | `false` | +| `ldap.allowSignUp` | Allows LDAP sign up for Grafana | `false` | +| `ldap.configMapName` | Name of the ConfigMap with the LDAP configuration file for Grafana | `nil` | +| `extraEnvVars` | Array containing extra env vars to configure Grafana | `{}` | +| `extraConfigmaps` | Array to mount extra ConfigMaps to configure Grafana | `{}` | +| `config.useGrafanaIniFile` | Allows to load a `grafana.ini` file | `false` | +| `config.grafanaIniConfigMap` | Name of the ConfigMap containing the `grafana.ini` file | `nil` | +| `config.grafanaIniSecret` | Name of the Secret containing the `grafana.ini` file | `nil` | +| `config.useCustomIniFile` | Allows to load a `custom.ini` file | `false` | +| `config.customIniConfigMap` | Name of the ConfigMap containing the `custom.ini` file | `nil` | +| `config.customIniSecret` | Name of the Secret containing the `custom.ini` file | `nil` | +| `dashboardsProvider.enabled` | Enable the use of a Grafana dashboard provider | `false` | +| `dashboardsProvider.configMapName` | Name of a ConfigMap containing a custom dashboard provider | `nil` | +| `dashboardsConfigMaps` | Array with the names of a series of ConfigMaps containing dashboards files | `nil` | +| `datasources.secretName` | Secret name containing custom datasource files | `nil` | +| `persistence.enabled` | Enable persistence | `true` | +| `presistence.storageClass` | Storage class to use with the PVC | `nil` | +| `persistence.accessMode` | Access mode to the PV | `ReadWriteOnce` | +| `persistence.size` | Size for the PV | `10Gi` | +| `livenessProbe.enabled` | Enable/disable the Liveness probe | `true` | +| `livenessProbe.initialDelaySeconds` | Delay before liveness probe is initiated | `60` | +| `livenessProbe.periodSeconds` | How often to perform the probe | `10` | +| `livenessProbe.timeoutSeconds` | When the probe times out | `5` | +| `livenessProbe.successThreshold` | Minimum consecutive successes for the probe to be considered successful after having failed. | `1` | +| `livenessProbe.failureThreshold` | Minimum consecutive failures for the probe to be considered failed after having succeeded. | `6` | +| `readinessProbe.enabled` | Enable/disable the Readiness probe | `true` | +| `readinessProbe.initialDelaySeconds` | Delay before readiness probe is initiated | `5` | +| `readinessProbe.periodSeconds` | How often to perform the probe | `10` | +| `readinessProbe.timeoutSeconds` | When the probe times out | `5` | +| `readinessProbe.failureThreshold` | Minimum consecutive failures for the probe to be considered failed after having succeeded. | `6` | +| `readinessProbe.successThreshold` | Minimum consecutive successes for the probe to be considered successful after having failed. | `1` | +| `service.type` | Kubernetes Service type | `ClusterIP` | +| `service.webPort` | Grafana client port | `80` | +| `service.clusterPort` | Grafana cluster port | `7077` | +| `service.nodePort` | Port to bind to for NodePort service type (client port) | `nil` | +| `service.annotations` | Annotations for Grafana service | `{}` | +| `service.loadBalancerIP` | loadBalancerIP if Grafana service type is `LoadBalancer` | `nil` | +| `ingress.enabled` | Enable the use of the ingress controller to access the web UI | `false` | +| `ingress.certManager` | Add annotations for cert-manager | `false` | +| `ingress.annotations` | Annotations for the Grafana Ingress | `{}` | +| `ingress.hosts[0].name` | Hostname to your Grafana installation | `grafana.local` | +| `ingress.hosts[0].paths` | Path within the url structure | `["/"]` | +| `ingress.hosts[0].tls` | Utilize TLS backend in ingress | `false` | +| `ingress.hosts[0].tlsHosts` | Array of TLS hosts for ingress record (defaults to `ingress.hosts[0].name` if `nil`) | `nil` | +| `ingress.hosts[0].tlsSecret` | TLS Secret (certificates) | `grafana.local-tls` | +| `securityContext.enabled` | Enable securityContext on for Granafa deployment | `true` | +| `securityContext.runAsUser` | User for the security context | `1001` | +| `securityContext.fsGroup` | Group to configure permissions for volumes | `1001` | +| `securityContext.runAsNonRoot` | Run containers as non-root users | `true` | +| `resources` | Configure resource requests and limits | `nil` | +| `nodeSelector` | Node labels for pod assignment | `{}` | +| `tolerations` | Tolerations for pod assignment | `[]` | +| `affinity` | Affinity for pod assignment | `{}` | +| `podAnnotations` | Pod annotations | `{}` | +| `metrics.enabled` | Enable the export of Prometheus metrics | `false` | +| `metrics.service.annotations` | Annotations for Prometheus metrics service | `Check values.yaml file` | +| `metrics.serviceMonitor.enabled` | if `true`, creates a Prometheus Operator ServiceMonitor (also requires `metrics.enabled` to be `true`) | `false` | +| `metrics.serviceMonitor.namespace` | Namespace in which Prometheus is running | `nil` | +| `metrics.serviceMonitor.interval` | Interval at which metrics should be scraped. | `nil` (Prometheus Operator default value) | +| `metrics.serviceMonitor.scrapeTimeout` | Timeout after which the scrape is ended | `nil` (Prometheus Operator default value) | +| `metrics.serviceMonitor.selector` | Prometheus instance selector labels | `nil` | + +Specify each parameter using the `--set key=value[,key=value]` argument to `helm install`. For example, + +```console +$ helm install --name my-release \ + --set admin.user=admin-user bitnami/grafana +``` + +The above command sets the Grafana admin user to `admin-user`. + +Alternatively, a YAML file that specifies the values for the parameters can be provided while installing the chart. For example, + +```console +$ helm install --name my-release -f values.yaml bitnami/grafana +``` + +> **Tip**: You can use the default [values.yaml](values.yaml) + +## Configuration and installation details + +### [Rolling VS Immutable tags](https://docs.bitnami.com/containers/how-to/understand-rolling-tags-containers/) + +It is strongly recommended to use immutable tags in a production environment. This ensures your deployment does not change automatically if the same tag is updated with a different image. + +Bitnami will release a new chart updating its containers if a new version of the main container, significant changes, or critical vulnerabilities exist. + +### Production configuration + +This chart includes a `values-production.yaml` file where you can find some parameters oriented to production configuration in comparison to the regular `values.yaml`. You can use this file instead of the default one. + +- Enable ingress controller: + +```diff +- ingress.enabled: false ++ ingress.enabled: true +``` + +- Enable exposing Prometheus metrics: + +```diff +- metrics.enabled: false ++ metrics.enabled: true +``` + +### Using custom configuration + +Grafana supports multiples configuration files. Using kubernetes you can mount a file using a ConfigMap or a Secret. For example, to mount a custom `grafana.ini` file or `custom.ini` file you can create a ConfigMap like the following: + +```yaml +apiVersion: v1 +kind: ConfigMap +metadata: + name: myconfig +data: + grafana.ini: |- + # Raw text of the file +``` + +And now you need to pass the ConfigMap name, to the corresponding parameters: `config.useGrafanaIniFile=true` and `config.grafanaIniConfigMap=myconfig`. + +To provide dashboards on deployment time, Grafana needs a dashboards provider and the dashboards themselves. +A default provider is created if enabled, or you can mount your own provider using a ConfigMap, but have in mind that the path to the dashboard folder must be `/opt/bitnami/grafana/dashboards`. + 1. To create a dashboard, it is needed to have a datasource for it. The datasources must be created mounting a secret with all the datasource files in it. In this case, it is not a ConfigMap because the datasource could contain sensitive information. + 2. To load the dashboards themselves you need to create a ConfigMap for each one containing the `json` file that defines the dashboard and set the array with the ConfigMap names into the `dashboardsConfigMaps` parameter. +Note the difference between the datasources and the dashboards creation. For the datasources we can use just one secret with all of the files, while for the dashboards we need one ConfigMap per file. + +For example, create the dashboard ConfigMap(s) and datasource Secret as described below: + +```console +$ kubectl create secret generic datasource-secret --from-file=datasource-secret.yaml +$ kubectl create configmap my-dashboard-1 --from-file=my-dashboard-1.json +$ kubectl create configmap my-dashboard-2 --from-file=my-dashboard-2.json +``` + +> Note: the commands above assume you had previously exported your dashboards in the JSON files: *my-dashboard-1.json* and *my-dashboard-2.json* + +> Note: the commands above assume you had previously created a datasource config file *datasource-secret.yaml*. Find an example at https://grafana.com/docs/grafana/latest/administration/provisioning/#example-datasource-config-file + +Once you have them, use the following parameters to deploy Grafana with 2 custom dashboards: + +```console +dashboardsProvider.enabled=true +datasources.secretName=datasource-secret +dashboardsConfigMaps[0].configMapName=my-dashboard-1 +dashboardsConfigMaps[0].fileName=my-dashboard-1.json +dashboardsConfigMaps[1].configMapName=my-dashboard-2 +dashboardsConfigMaps[1].fileName=my-dashboard-2.json +``` + +More info at [Grafana documentation](https://grafana.com/docs/grafana/latest/administration/provisioning/#dashboards). + +### LDAP configuration + +To enable LDAP authentication it is necessary to provide a ConfigMap with the Grafana LDAP configuration file. For instance: + +**configmap.yaml**: + +```yaml +apiVersion: v1 +kind: ConfigMap +metadata: + name: ldap-config +data: + ldap.toml: |- + [[servers]] + # Ldap server host (specify multiple hosts space separated) + host = "ldap" + # Default port is 389 or 636 if use_ssl = true + port = 389 + # Set to true if ldap server supports TLS + use_ssl = false + # Set to true if connect ldap server with STARTTLS pattern (create connection in insecure, then upgrade to secure connection with TLS) + start_tls = false + # set to true if you want to skip ssl cert validation + ssl_skip_verify = false + # set to the path to your root CA certificate or leave unset to use system defaults + # root_ca_cert = "/path/to/certificate.crt" + # Authentication against LDAP servers requiring client certificates + # client_cert = "/path/to/client.crt" + # client_key = "/path/to/client.key" + + # Search user bind dn + bind_dn = "cn=admin,dc=example,dc=org" + # Search user bind password + # If the password contains # or ; you have to wrap it with triple quotes. Ex """#password;""" + bind_password = 'admin' + + # User search filter, for example "(cn=%s)" or "(sAMAccountName=%s)" or "(uid=%s)" + # Allow login from email or username, example "(|(sAMAccountName=%s)(userPrincipalName=%s))" + search_filter = "(uid=%s)" + + # An array of base dns to search through + search_base_dns = ["ou=People,dc=support,dc=example,dc=org"] + + # group_search_filter = "(&(objectClass=posixGroup)(memberUid=%s))" + # group_search_filter_user_attribute = "distinguishedName" + # group_search_base_dns = ["ou=groups,dc=grafana,dc=org"] + + # Specify names of the ldap attributes your ldap uses + [servers.attributes] + name = "givenName" + surname = "sn" + username = "cn" + member_of = "memberOf" + email = "email" +``` + +Create the ConfigMap into the cluster and deploy the Grafana Helm Chart using the existing ConfigMap and the following parameters: + +```console +ldap.enabled=true +ldap.configMapName=ldap-config +ldap.allowSignUp=true +``` + +### Supporting HA (High Availability) + +To support HA Grafana just need an external database where store dashboards, users and other persistent data. +To configure the external database provide a configuration file containing the [database section](https://grafana.com/docs/installation/configuration/#database) + +More information about Grafana HA [here](https://grafana.com/docs/tutorials/ha_setup/) + +## Persistence + +The [Bitnami Grafana](https://github.com/bitnami/bitnami-docker-grafana) image stores the Grafana data and configurations at the `/opt/bitnami/grafana/data` path of the container. + +Persistent Volume Claims are used to keep the data across deployments. This is known to work in GCE, AWS, and minikube. +See the [Parameters](#parameters) section to configure the PVC or to disable persistence. diff --git a/helm/grafana/templates/NOTES.txt b/helm/grafana/templates/NOTES.txt new file mode 100755 index 000000000..c16259c23 --- /dev/null +++ b/helm/grafana/templates/NOTES.txt @@ -0,0 +1,37 @@ +** Please be patient while the chart is being deployed ** + +1. Get the application URL by running these commands: + +{{- if .Values.ingress.enabled }} +{{- range $host := .Values.ingress.hosts }} + {{- range .paths }} + http{{ if $.Values.ingress.tls }}s{{ end }}://{{ $host.name }}{{ . }} + {{- end }} +{{- end }} +{{- else if contains "NodePort" .Values.service.type }} + export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ include "grafana.fullname" . }}) + export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}") + echo http://$NODE_IP:$NODE_PORT +{{- else if contains "LoadBalancer" .Values.service.type }} + NOTE: It may take a few minutes for the LoadBalancer IP to be available. + You can watch the status of by running 'kubectl get --namespace {{ .Release.Namespace }} svc -w {{ include "grafana.fullname" . }}' + export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ include "grafana.fullname" . }} -o jsonpath='{.status.loadBalancer.ingress[0].ip}') + echo http://$SERVICE_IP:{{ .Values.service.port }} +{{- else if contains "ClusterIP" .Values.service.type }} + echo "Browse to http://127.0.0.1:8080" + kubectl port-forward svc/{{ include "grafana.fullname" . }} 8080:{{ .Values.service.port }} & +{{- end }} + +2. Get the admin credentials: + + echo "User: {{ .Values.admin.user }}" + echo "Password: $(kubectl get secret {{ include "grafana.fullname" . }}-secret --namespace {{ .Release.Namespace }} -o jsonpath="{.data.GF_SECURITY_ADMIN_PASSWORD}" | base64 --decode)" + +{{- if and (contains "bitnami/" .Values.image.repository) (not (.Values.image.tag | toString | regexFind "-r\\d+$|sha256:")) }} + +WARNING: Rolling tag detected ({{ .Values.image.repository }}:{{ .Values.image.tag }}), please note that it is strongly recommended to avoid using rolling tags in a production environment. ++info https://docs.bitnami.com/containers/how-to/understand-rolling-tags-containers/ + +{{- end }} + +{{ include "grafana.validateValues" . }} diff --git a/helm/grafana/templates/_helpers.tpl b/helm/grafana/templates/_helpers.tpl new file mode 100755 index 000000000..a976148cb --- /dev/null +++ b/helm/grafana/templates/_helpers.tpl @@ -0,0 +1,195 @@ +{{/* vim: set filetype=mustache: */}} +{{/* +Expand the name of the chart. +*/}} +{{- define "grafana.name" -}} +{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}} +{{- end -}} + +{{/* +Return the proper Grafana image name +*/}} +{{- define "grafana.image" -}} +{{- $registryName := .Values.image.registry -}} +{{- $repositoryName := .Values.image.repository -}} +{{- $tag := .Values.image.tag | toString -}} +{{/* +Helm 2.11 supports the assignment of a value to a variable defined in a different scope, +but Helm 2.9 and 2.10 doesn't support it, so we need to implement this if-else logic. +Also, we can't use a single if because lazy evaluation is not an option +*/}} +{{- if .Values.global }} + {{- if .Values.global.imageRegistry }} + {{- printf "%s/%s:%s" .Values.global.imageRegistry $repositoryName $tag -}} + {{- else -}} + {{- printf "%s/%s:%s" $registryName $repositoryName $tag -}} + {{- end -}} +{{- else -}} + {{- printf "%s/%s:%s" $registryName $repositoryName $tag -}} +{{- end -}} +{{- end -}} + +{{/* +Common labels +*/}} +{{- define "grafana.labels" -}} +app.kubernetes.io/name: {{ include "grafana.name" . }} +helm.sh/chart: {{ include "grafana.chart" . }} +app.kubernetes.io/instance: {{ .Release.Name }} +app.kubernetes.io/managed-by: {{ .Release.Service }} +{{- end -}} + +{{/* +Labels to use on deploy.spec.selector.matchLabels and svc.spec.selector +*/}} +{{- define "grafana.matchLabels" -}} +app.kubernetes.io/name: {{ include "grafana.name" . }} +app.kubernetes.io/instance: {{ .Release.Name }} +{{- end -}} + +{{/* +Create a default fully qualified app name. +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +If release name contains chart name it will be used as a full name. +*/}} +{{- define "grafana.fullname" -}} +{{- if .Values.fullnameOverride -}} +{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" -}} +{{- else -}} +{{- $name := default .Chart.Name .Values.nameOverride -}} +{{- if contains $name .Release.Name -}} +{{- .Release.Name | trunc 63 | trimSuffix "-" -}} +{{- else -}} +{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}} +{{- end -}} +{{- end -}} +{{- end -}} + +{{/* +Create chart name and version as used by the chart label. +*/}} +{{- define "grafana.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}} +{{- end -}} + +{{/* +Return the proper Docker Image Registry Secret Names +*/}} +{{- define "grafana.imagePullSecrets" -}} +{{/* +Helm 2.11 supports the assignment of a value to a variable defined in a different scope, +but Helm 2.9 and 2.10 does not support it, so we need to implement this if-else logic. +Also, we can not use a single if because lazy evaluation is not an option +*/}} +{{- if .Values.global }} +{{- if .Values.global.imagePullSecrets }} +imagePullSecrets: +{{- range .Values.global.imagePullSecrets }} + - name: {{ . }} +{{- end }} +{{- else if .Values.image.pullSecrets }} +imagePullSecrets: +{{- range .Values.image.pullSecrets }} + - name: {{ . }} +{{- end }} +{{- end -}} +{{- else if .Values.image.pullSecrets }} +imagePullSecrets: +{{- range .Values.image.pullSecrets }} + - name: {{ . }} +{{- end }} +{{- end -}} +{{- end -}} + +{{/* +Return the proper Storage Class +*/}} +{{- define "grafana.storageClass" -}} +{{/* +Helm 2.11 supports the assignment of a value to a variable defined in a different scope, +but Helm 2.9 and 2.10 does not support it, so we need to implement this if-else logic. +*/}} +{{- if .Values.global -}} + {{- if .Values.global.storageClass -}} + {{- if (eq "-" .Values.global.storageClass) -}} + {{- printf "storageClassName: \"\"" -}} + {{- else }} + {{- printf "storageClassName: %s" .Values.global.storageClass -}} + {{- end -}} + {{- else -}} + {{- if .Values.persistence.storageClass -}} + {{- if (eq "-" .Values.persistence.storageClass) -}} + {{- printf "storageClassName: \"\"" -}} + {{- else }} + {{- printf "storageClassName: %s" .Values.persistence.storageClass -}} + {{- end -}} + {{- end -}} + {{- end -}} +{{- else -}} + {{- if .Values.persistence.storageClass -}} + {{- if (eq "-" .Values.persistence.storageClass) -}} + {{- printf "storageClassName: \"\"" -}} + {{- else }} + {{- printf "storageClassName: %s" .Values.persistence.storageClass -}} + {{- end -}} + {{- end -}} +{{- end -}} +{{- end -}} + +{{/* +Validate values for Grafana. +*/}} +{{- define "grafana.validateValues" -}} +{{- $messages := list -}} +{{- $messages := append $messages (include "grafana.validateValues.database" .) -}} +{{- $messages := append $messages (include "grafana.validateValues.configmapsOrSecrets" .) -}} +{{- $messages := without $messages "" -}} +{{- $message := join "\n" $messages -}} + +{{- if $message -}} +{{- printf "\nVALUES VALIDATION:\n%s" $message -}} +{{- end -}} +{{- end -}} + +{{/* +Function to validate the external database +*/}} +{{- define "grafana.validateValues.database" -}} +{{- $replicaCount := int .Values.replicaCount }} +{{- if gt $replicaCount 1 -}} +WARNING: Using more than one replica requires using an external database to share data between Grafana instances. + By default Grafana uses an internal sqlite3 per instance but you can configure an external MySQL or PostgreSQL. + Please, ensure you provide a configuration file configuring the external database to share data between replicas. +{{- end -}} +{{- end -}} + +{{/* +Function to validate grafana confirmaps and secrets +*/}} +{{- define "grafana.validateValues.configmapsOrSecrets" -}} +{{- if and .Values.config.grafanaIniSecret .Values.config.grafanaIniConfigMap -}} +CONFLICT: You specified both config.grafanaIniSecret and config.grafanaIniConfigMap. Please set only one of them. +{{- end -}} +{{- if and .Values.config.customIniSecret .Values.config.customIniConfigMap -}} +CONFLICT: You specified both config.customIniSecret and config.customIniConfigMap. Please set only one of them. +{{- end -}} +{{- if and .Values.config.useGrafanaIniFile (not .Values.config.grafanaIniSecret) (not .Values.config.grafanaIniConfigMap) -}} +WARNING: You enabled config.useGrafanaIniFile but did not specify config.grafanaIniSecret nor config.grafanaIniConfigMap +{{- end -}} +{{- if and .Values.config.useCustomIniFile (not .Values.config.customIniSecret) (not .Values.config.customIniConfigMap) -}} +WARNING: You enabled config.useCustomIniFile but did not specify config.customIniSecret nor config.customIniConfigMap +{{- end -}} +{{- end -}} + +{{/* +Renders a value that contains template. +Usage: +{{ include "grafana.tplValue" ( dict "value" .Values.path.to.the.Value "context" $) }} +*/}} +{{- define "grafana.tplValue" -}} + {{- if typeIs "string" .value }} + {{- tpl .value .context }} + {{- else }} + {{- tpl (.value | toYaml) .context }} + {{- end }} +{{- end -}} diff --git a/helm/grafana/templates/configmap.yaml b/helm/grafana/templates/configmap.yaml new file mode 100755 index 000000000..ad36539b7 --- /dev/null +++ b/helm/grafana/templates/configmap.yaml @@ -0,0 +1,18 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ include "grafana.fullname" . }}-envvars + labels: {{- include "grafana.labels" . | nindent 4 }} +data: + GF_SECURITY_ADMIN_USER: {{ .Values.admin.user | quote }} + {{- if .Values.plugins }} + GF_INSTALL_PLUGINS: {{ .Values.plugins | quote }} + {{- end }} + GF_PATHS_PLUGINS: "/opt/bitnami/grafana/data/plugins" + GF_AUTH_LDAP_ENABLED: {{ .Values.ldap.enabled | quote }} + GF_AUTH_LDAP_CONFIG_FILE: "/opt/bitnami/grafana/conf/ldap.toml" + GF_AUTH_LDAP_ALLOW_SIGN_UP: {{ .Values.ldap.allowSignUp | quote }} + GF_PATHS_PROVISIONING: "/opt/bitnami/grafana/conf/provisioning" + GF_PATHS_CONFIG: "/opt/bitnami/grafana/conf/grafana.ini" + GF_PATHS_DATA: "/opt/bitnami/grafana/data" + GF_PATHS_LOGS: "/opt/bitnami/grafana/logs" diff --git a/helm/grafana/templates/dashboard-provider.yaml b/helm/grafana/templates/dashboard-provider.yaml new file mode 100755 index 000000000..86b10d394 --- /dev/null +++ b/helm/grafana/templates/dashboard-provider.yaml @@ -0,0 +1,31 @@ +{{- if .Values.dashboardsProvider.enabled }} +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ include "grafana.fullname" . }}-provider + labels: {{- include "grafana.labels" . | nindent 4 }} +data: + default-provider.yaml: |- + apiVersion: 1 + + providers: + # an unique provider name + - name: 'default-provider' + # org id. will default to orgId 1 if not specified + orgId: 1 + # name of the dashboard folder. Required + folder: dashboards + # folder UID. will be automatically generated if not specified + folderUid: '' + # provider type. Required + type: file + # disable dashboard deletion + disableDeletion: false + # enable dashboard editing + editable: true + # how often Grafana will scan for changed dashboards + updateIntervalSeconds: 10 + options: + # path to dashboard files on disk. Required + path: /opt/bitnami/grafana/dashboards +{{- end }} diff --git a/helm/grafana/templates/deployment.yaml b/helm/grafana/templates/deployment.yaml new file mode 100755 index 000000000..85d1fa6a2 --- /dev/null +++ b/helm/grafana/templates/deployment.yaml @@ -0,0 +1,187 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ include "grafana.fullname" . }} + labels: {{- include "grafana.labels" . | nindent 4 }} +spec: + replicas: {{ .Values.replicaCount }} + selector: + matchLabels: {{- include "grafana.matchLabels" . | nindent 6 }} + {{- if .Values.updateStrategy }} + strategy: {{ toYaml .Values.updateStrategy | nindent 4 }} + {{- end }} + template: + metadata: + labels: {{- include "grafana.labels" . | nindent 8 }} + annotations: + checksum/config: {{ include (print $.Template.BasePath "/configmap.yaml") . | sha256sum }} + checksum/dashboard-provider: {{ include (print $.Template.BasePath "/dashboard-provider.yaml") . | sha256sum }} +{{- if .Values.podAnnotations }} +{{ toYaml .Values.podAnnotations | indent 8 }} +{{- end }} + spec: +{{- include "grafana.imagePullSecrets" . | nindent 6 }} + {{- if .Values.schedulerName }} + schedulerName: {{ .Values.schedulerName | quote }} + {{- end }} + {{- if .Values.affinity }} + affinity: {{- include "grafana.tplValue" (dict "value" .Values.affinity "context" $) | nindent 8 }} + {{- end }} + {{- if .Values.nodeSelector }} + nodeSelector: {{- include "grafana.tplValue" (dict "value" .Values.nodeSelector "context" $) | nindent 8 }} + {{- end }} + {{- if .Values.tolerations }} + tolerations: {{- include "grafana.tplValue" (dict "value" .Values.tolerations "context" $) | nindent 8 }} + {{- end }} + {{- if .Values.securityContext.enabled }} + securityContext: + runAsUser: {{ .Values.securityContext.runAsUser }} + fsGroup: {{ .Values.securityContext.fsGroup }} + runAsNonRoot: {{ .Values.securityContext.runAsNonRoot }} + {{- end }} + containers: + - name: {{ .Chart.Name }} + image: {{ template "grafana.image" . }} + imagePullPolicy: {{ .Values.image.pullPolicy }} + envFrom: + - configMapRef: + name: {{ include "grafana.fullname" . }}-envvars + - secretRef: + name: {{ include "grafana.fullname" . }}-secret + env: + {{- if .Values.smtp.existingSecret }} + - name: GF_SMTP_USER + valueFrom: + secretKeyRef: + name: {{ .Values.smtp.existingSecret }} + key: {{ .Values.smtp.existingSecretUserKey }} + - name: GF_SMTP_PASSWORD + valueFrom: + secretKeyRef: + name: {{ .Values.smtp.existingSecret }} + key: {{ .Values.smtp.existingSecretPasswordKey }} + {{- end }} + {{- if .Values.extraEnvVars }} + {{- toYaml .Values.extraEnvVars | nindent 12 }} + {{- end }} + volumeMounts: + {{- if .Values.config.useGrafanaIniFile }} + - name: grafana-ini + mountPath: /opt/bitnami/grafana/conf/grafana.ini + subPath: grafana.ini + {{- end }} + {{- if .Values.config.useCustomIniFile }} + - name: custom-ini + mountPath: /opt/bitnami/grafana/conf/custom.ini + subPath: custom.ini + {{- end }} + - name: data + mountPath: /opt/bitnami/grafana/data + {{- if .Values.dashboardsProvider.enabled }} + - name: dashboards-provider + mountPath: /opt/bitnami/grafana/conf/provisioning/dashboards + {{- end }} + {{- range .Values.dashboardsConfigMaps }} + - name: {{ .configMapName }} + mountPath: /opt/bitnami/grafana/dashboards/{{ .fileName }} + subPath: {{ .fileName }} + {{- end }} + {{- if .Values.datasources.secretName }} + - name: datasources + mountPath: /opt/bitnami/grafana/conf/provisioning/datasources + {{- end }} + {{- if and .Values.ldap.enabled .Values.ldap.configMapName }} + - name: ldap + mountPath: /opt/bitnami/grafana/conf/ldap.toml + subPath: ldap.toml + {{- end }} + {{- range .Values.extraConfigmaps }} + - name: {{ .name }} + mountPath: {{ .mountPath }} + subPath: {{ .subPath | default "" }} + readOnly: {{ .readOnly }} + {{- end }} + ports: + - name: dashboard + containerPort: 3000 + protocol: TCP + {{- if .Values.livenessProbe.enabled }} + livenessProbe: + httpGet: + path: / + port: dashboard + initialDelaySeconds: {{ .Values.livenessProbe.initialDelaySeconds }} + periodSeconds: {{ .Values.livenessProbe.periodSeconds }} + timeoutSeconds: {{ .Values.livenessProbe.timeoutSeconds }} + successThreshold: {{ .Values.livenessProbe.successThreshold }} + failureThreshold: {{ .Values.livenessProbe.failureThreshold }} + {{- end }} + {{- if .Values.readinessProbe.enabled }} + readinessProbe: + httpGet: + path: / + port: dashboard + initialDelaySeconds: {{ .Values.readinessProbe.initialDelaySeconds }} + periodSeconds: {{ .Values.readinessProbe.periodSeconds }} + timeoutSeconds: {{ .Values.readinessProbe.timeoutSeconds }} + successThreshold: {{ .Values.readinessProbe.successThreshold }} + failureThreshold: {{ .Values.readinessProbe.failureThreshold }} + {{- end }} + {{- if .Values.resources }} + resources: {{- toYaml .Values.resources | nindent 12 }} + {{- end }} + volumes: + - name: data + {{- if .Values.persistence.enabled }} + persistentVolumeClaim: + claimName: {{ .Values.persistence.existingClaim | default (include "grafana.fullname" .) }} + {{- end }} + {{- if and .Values.ldap.enabled .Values.ldap.configMapName }} + - name: ldap + configMap: + name: {{ .Values.ldap.configMapName }} + {{- end }} + {{- if .Values.dashboardsProvider.enabled }} + - name: dashboards-provider + configMap: + {{- if .Values.dashboardsProvider.configMapName }} + name: {{ .Values.dashboardsProvider.configMapName }} + {{- else }} + name: {{ include "grafana.fullname" . }}-provider + {{- end }} + {{- end }} + {{- range .Values.dashboardsConfigMaps }} + - name: {{ .configMapName }} + configMap: + name: {{ .configMapName }} + {{- end }} + {{- if .Values.datasources.secretName }} + - name: datasources + secret: + secretName: {{ .Values.datasources.secretName }} + {{- end }} + {{- if .Values.config.useGrafanaIniFile }} + - name: grafana-ini + {{- if .Values.config.grafanaIniConfigMap }} + configMap: + name: {{ .Values.config.grafanaIniConfigMap }} + {{- else if .Values.config.grafanaIniSecret }} + secret: + secretName: {{ .Values.config.grafanaIniSecret }} + {{- end }} + {{- end }} + {{- if .Values.config.useCustomIniFile }} + - name: custom-ini + {{- if .Values.config.customIniConfigMap }} + configMap: + name: {{ .Values.config.customIniConfigMap }} + {{- else if .Values.config.customIniSecret }} + secret: + secretName: {{ .Values.config.customIniSecret }} + {{- end }} + {{- end }} + {{- range .Values.extraConfigmaps }} + - name: {{ .name }} + configMap: + name: {{ .name }} + {{- end }} diff --git a/helm/grafana/templates/ingress.yaml b/helm/grafana/templates/ingress.yaml new file mode 100755 index 000000000..f3ee85058 --- /dev/null +++ b/helm/grafana/templates/ingress.yaml @@ -0,0 +1,42 @@ +{{- if .Values.ingress.enabled -}} +apiVersion: extensions/v1beta1 +kind: Ingress +metadata: + name: {{ include "grafana.fullname" . }} + labels: {{- include "grafana.labels" . | nindent 4 }} + annotations: + {{- if .Values.ingress.certManager }} + kubernetes.io/tls-acme: "true" + {{- end }} + {{- with .Values.ingress.annotations }} + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + rules: + {{- range .Values.ingress.hosts }} + - host: {{ .name | quote }} + http: + paths: + {{- $paths := default (list "/") .paths }} + {{- range $paths }} + - path: {{ . }} + backend: + serviceName: {{ include "grafana.fullname" $ }} + servicePort: http + {{- end }} + {{- end }} + tls: + {{- range .Values.ingress.hosts }} + {{- if .tls }} + - hosts: + {{- if .tlsHosts }} + {{- range $host := .tlsHosts }} + - {{ $host }} + {{- end }} + {{- else }} + - {{ .name }} + {{- end }} + secretName: {{ .tlsSecret }} + {{- end }} +{{- end }} +{{- end }} diff --git a/helm/grafana/templates/pvc.yaml b/helm/grafana/templates/pvc.yaml new file mode 100755 index 000000000..8f2cbb3e1 --- /dev/null +++ b/helm/grafana/templates/pvc.yaml @@ -0,0 +1,14 @@ +{{- if and .Values.persistence.enabled (not .Values.persistence.existingClaim) }} +kind: PersistentVolumeClaim +apiVersion: v1 +metadata: + name: {{ template "grafana.fullname" . }} + labels: {{- include "grafana.labels" . | nindent 4 }} +spec: + accessModes: + - {{ .Values.persistence.accessMode | quote }} + resources: + requests: + storage: {{ .Values.persistence.size | quote }} + {{ include "grafana.storageClass" . }} +{{- end -}} diff --git a/helm/grafana/templates/secret.yaml b/helm/grafana/templates/secret.yaml new file mode 100755 index 000000000..ee947106a --- /dev/null +++ b/helm/grafana/templates/secret.yaml @@ -0,0 +1,18 @@ +apiVersion: v1 +kind: Secret +metadata: + name: {{ template "grafana.fullname" . }}-secret + labels: {{- include "grafana.labels" . | nindent 4 }} +type: Opaque +data: + {{- if .Values.admin.password }} + GF_SECURITY_ADMIN_PASSWORD: {{ .Values.admin.password | b64enc | quote }} + {{- else }} + GF_SECURITY_ADMIN_PASSWORD: {{ randAlphaNum 10 | b64enc | quote }} + {{- end }} + {{- if and .Values.smtp.enabled (not .Values.smtp.existingSecret) }} + {{- if and .Values.smtp.user .Values.smtp.password }} + GF_SMTP_USER: {{ .Values.smtp.user | b64enc | quote }} + GF_SMTP_PASSWORD: {{ .Values.smtp.password | b64enc | quote }} + {{- end }} + {{- end }} diff --git a/helm/grafana/templates/service.yaml b/helm/grafana/templates/service.yaml new file mode 100755 index 000000000..8b9909d13 --- /dev/null +++ b/helm/grafana/templates/service.yaml @@ -0,0 +1,30 @@ +apiVersion: v1 +kind: Service +metadata: + name: {{ include "grafana.fullname" . }} + labels: {{- include "grafana.labels" . | nindent 4 }} + {{- if or (and .Values.metrics.enabled .Values.metrics.service.annotations) .Values.service.annotations }} + annotations: + {{- if and .Values.metrics.enabled .Values.metrics.service.annotations }} + {{- include "grafana.tplValue" (dict "value" .Values.metrics.service.annotations "context" $) | nindent 4 }} + {{- end }} + {{- if .Values.service.annotations }} + {{- include "grafana.tplValue" (dict "value" .Values.service.annotations "context" $) | nindent 4 }} + {{- end }} + {{- end }} +spec: + type: {{ .Values.service.type }} + {{- if and (eq .Values.service.type "LoadBalancer") (not (empty .Values.service.loadBalancerIP)) }} + loadBalancerIP: {{ .Values.service.loadBalancerIP }} + {{- end }} + ports: + - port: {{ .Values.service.port }} + targetPort: dashboard + protocol: TCP + name: http + {{- if and (or (eq .Values.service.type "NodePort") (eq .Values.service.type "LoadBalancer")) (not (empty .Values.service.nodePort)) }} + nodePort: {{ .Values.service.nodePort }} + {{- else if eq .Values.service.type "ClusterIP" }} + nodePort: null + {{- end }} + selector: {{- include "grafana.matchLabels" . | nindent 4 }} diff --git a/helm/grafana/templates/servicemonitor.yaml b/helm/grafana/templates/servicemonitor.yaml new file mode 100755 index 000000000..125177752 --- /dev/null +++ b/helm/grafana/templates/servicemonitor.yaml @@ -0,0 +1,28 @@ +{{- if and .Values.metrics.enabled .Values.metrics.serviceMonitor.enabled }} +apiVersion: monitoring.coreos.com/v1 +kind: ServiceMonitor +metadata: + name: {{ include "grafana.fullname" . }} + {{- if .Values.metrics.serviceMonitor.namespace }} + namespace: {{ .Values.metrics.serviceMonitor.namespace }} + {{- end }} + labels: {{- include "grafana.labels" . | nindent 4 }} + {{- range $key, $value := .Values.metrics.serviceMonitor.selector }} + {{ $key }}: {{ $value | quote }} + {{- end }} +spec: + selector: + matchLabels: {{ include "grafana.matchLabels" . | nindent 6 }} + endpoints: + - port: http + path: "/metrics" + {{- if .Values.metrics.serviceMonitor.interval }} + interval: {{ .Values.metrics.serviceMonitor.interval }} + {{- end }} + {{- if .Values.metrics.serviceMonitor.scrapeTimeout }} + scrapeTimeout: {{ .Values.metrics.serviceMonitor.scrapeTimeout }} + {{- end }} + namespaceSelector: + matchNames: + - {{ .Release.Namespace }} +{{- end }} diff --git a/helm/grafana/values-production.yaml b/helm/grafana/values-production.yaml new file mode 100755 index 000000000..2d67a8c4a --- /dev/null +++ b/helm/grafana/values-production.yaml @@ -0,0 +1,327 @@ +## Global Docker image parameters +## Please, note that this will override the image parameters, including dependencies, configured to use the global value +## Current available global Docker image parameters: imageRegistry, imagePullSecrets and storageClass +## +# global: +# imageRegistry: myRegistryName +# imagePullSecrets: +# - myRegistryKeySecretName +# storageClass: myStorageClass + +## Bitnami Grafana image version +## ref: https://hub.docker.com/r/bitnami/grafana/tags/ +## +image: + registry: docker.io + repository: bitnami/grafana + tag: 6.5.2-debian-9-r0 + ## Specify a imagePullPolicy + ## Defaults to 'Always' if image tag is 'latest', else set to 'IfNotPresent' + ## ref: http://kubernetes.io/docs/user-guide/images/#pre-pulling-images + ## + pullPolicy: IfNotPresent + ## Optionally specify an array of imagePullSecrets. + ## Secrets must be manually created in the namespace. + ## ref: https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry/ + ## + # pullSecrets: + # - myRegistryKeySecretName + +## String to partially override grafana.fullname template (will maintain the release name) +## +# nameOverride: + +## String to fully override grafana.fullname template +## +# fullnameOverride: + +## Number of grafana Pod replicas +## +replicaCount: 1 + +## Set up update strategy for Grafana installation. Set to Recreate if you use persistent volume that cannot be mounted by more than one pods to makesure the pods is destroyed first. +## ref: https://kubernetes.io/docs/concepts/workloads/controllers/deployment/#strategy +## Example: +# updateStrategy: +# type: RollingUpdate +# rollingUpdate: +# maxSurge: 25% +# maxUnavailable: 25% +updateStrategy: + type: RollingUpdate + +## Use an alternate scheduler, e.g. "stork". +## ref: https://kubernetes.io/docs/tasks/administer-cluster/configure-multiple-schedulers/ +## +# schedulerName: + +## Admin access configuration +## If a password is not provided a random password will be generated +## +admin: + user: "admin" + # password: + +## SMTP configuration +## Use either a secret container the credentials or set the credentials through command line. +## +smtp: + enabled: false + # Provide credentials by command line + user: user + password: password + ## Existing secret with SMTP credentials (user and password) + # existingSecret: + ## User and password key on the existing secret ( "user" and "password" by default ) + existingSecretUserKey: user + existingSecretPasswordKey: password + +## Grafana plugins that will be installed +## Specify plugins as a list separated by commas ( you will need to scape them when specifying from command line ) +## Example: +## plugins: grafana-kubernetes-app,grafana-example-app +## +# plugins: + +## Ldap configuration for Grafana +## +ldap: + enabled: false + allowSignUp: false + ## configMap with LDAP configuration file (ldap.toml) + # configMapName: + +## An array to add extra env vars +## For example: +## extraEnvVars: +## - name: GF_DEFAULT_INSTANCE_NAME +## value: my-instance +## +extraEnvVars: {} + +## An array to add extra configmaps: +## For example: +## extraConfigmaps: +## - name: myconfigmap +## mountPath: /opt/bitnami/desired-path +## subPath: file-name.extension (optional) +## readOnly: true +## +extraConfigmaps: {} + +## Parameters to override the default grafana.ini and custom.ini files. +## It is needed to create a configmap or a secret containing the grafana.ini and custom.ini files. +## +config: + useGrafanaIniFile: false + grafanaIniConfigMap: + grafanaIniSecret: + useCustomIniFile: false + customIniConfigMap: + customIniSecret: + +## Create dasboard provider to load dashboards, a default one is created to load +## dashboards from "/opt/bitnami/grafana/dashboards" +## +dashboardsProvider: + enabled: false + ## ConfigMap with a custom provider file. + ## Important to set the Path to "/opt/bitnami/grafana/dashboards" + configMapName: + +## Create dashboards from a custom configMap that contains the file. +## They will be mounted by the default dashboard provider if it is enabled +## Use an array with the configMap names. +## Example: +## dashboardsConfigMaps: +## - configMapName: mydashboard +## fileName: mydashboard.json +## - configMapName: myotherdashboard +## fileName: myotherdashboard.json +## +dashboardsConfigMaps: [] + +## Create datasources from a custom secret +## The secret must contain the files +## +datasources: + secretName: + +## Enable persistence using Persistent Volume Claims +## ref: http://kubernetes.io/docs/user-guide/persistent-volumes/ +## +## +persistence: + enabled: true + ## wordpress data Persistent Volume Storage Class + ## If defined, storageClassName: + ## If set to "-", storageClassName: "", which disables dynamic provisioning + ## If undefined (the default) or set to null, no storageClassName spec is + ## set, choosing the default provisioner. (gp2 on AWS, standard on + ## GKE, AWS & OpenStack) + ## + # storageClass: "-" + ## + ## If you want to reuse an existing claim, you can pass the name of the PVC using + ## the existingClaim variable + # existingClaim: your-claim + accessMode: ReadWriteOnce + size: 10Gi + +## Grafana containers' liveness and readiness probes +## ref: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle/#container-probes +## +livenessProbe: + enabled: true + initialDelaySeconds: 120 + periodSeconds: 10 + timeoutSeconds: 5 + failureThreshold: 6 + successThreshold: 1 +readinessProbe: + enabled: true + initialDelaySeconds: 30 + periodSeconds: 10 + timeoutSeconds: 5 + failureThreshold: 6 + successThreshold: 1 + +## Service parameters +## +service: + ## K8s service type + ## + type: ClusterIP + ## Grafana service port + ## + port: 3000 + ## Specify the nodePort value for the LoadBalancer and NodePort service types. + ## ref: https://kubernetes.io/docs/concepts/services-networking/service/#type-nodeport + ## + # nodePort: + ## loadBalancerIP for the PrestaShop Service (optional, cloud specific) + ## ref: http://kubernetes.io/docs/user-guide/services/#type-loadbalancer + ## + # loadBalancerIP: + ## Provide any additional annotations which may be required. This can be used to + ## set the LoadBalancer service type to internal only. + ## ref: https://kubernetes.io/docs/concepts/services-networking/service/#internal-load-balancer + ## + annotations: {} + +## Configure the ingress resource that allows you to access the +## Grafana web. Set up the URL +## ref: http://kubernetes.io/docs/user-guide/ingress/ +## +ingress: + ## Set to true to enable ingress record generation + enabled: true + + ## Set this to true in order to add the corresponding annotations for cert-manager + certManager: false + + ## Ingress annotations done as key:value pairs + ## For a full list of possible ingress annotations, please see + ## ref: https://github.com/kubernetes/ingress-nginx/blob/master/docs/user-guide/nginx-configuration/annotations.md + ## + ## If tls is set to true, annotation ingress.kubernetes.io/secure-backends: "true" will automatically be set + ## If certManager is set to true, annotation kubernetes.io/tls-acme: "true" will automatically be set + # annotations: + # kubernetes.io/ingress.class: nginx + + ## The list of hostnames to be covered with this ingress record. + ## Most likely this will be just one host, but in the event more hosts are needed, this is an array + hosts: + - name: grafana.local + paths: ["/"] + + ## Set this to true in order to enable TLS on the ingress record + tls: false + + ## Optionally specify the TLS hosts for the ingress record + ## Useful when the Ingress controller supports www-redirection + ## If not specified, the above host name will be used + # tlsHosts: + # - www.grafana.local + # - grafana.local + + ## If TLS is set to true, you must declare what secret will store the key/certificate for TLS + tlsSecret: grafana.local-tls + +## SecurityContext configuration +## +securityContext: + enabled: true + runAsUser: 1001 + fsGroup: 1001 + runAsNonRoot: true + +## Grafana containers' resource requests and limits +## ref: http://kubernetes.io/docs/user-guide/compute-resources/ +## +resources: + # We usually recommend not to specify default resources and to leave this as a conscious + # choice for the user. This also increases chances charts run on environments with little + # resources, such as Minikube. If you do want to specify resources, uncomment the following + # lines, adjust them as necessary, and remove the curly braces after 'resources:'. + limits: {} + # cpu: 500m + # memory: 1Gi + requests: {} + # cpu: 250m + # memory: 256Mi + +## Node labels for pod assignment +## Ref: https://kubernetes.io/docs/user-guide/node-selection/ +## +nodeSelector: {} + +## Tolerations for pod assignment +## Ref: https://kubernetes.io/docs/concepts/configuration/taint-and-toleration/ +## +tolerations: [] + +## Affinity for pod assignment +## Ref: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#affinity-and-anti-affinity +## +affinity: {} + +## Pod annotations +## ref: https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations/ +## +podAnnotations: {} + +## Prometheus metrics +## +metrics: + enabled: true + + ## Prometheus Operator ServiceMonitor configuration + ## + service: + annotations: + prometheus.io/scrape: "true" + prometheus.io/port: "3000" + prometheus.io/path: "/metrics" + + serviceMonitor: + enabled: false + ## Namespace in which Prometheus is running + ## + # namespace: monitoring + + ## Interval at which metrics should be scraped. + ## ref: https://github.com/coreos/prometheus-operator/blob/master/Documentation/api.md#endpoint + ## + # interval: 10s + + ## Timeout after which the scrape is ended + ## ref: https://github.com/coreos/prometheus-operator/blob/master/Documentation/api.md#endpoint + ## + # scrapeTimeout: 10s + + ## ServiceMonitor selector labels + ## ref: https://github.com/bitnami/charts/tree/master/bitnami/prometheus-operator#prometheus-configuration + ## + # selector: + # prometheus: my-prometheus diff --git a/helm/grafana/values.yaml b/helm/grafana/values.yaml new file mode 100755 index 000000000..038bd8143 --- /dev/null +++ b/helm/grafana/values.yaml @@ -0,0 +1,327 @@ +## Global Docker image parameters +## Please, note that this will override the image parameters, including dependencies, configured to use the global value +## Current available global Docker image parameters: imageRegistry, imagePullSecrets and storageClass +## +# global: +# imageRegistry: myRegistryName +# imagePullSecrets: +# - myRegistryKeySecretName +# storageClass: myStorageClass + +## Bitnami Grafana image version +## ref: https://hub.docker.com/r/bitnami/grafana/tags/ +## +image: + registry: docker.io + repository: bitnami/grafana + tag: 6.5.2-debian-9-r0 + ## Specify a imagePullPolicy + ## Defaults to 'Always' if image tag is 'latest', else set to 'IfNotPresent' + ## ref: http://kubernetes.io/docs/user-guide/images/#pre-pulling-images + ## + pullPolicy: IfNotPresent + ## Optionally specify an array of imagePullSecrets. + ## Secrets must be manually created in the namespace. + ## ref: https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry/ + ## + # pullSecrets: + # - myRegistryKeySecretName + +## String to partially override grafana.fullname template (will maintain the release name) +## +# nameOverride: + +## String to fully override grafana.fullname template +## +# fullnameOverride: + +## Number of grafana Pod replicas +## +replicaCount: 1 + +## Set up update strategy for Grafana installation. Set to Recreate if you use persistent volume that cannot be mounted by more than one pods to makesure the pods is destroyed first. +## ref: https://kubernetes.io/docs/concepts/workloads/controllers/deployment/#strategy +## Example: +# updateStrategy: +# type: RollingUpdate +# rollingUpdate: +# maxSurge: 25% +# maxUnavailable: 25% +updateStrategy: + type: RollingUpdate + +## Use an alternate scheduler, e.g. "stork". +## ref: https://kubernetes.io/docs/tasks/administer-cluster/configure-multiple-schedulers/ +## +# schedulerName: + +## Admin access configuration +## If a password is not provided a random password will be generated +## +admin: + user: "admin" + password: "admin" + +## SMTP configuration +## Use either a secret container the credentials or set the credentials through command line. +## +smtp: + enabled: false + # Provide credentials by command line + user: user + password: password + ## Existing secret with SMTP credentials (user and password) + # existingSecret: + ## User and password key on the existing secret ( "user" and "password" by default ) + existingSecretUserKey: user + existingSecretPasswordKey: password + +## Grafana plugins that will be installed +## Specify plugins as a list separated by commas ( you will need to scape them when specifying from command line ) +## Example: +## plugins: grafana-kubernetes-app,grafana-example-app +## +plugins: devopsprodigy-kubegraf-app + +## Ldap configuration for Grafana +## +ldap: + enabled: false + allowSignUp: false + ## configMap with LDAP configuration file (ldap.toml) + # configMapName: + +## An array to add extra env vars +## For example: +## extraEnvVars: +## - name: GF_DEFAULT_INSTANCE_NAME +## value: my-instance +## +extraEnvVars: {} + +## An array to add extra configmaps: +## For example: +## extraConfigmaps: +## - name: myconfigmap +## mountPath: /opt/bitnami/desired-path +## subPath: file-name.extension (optional) +## readOnly: true +## +extraConfigmaps: {} + +## Parameters to override the default grafana.ini and custom.ini files. +## It is needed to create a configmap or a secret containing the grafana.ini and custom.ini files. +## +config: + useGrafanaIniFile: false + grafanaIniConfigMap: + grafanaIniSecret: + useCustomIniFile: false + customIniConfigMap: + customIniSecret: + +## Create dasboard provider to load dashboards, a default one is created to load +## dashboards from "/opt/bitnami/grafana/dashboards" +## +dashboardsProvider: + enabled: false + ## ConfigMap with a custom provider file. + ## Important to set the Path to "/opt/bitnami/grafana/dashboards" + configMapName: + +## Create dashboards from a custom configMap that contains the file. +## They will be mounted by the default dashboard provider if it is enabled +## Use an array with the configMap names. +## Example: +## dashboardsConfigMaps: +## - configMapName: mydashboard +## fileName: mydashboard.json +## - configMapName: myotherdashboard +## fileName: myotherdashboard.json +## +dashboardsConfigMaps: [] + +## Create datasources from a custom secret +## The secret must contain the files +## +datasources: + secretName: + +## Enable persistence using Persistent Volume Claims +## ref: http://kubernetes.io/docs/user-guide/persistent-volumes/ +## +## +persistence: + enabled: true + ## wordpress data Persistent Volume Storage Class + ## If defined, storageClassName: + ## If set to "-", storageClassName: "", which disables dynamic provisioning + ## If undefined (the default) or set to null, no storageClassName spec is + ## set, choosing the default provisioner. (gp2 on AWS, standard on + ## GKE, AWS & OpenStack) + ## + # storageClass: "-" + ## + ## If you want to reuse an existing claim, you can pass the name of the PVC using + ## the existingClaim variable + # existingClaim: your-claim + accessMode: ReadWriteOnce + size: 10Gi + +## Grafana containers' liveness and readiness probes +## ref: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle/#container-probes +## +livenessProbe: + enabled: true + initialDelaySeconds: 120 + periodSeconds: 10 + timeoutSeconds: 5 + failureThreshold: 6 + successThreshold: 1 +readinessProbe: + enabled: true + initialDelaySeconds: 30 + periodSeconds: 10 + timeoutSeconds: 5 + failureThreshold: 6 + successThreshold: 1 + +## Service parameters +## +service: + ## K8s service type + ## + type: ClusterIP + ## Grafana service port + ## + port: 3000 + ## Specify the nodePort value for the LoadBalancer and NodePort service types. + ## ref: https://kubernetes.io/docs/concepts/services-networking/service/#type-nodeport + ## + # nodePort: + ## loadBalancerIP for the PrestaShop Service (optional, cloud specific) + ## ref: http://kubernetes.io/docs/user-guide/services/#type-loadbalancer + ## + # loadBalancerIP: + ## Provide any additional annotations which may be required. This can be used to + ## set the LoadBalancer service type to internal only. + ## ref: https://kubernetes.io/docs/concepts/services-networking/service/#internal-load-balancer + ## + annotations: {} + +## Configure the ingress resource that allows you to access the +## Grafana web. Set up the URL +## ref: http://kubernetes.io/docs/user-guide/ingress/ +## +ingress: + ## Set to true to enable ingress record generation + enabled: false + + ## Set this to true in order to add the corresponding annotations for cert-manager + certManager: false + + ## Ingress annotations done as key:value pairs + ## For a full list of possible ingress annotations, please see + ## ref: https://github.com/kubernetes/ingress-nginx/blob/master/docs/user-guide/nginx-configuration/annotations.md + ## + ## If tls is set to true, annotation ingress.kubernetes.io/secure-backends: "true" will automatically be set + ## If certManager is set to true, annotation kubernetes.io/tls-acme: "true" will automatically be set + # annotations: + # kubernetes.io/ingress.class: nginx + + ## The list of hostnames to be covered with this ingress record. + ## Most likely this will be just one host, but in the event more hosts are needed, this is an array + hosts: + - name: grafana.local + paths: ["/"] + + ## Set this to true in order to enable TLS on the ingress record + tls: false + + ## Optionally specify the TLS hosts for the ingress record + ## Useful when the Ingress controller supports www-redirection + ## If not specified, the above host name will be used + # tlsHosts: + # - www.grafana.local + # - grafana.local + + ## If TLS is set to true, you must declare what secret will store the key/certificate for TLS + tlsSecret: grafana.local-tls + +## SecurityContext configuration +## +securityContext: + enabled: true + runAsUser: 1001 + fsGroup: 1001 + runAsNonRoot: true + +## Grafana containers' resource requests and limits +## ref: http://kubernetes.io/docs/user-guide/compute-resources/ +## +resources: + # We usually recommend not to specify default resources and to leave this as a conscious + # choice for the user. This also increases chances charts run on environments with little + # resources, such as Minikube. If you do want to specify resources, uncomment the following + # lines, adjust them as necessary, and remove the curly braces after 'resources:'. + limits: {} + # cpu: 500m + # memory: 1Gi + requests: {} + # cpu: 250m + # memory: 256Mi + +## Node labels for pod assignment +## Ref: https://kubernetes.io/docs/user-guide/node-selection/ +## +nodeSelector: {} + +## Tolerations for pod assignment +## Ref: https://kubernetes.io/docs/concepts/configuration/taint-and-toleration/ +## +tolerations: [] + +## Affinity for pod assignment +## Ref: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#affinity-and-anti-affinity +## +affinity: {} + +## Pod annotations +## ref: https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations/ +## +podAnnotations: {} + +## Prometheus metrics +## +metrics: + enabled: false + + ## Prometheus Operator ServiceMonitor configuration + ## + service: + annotations: + prometheus.io/scrape: "true" + prometheus.io/port: "3000" + prometheus.io/path: "/metrics" + + serviceMonitor: + enabled: false + ## Namespace in which Prometheus is running + ## + # namespace: monitoring + + ## Interval at which metrics should be scraped. + ## ref: https://github.com/coreos/prometheus-operator/blob/master/Documentation/api.md#endpoint + ## + # interval: 10s + + ## Timeout after which the scrape is ended + ## ref: https://github.com/coreos/prometheus-operator/blob/master/Documentation/api.md#endpoint + ## + # scrapeTimeout: 10s + + ## ServiceMonitor selector labels + ## ref: https://github.com/bitnami/charts/tree/master/bitnami/prometheus-operator#prometheus-configuration + ## + # selector: + # prometheus: my-prometheus diff --git a/helm/mds/.helmignore b/helm/mds/.helmignore index d360ac197..fc93e74cd 100644 --- a/helm/mds/.helmignore +++ b/helm/mds/.helmignore @@ -21,4 +21,4 @@ .vscode/ tests/ # misc -tools/ +tools/ \ No newline at end of file diff --git a/helm/mds/Chart.yaml b/helm/mds/Chart.yaml index 0b7c57ada..b96f1f480 100644 --- a/helm/mds/Chart.yaml +++ b/helm/mds/Chart.yaml @@ -1,5 +1,5 @@ apiVersion: v1 -appVersion: "0.4.0" +appVersion: '0.4.0' description: Mobility Data Specification name: mds version: 0.4.0 diff --git a/helm/mds/requirements.lock b/helm/mds/requirements.lock index ee16fd2ca..82fca8579 100644 --- a/helm/mds/requirements.lock +++ b/helm/mds/requirements.lock @@ -1,9 +1,9 @@ dependencies: - name: redis repository: https://kubernetes-charts.storage.googleapis.com - version: 9.0.2 + version: 10.5.7 - name: postgresql repository: https://kubernetes-charts.storage.googleapis.com - version: 6.2.1 -digest: sha256:46befe87aa04bda44a3441c3307d2441cc29d2cb611f180a59778520c316f46f -generated: "2019-11-01T00:11:12.520391-07:00" + version: 8.6.4 +digest: sha256:064fe32ebc10dca2926cd5557958bac56a0d7095f678888d77fd995bfc64784e +generated: "2020-05-20T14:37:10.7595032-07:00" diff --git a/helm/mds/requirements.yaml b/helm/mds/requirements.yaml index a62d2493b..2df563e09 100644 --- a/helm/mds/requirements.yaml +++ b/helm/mds/requirements.yaml @@ -1,10 +1,10 @@ # !! File must end with empty line !! dependencies: - name: redis - version: 9.0.2 + version: 10.5.7 repository: alias:stable condition: redis.internal - name: postgresql repository: alias:stable - version: 6.2.1 + version: 8.6.4 condition: postgresql.internal diff --git a/helm/mds/templates/auth.yaml b/helm/mds/templates/auth.yaml index 1944d3f6c..627bdc0d7 100644 --- a/helm/mds/templates/auth.yaml +++ b/helm/mds/templates/auth.yaml @@ -1,17 +1,19 @@ {{- if .Values.jwt.enabled }} -{{- range $key, $api := .Values.apis }} +{{- range $name, $api := .Values.apis }} {{- if $api.enabled }} ---- +{{- if hasKey $api "pathPrefix" }} +{{- if (ne $name "mds-web-sockets") }} apiVersion: authentication.istio.io/v1alpha1 kind: Policy metadata: - name: {{ $key }}-auth + name: {{ $name }}-auth namespace: {{ $.Release.Namespace }} spec: targets: - - name: {{ $key }} + - name: {{ $name }} peers: - mtls: {} + originIsOptional: true origins: - jwt: audiences: @@ -25,6 +27,95 @@ spec: - exact: {{ $api.pathPrefix }}/health - exact: /health principalBinding: USE_ORIGIN +--- +{{- end }} +{{- end }} +{{- end }} +{{- end }} +{{- if .Values.jwt.bypassInternal }} +# This sets up outbound mTLS for intra-namespace requests +apiVersion: networking.istio.io/v1alpha3 +kind: DestinationRule +metadata: + name: default + namespace: {{ .Release.Namespace }} +spec: + host: {{ printf "*.%s.svc.cluster.local" .Release.Namespace | quote }} + trafficPolicy: + tls: + mode: ISTIO_MUTUAL +--- +apiVersion: rbac.istio.io/v1alpha1 +kind: ServiceRole +metadata: + name: mds-internal-service + namespace: {{ .Release.Namespace }} +spec: + rules: + - services: ["*"] + methods: ["*"] + constraints: + - key: "destination.metadata[namespace]" + values: + - {{ .Release.Namespace }} +--- +apiVersion: rbac.istio.io/v1alpha1 +kind: ServiceRoleBinding +metadata: + name: mds-internal-service + namespace: {{ .Release.Namespace }} +spec: + roleRef: + kind: ServiceRole + name: "mds-internal-service" + subjects: + - properties: + source.namespace: {{ .Release.Namespace | quote }} +--- +apiVersion: rbac.istio.io/v1alpha1 +kind: ServiceRole +metadata: + name: mds-external-service + namespace: {{ .Release.Namespace }} +spec: + rules: + - services: ["*"] + methods: ["*"] + constraints: + - key: "destination.metadata[namespace]" + values: + - {{ .Release.Namespace }} +--- +apiVersion: rbac.istio.io/v1alpha1 +kind: ServiceRoleBinding +metadata: + name: mds-external-service + namespace: {{ .Release.Namespace }} +spec: + roleRef: + kind: ServiceRole + name: mds-external-service + subjects: + {{- range .Values.jwt.audiences }} + # allow presenters of JWT tokens based one or more properties of the token + # (audience, claims, etc.) + # https://archive.istio.io/v1.3/docs/reference/config/authorization/constraints-and-properties/#supported-properties + - properties: + request.auth.audiences: {{ . | quote }} + request.auth.claims[iss]: {{ $.Values.jwt.issuer }} + {{- end }} +{{- if .Values.jwt.firstInstall }} +--- +# THIS MUST BE IN PLACE FOR JWT TO FUNCTION CORRECTLY +apiVersion: "rbac.istio.io/v1alpha1" +kind: ClusterRbacConfig +metadata: + name: default +spec: + mode: 'ON_WITH_INCLUSION' + inclusion: + namespaces: + - {{ .Release.Namespace }} +{{- end }} {{- end }} {{- end }} -{{- end }} \ No newline at end of file diff --git a/helm/mds/templates/autoscaler.yaml b/helm/mds/templates/autoscaler.yaml index e0b5d8429..3d701eefc 100644 --- a/helm/mds/templates/autoscaler.yaml +++ b/helm/mds/templates/autoscaler.yaml @@ -1,9 +1,9 @@ -{{- range $key, $api := .Values.apis }} +{{- range $name, $api := .Values.apis }} {{- if $api.enabled }} apiVersion: autoscaling/v2beta1 kind: HorizontalPodAutoscaler metadata: - name: {{ $key }}-hpa + name: {{ $name }}-hpa namespace: {{ $.Release.Namespace }} annotations: metric-config.object.istio-requests-total.prometheus/per-replica: "true" @@ -11,7 +11,7 @@ metadata: sum( rate( istio_requests_total{ - destination_workload="{{ $key }}", + destination_workload="{{ $name }}", destination_workload_namespace="{{ $.Release.Namespace }}" }[1m] ) @@ -20,7 +20,7 @@ metadata: count( container_memory_usage_bytes{ namespace="{{ $.Release.Namespace }}", - pod_name=~"{{ $key }}.*" + pod_name=~"{{ $name }}.*" } ) by (pod_name) ) @@ -30,7 +30,7 @@ spec: scaleTargetRef: apiVersion: apps/v1 kind: Deployment - name: {{ $key }} + name: {{ $name }} metrics: - type: Object object: @@ -38,7 +38,7 @@ spec: target: apiVersion: v1 kind: Pod - name: {{ $key }} + name: {{ $name }} targetValue: 10 --- {{- end }} diff --git a/helm/mds/templates/cronjob.yaml b/helm/mds/templates/cronjob.yaml deleted file mode 100644 index 46cbb18a3..000000000 --- a/helm/mds/templates/cronjob.yaml +++ /dev/null @@ -1,47 +0,0 @@ -# todo: revist values names once we register more then one cronjob concurrently -{{- if and $.Values.audience $.Values.auth0Domain $.Values.clientId $.Values.googleClientEmail $.Values.clientSecret $.Values.googlePrivateKey }} -apiVersion: batch/v1beta1 -kind: CronJob -metadata: - name: metrics-sheet - namespace: {{ $.Release.Namespace }} -spec: - schedule: "*/1 * * * *" - jobTemplate: - spec: - template: - spec: - containers: - - name: metrics-sheet - image: busybox - env: - - name: AUDIENCE - value: {{ $.Values.audience }} - - name: AUTH0_DOMAIN - value: {{ $.Values.auth0Domain }} - - name: CLIENT_ID - value: {{ $.Values.clientId }} - - name: CLIENT_SECRET - valueFrom: - secretKeyRef: - name: metrics-sheet - key: CLIENT_SECRET - - name: GOOGLE_CLIENT_EMAIL - value: {{ $.Values.googleClientEmail }} - - name: GOOGLE_PRIVATE_KEY - valueFrom: - secretKeyRef: - name: metrics-sheet - key: GOOGLE_PRIVATE_KEY - restartPolicy: OnFailure ---- -apiVersion: v1 -kind: Secret -metadata: - name: metrics-sheet - namespace: {{ $.Release.Namespace }} -type: Opaque -data: - CLIENT_SECRET: {{ $.Values.clientSecret | b64enc }} - GOOGLE_PRIVATE_KEY: {{ $.Values.googlePrivateKey | b64enc }} -{{- end}} \ No newline at end of file diff --git a/helm/mds/templates/deployment.yaml b/helm/mds/templates/deployment.yaml index 96acc5b00..cc9754db4 100644 --- a/helm/mds/templates/deployment.yaml +++ b/helm/mds/templates/deployment.yaml @@ -18,12 +18,16 @@ spec: labels: app: {{ $name }} spec: + volumes: + {{- if and $.Values.nats.enabled $.Values.nats.credentials }} + - name: creds-volume + secret: + secretName: nats-creds + {{- end }} containers: - name: {{ $name }} + image: {{ if $.Values.registry }}{{ printf "%s/" $.Values.registry }}{{- end}}{{ $name }}:{{ $api.version }} {{- if $.Values.registry }} - image: {{ $.Values.registry }}/{{ $name }}:{{ $api.version }} - {{- else }} - image: {{ $name }}:{{ $api.version }} imagePullPolicy: IfNotPresent {{- end }} resources: @@ -37,6 +41,11 @@ spec: - containerPort: 4000 name: user-port protocol: TCP + volumeMounts: + {{- if and $.Values.nats.enabled $.Values.nats.credentials }} + - name: creds-volume + mountPath: /var/run/nats/secrets + {{- end }} env: - name: PORT value: "4000" @@ -55,7 +64,7 @@ spec: key: postgresql-password - name: PG_HOST {{- if $.Values.postgresql.internal }} - value: mds-postgresql.{{ $.Release.Namespace }}.svc.cluster.local + value: {{ $.Release.Name }}-postgresql.{{ $.Release.Namespace }}.svc.cluster.local {{- else }} value: {{ $.Values.postgresql.host }} {{- end }} @@ -66,24 +75,31 @@ spec: {{- if $.Values.postgresql.port }} - name: PG_PORT value: {{ $.Values.postgresql.port | quote }} - {{- end }} + {{- end}} {{- if $api.migration }} - name: PG_MIGRATIONS value: {{ $api.migration | quote }} {{- end }} - name: REDIS_HOST {{- if $.Values.redis.internal }} - value: mds-redis-master.{{ $.Release.Namespace }}.svc.cluster.local + value: {{ $.Release.Name }}-redis-master.{{ $.Release.Namespace }}.svc.cluster.local {{- else }} value: {{ $.Values.redis.host }} {{- end }} - name: REDIS_PORT value: {{ $.Values.redis.port | quote }} - {{- if $.Values.useEvents }} - - name: CE_NAME - value: {{ $name }} - - name: SINK - value: http://default-broker.{{ $.Release.Namespace }}.svc.cluster.local + {{- if $.Values.nats.enabled }} + - name: NATS + value: {{ default "nats" $.Values.natsNamespace }}-nats-server.{{ default "nats" $.Values.natsNamespace }}.svc.cluster.local +# value: nats-cluster-mgmt.{{ default "nats" $.Values.natsNamespace }}.svc.cluster.local + {{- end }} + - name: KAFKA_HOST + value: host.docker.internal:9092 + - name: TENANT_ID + {{- if hasKey $.Values "tenantId" }} + value: {{ $.Values.tenantId }} + {{- else }} + value: {{ $.Release.Namespace }} {{- end }} {{- if $.Values.slack.channel }} - name: SLACK_CHANNEL @@ -97,6 +113,11 @@ spec: {{- if $api.env }} {{ toYaml $api.env | indent 10 }} {{- end }} + {{- if hasKey $.Values "global" }} + {{- if hasKey $.Values.global "env" }} +{{ toYaml $.Values.global.env | indent 10 }} + {{- end }} + {{- end }} {{- end }} {{- end }} --- @@ -113,3 +134,14 @@ data: postgresql-password: {{ .Values.postgresql.password | b64enc }} {{- end }} type: Opaque +{{- if and $.Values.nats.enabled $.Values.nats.credentials }} +--- +apiVersion: v1 +kind: Secret +metadata: + name: nats-creds + namespace: {{ $.Release.Namespace }} +data: + sys.creds: {{ $.Values.nats | b64enc }} +type: Opaque +{{- end }} diff --git a/helm/mds/templates/egress.yaml b/helm/mds/templates/egress.yaml index ea70d75e1..577d81a82 100644 --- a/helm/mds/templates/egress.yaml +++ b/helm/mds/templates/egress.yaml @@ -1,3 +1,46 @@ +{{- if $.Values.nats.enabled }} +{{- $natsNamespace := default "nats" .Values.natsNamespace }} +apiVersion: networking.istio.io/v1alpha3 +kind: ServiceEntry +metadata: + name: nats + namespace: {{ .Release.Namespace }} +spec: + hosts: + - {{ printf "%s-nats-server.%s.svc.cluster.local" $natsNamespace $natsNamespace }} + - "*.kafka.svc.cluster.local" + ports: + - number: 4222 + name: client + protocol: TCP + - number: 9092 + name: kafka-client + protocol: TCP + location: MESH_EXTERNAL +--- +apiVersion: networking.istio.io/v1alpha3 +kind: DestinationRule +metadata: + name: kafka + namespace: {{ .Release.Namespace }} +spec: + host: "*.kafka.svc.cluster.local" + trafficPolicy: + tls: + mode: DISABLE +--- +apiVersion: networking.istio.io/v1alpha3 +kind: DestinationRule +metadata: + name: nats + namespace: {{ .Release.Namespace }} +spec: + host: {{ printf "*.%s.svc.cluster.local" $natsNamespace | quote }} + trafficPolicy: + tls: + mode: DISABLE +--- +{{- end }} {{- if (eq .Values.postgresql.internal false) }} apiVersion: networking.istio.io/v1alpha3 kind: ServiceEntry @@ -46,3 +89,21 @@ spec: location: MESH_EXTERNAL --- {{- end }} +{{- if hasKey .Values "egress" }} +apiVersion: networking.istio.io/v1alpha3 +kind: ServiceEntry +metadata: + name: egress-other + namespace: {{ .Release.Namespace }} +spec: + hosts: + {{- range .Values.egress.hosts }} + - {{ . }} + {{- end }} + ports: + - number: 443 + name: tcp + protocol: TCP + location: MESH_EXTERNAL +--- +{{- end }} diff --git a/helm/mds/templates/ingress.yaml b/helm/mds/templates/ingress.yaml index 0db8788e7..68a38bf8a 100644 --- a/helm/mds/templates/ingress.yaml +++ b/helm/mds/templates/ingress.yaml @@ -1,9 +1,10 @@ -{{- range $key, $api := .Values.apis }} +{{- range $name, $api := .Values.apis }} {{- if $api.enabled }} +{{- if hasKey $api "pathPrefix" }} apiVersion: networking.istio.io/v1alpha3 kind: VirtualService metadata: - name: {{ $key }}-route + name: {{ $name }}-route namespace: {{ $.Release.Namespace }} spec: hosts: @@ -16,7 +17,7 @@ spec: regex: {{ printf "^%s($|/.*$)" $api.pathPrefix | quote }} route: - destination: - host: {{ $key }}.{{ $.Release.Namespace}}.svc.cluster.local + host: {{ $name }}.{{ $.Release.Namespace}}.svc.cluster.local port: number: 4000 corsPolicy: @@ -38,6 +39,7 @@ spec: --- {{- end }} {{- end }} +{{- end }} apiVersion: networking.istio.io/v1alpha3 kind: Gateway metadata: @@ -56,13 +58,29 @@ spec: mode: SIMPLE serverCertificate: /etc/istio/ingressgateway-certs/tls.crt privateKey: /etc/istio/ingressgateway-certs/tls.key - hosts: - - "*" {{- else }} - port: number: 80 name: http protocol: HTTP + {{- end }} hosts: - {{ .Values.domain | quote }} - {{- end }} +--- +# see https://github.com/istio/istio/issues/13848 +# "Node.js has 5 sec default connection idleTimeout, and envoy has the same one. +# In rare time it generate this error - envoy send reqest in the moment when nodejs closing it." +apiVersion: networking.istio.io/v1alpha3 +kind: DestinationRule +metadata: + name: mds-resolve-timeout-race + namespace: {{ .Release.Namespace }} +spec: + host: {{ printf "*.%s.svc.cluster.local" .Release.Namespace | quote }} + trafficPolicy: + tls: + mode: ISTIO_MUTUAL + connectionPool: + http: + idleTimeout: 3s + maxRetries: 3 diff --git a/helm/mds/templates/service.yaml b/helm/mds/templates/service.yaml index 26e8103c9..8e36f8f57 100644 --- a/helm/mds/templates/service.yaml +++ b/helm/mds/templates/service.yaml @@ -10,8 +10,7 @@ spec: selector: app: {{ $name }} ports: - - - name: http-{{ $name }} + - name: http-{{ $name }} port: 4000 {{- end }} {{- end }} diff --git a/helm/mds/tests/auth-agency-jwt-audience_test.yaml b/helm/mds/tests/auth-agency-jwt-audience_test.yaml index 38ac09d28..aa56d916f 100644 --- a/helm/mds/tests/auth-agency-jwt-audience_test.yaml +++ b/helm/mds/tests/auth-agency-jwt-audience_test.yaml @@ -1,4 +1,4 @@ -suite: test authetication jwt audience agency +suite: test authetication agency jwt audience templates: - auth.yaml tests: diff --git a/helm/mds/tests/auth-all-disabled_test.yaml b/helm/mds/tests/auth-all-disabled_test.yaml new file mode 100644 index 000000000..223d2740f --- /dev/null +++ b/helm/mds/tests/auth-all-disabled_test.yaml @@ -0,0 +1,17 @@ +suite: test authetication disabled +templates: + - auth.yaml +tests: + - it: doc 1 + release: + namespace: mds + set: + jwt: + issuer: my-issuer + enabled: false + audiences: + - audience-1 + - audience-2 + asserts: + - hasDocuments: + count: 0 diff --git a/helm/mds/tests/auth-all-jwt-audience_test.yaml b/helm/mds/tests/auth-all-jwt-audience_test.yaml index b9486670e..d75fb4824 100644 --- a/helm/mds/tests/auth-all-jwt-audience_test.yaml +++ b/helm/mds/tests/auth-all-jwt-audience_test.yaml @@ -14,4 +14,4 @@ tests: - audience-2 asserts: - hasDocuments: - count: 8 \ No newline at end of file + count: 12 \ No newline at end of file diff --git a/helm/mds/tests/auth-all-default_test.yaml b/helm/mds/tests/auth-all_test.yaml similarity index 100% rename from helm/mds/tests/auth-all-default_test.yaml rename to helm/mds/tests/auth-all_test.yaml diff --git a/helm/mds/tests/auth-audit-jwt-audience_test.yaml b/helm/mds/tests/auth-audit-jwt-audience_test.yaml index 74c5e0ae4..6527c4396 100644 --- a/helm/mds/tests/auth-audit-jwt-audience_test.yaml +++ b/helm/mds/tests/auth-audit-jwt-audience_test.yaml @@ -1,4 +1,4 @@ -suite: test authetication jwt audience audit +suite: test authetication audit jwt audience templates: - auth.yaml tests: diff --git a/helm/mds/tests/auth-compliance-jwt-audience_test.yaml b/helm/mds/tests/auth-compliance-jwt-audience_test.yaml index d0903c933..a1801d3f5 100644 --- a/helm/mds/tests/auth-compliance-jwt-audience_test.yaml +++ b/helm/mds/tests/auth-compliance-jwt-audience_test.yaml @@ -1,4 +1,4 @@ -suite: test authetication jwt audience compliance +suite: test authetication compliance jwt audience templates: - auth.yaml tests: diff --git a/helm/mds/tests/auth-config-jwt-audience_test.yaml b/helm/mds/tests/auth-config-jwt-audience_test.yaml new file mode 100644 index 000000000..07f2ae7e1 --- /dev/null +++ b/helm/mds/tests/auth-config-jwt-audience_test.yaml @@ -0,0 +1,52 @@ +suite: test authetication config jwt audience +templates: + - auth.yaml +tests: + - it: doc 3 + release: + namespace: mds + set: + jwt: + issuer: my-issuer + enabled: true + audiences: + - audience-1 + - audience-2 + asserts: + - isAPIVersion: + of: authentication.istio.io/v1alpha1 + documentIndex: 3 + - isKind: + of: Policy + documentIndex: 3 + - isEmpty: + path: spec.peers[0].mtls + documentIndex: 3 + - equal: + path: spec.origins[0].jwt.audiences[0] + value: audience-1 + documentIndex: 3 + - equal: + path: spec.origins[0].jwt.audiences[1] + value: audience-2 + documentIndex: 3 + - equal: + path: spec.origins[0].jwt.issuer + value: "my-issuer" + documentIndex: 3 + - equal: + path: spec.origins[0].jwt.jwksUri + value: my-issuer.well-known/jwks.json + documentIndex: 3 + - equal: + path: spec.origins[0].jwt.trigger_rules[0].excluded_paths[0].exact + value: /config/health + documentIndex: 3 + - equal: + path: spec.origins[0].jwt.trigger_rules[0].excluded_paths[1].exact + value: /health + documentIndex: 3 + - equal: + path: spec.principalBinding + value: USE_ORIGIN + documentIndex: 3 diff --git a/helm/mds/tests/auth-daily-jwt-audience_test.yaml b/helm/mds/tests/auth-daily-jwt-audience_test.yaml index b47cb1b46..f31d83824 100644 --- a/helm/mds/tests/auth-daily-jwt-audience_test.yaml +++ b/helm/mds/tests/auth-daily-jwt-audience_test.yaml @@ -1,8 +1,8 @@ -suite: test authetication jwt audience daily +suite: test authetication daily jwt audience templates: - auth.yaml tests: - - it: doc 3 + - it: doc release: namespace: mds set: @@ -15,50 +15,50 @@ tests: asserts: - isAPIVersion: of: authentication.istio.io/v1alpha1 - documentIndex: 3 + documentIndex: 4 - isKind: of: Policy - documentIndex: 3 + documentIndex: 4 - equal: path: metadata.name value: mds-daily-auth - documentIndex: 3 + documentIndex: 4 - equal: path: metadata.namespace value: mds - documentIndex: 3 + documentIndex: 4 - equal: path: spec.targets[0].name value: mds-daily - documentIndex: 3 + documentIndex: 4 - isEmpty: path: spec.peers[0].mtls - documentIndex: 3 + documentIndex: 4 - equal: path: spec.origins[0].jwt.audiences[0] value: audience-1 - documentIndex: 3 + documentIndex: 4 - equal: path: spec.origins[0].jwt.audiences[1] value: audience-2 - documentIndex: 3 + documentIndex: 4 - equal: path: spec.origins[0].jwt.issuer value: "my-issuer" - documentIndex: 3 + documentIndex: 4 - equal: path: spec.origins[0].jwt.jwksUri value: my-issuer.well-known/jwks.json - documentIndex: 3 + documentIndex: 4 - equal: path: spec.origins[0].jwt.trigger_rules[0].excluded_paths[0].exact value: /daily/health - documentIndex: 3 + documentIndex: 4 - equal: path: spec.origins[0].jwt.trigger_rules[0].excluded_paths[1].exact value: /health - documentIndex: 3 + documentIndex: 4 - equal: path: spec.principalBinding value: USE_ORIGIN - documentIndex: 3 + documentIndex: 4 diff --git a/helm/mds/tests/auth-geography-author-jwt-audience_test.yaml b/helm/mds/tests/auth-geography-author-jwt-audience_test.yaml new file mode 100644 index 000000000..7ac9b9234 --- /dev/null +++ b/helm/mds/tests/auth-geography-author-jwt-audience_test.yaml @@ -0,0 +1,64 @@ +suite: test authetication geography author jwt audience +templates: + - auth.yaml +tests: + - it: doc + release: + namespace: mds + set: + jwt: + issuer: my-issuer + enabled: true + audiences: + - audience-1 + - audience-2 + asserts: + - isAPIVersion: + of: authentication.istio.io/v1alpha1 + documentIndex: + - isKind: + of: Policy + documentIndex: 6 + - equal: + path: metadata.name + value: mds-geography-author-auth + documentIndex: 6 + - equal: + path: metadata.namespace + value: mds + documentIndex: 6 + - equal: + path: spec.targets[0].name + value: mds-geography-author + documentIndex: 6 + - isEmpty: + path: spec.peers[0].mtls + documentIndex: 6 + - equal: + path: spec.origins[0].jwt.audiences[0] + value: audience-1 + documentIndex: 6 + - equal: + path: spec.origins[0].jwt.audiences[1] + value: audience-2 + documentIndex: 6 + - equal: + path: spec.origins[0].jwt.issuer + value: 'my-issuer' + documentIndex: 6 + - equal: + path: spec.origins[0].jwt.jwksUri + value: my-issuer.well-known/jwks.json + documentIndex: 6 + - equal: + path: spec.origins[0].jwt.trigger_rules[0].excluded_paths[0].exact + value: /geography-author/health + documentIndex: 6 + - equal: + path: spec.origins[0].jwt.trigger_rules[0].excluded_paths[1].exact + value: /health + documentIndex: 6 + - equal: + path: spec.principalBinding + value: USE_ORIGIN + documentIndex: 6 diff --git a/helm/mds/tests/auth-native-jwt-audience_test.yaml b/helm/mds/tests/auth-jurisdiction-jwt-audience_test.yaml similarity index 69% rename from helm/mds/tests/auth-native-jwt-audience_test.yaml rename to helm/mds/tests/auth-jurisdiction-jwt-audience_test.yaml index e516a73b2..132379da6 100644 --- a/helm/mds/tests/auth-native-jwt-audience_test.yaml +++ b/helm/mds/tests/auth-jurisdiction-jwt-audience_test.yaml @@ -1,8 +1,8 @@ -suite: test authetication jwt audience native +suite: test authetication jurisdiction jwt audience templates: - auth.yaml tests: - - it: doc 4 + - it: doc release: namespace: mds set: @@ -15,50 +15,50 @@ tests: asserts: - isAPIVersion: of: authentication.istio.io/v1alpha1 - documentIndex: 4 + documentIndex: - isKind: of: Policy - documentIndex: 4 + documentIndex: 7 - equal: path: metadata.name - value: mds-native-auth - documentIndex: 4 + value: mds-jurisdiction-auth + documentIndex: 7 - equal: path: metadata.namespace value: mds - documentIndex: 4 + documentIndex: 7 - equal: path: spec.targets[0].name - value: mds-native - documentIndex: 4 + value: mds-jurisdiction + documentIndex: 7 - isEmpty: path: spec.peers[0].mtls - documentIndex: 4 + documentIndex: 7 - equal: path: spec.origins[0].jwt.audiences[0] value: audience-1 - documentIndex: 4 + documentIndex: 7 - equal: path: spec.origins[0].jwt.audiences[1] value: audience-2 - documentIndex: 4 + documentIndex: 7 - equal: path: spec.origins[0].jwt.issuer - value: "my-issuer" - documentIndex: 4 + value: 'my-issuer' + documentIndex: 7 - equal: path: spec.origins[0].jwt.jwksUri value: my-issuer.well-known/jwks.json - documentIndex: 4 + documentIndex: 7 - equal: path: spec.origins[0].jwt.trigger_rules[0].excluded_paths[0].exact - value: /native/health - documentIndex: 4 + value: /jurisdiction/health + documentIndex: 7 - equal: path: spec.origins[0].jwt.trigger_rules[0].excluded_paths[1].exact value: /health - documentIndex: 4 + documentIndex: 7 - equal: path: spec.principalBinding value: USE_ORIGIN - documentIndex: 4 + documentIndex: 7 diff --git a/helm/mds/tests/auth-policy-author-jwt-audience_test.yaml b/helm/mds/tests/auth-policy-author-jwt-audience_test.yaml new file mode 100644 index 000000000..12d166c3d --- /dev/null +++ b/helm/mds/tests/auth-policy-author-jwt-audience_test.yaml @@ -0,0 +1,64 @@ +suite: test authetication policy author jwt audience +templates: + - auth.yaml +tests: + - it: doc + release: + namespace: mds + set: + jwt: + issuer: my-issuer + enabled: true + audiences: + - audience-1 + - audience-2 + asserts: + - isAPIVersion: + of: authentication.istio.io/v1alpha1 + documentIndex: 11 + - isKind: + of: Policy + documentIndex: 11 + - equal: + path: metadata.name + value: mds-policy-author-auth + documentIndex: 11 + - equal: + path: metadata.namespace + value: mds + documentIndex: 11 + - equal: + path: spec.targets[0].name + value: mds-policy-author + documentIndex: 11 + - isEmpty: + path: spec.peers[0].mtls + documentIndex: 11 + - equal: + path: spec.origins[0].jwt.audiences[0] + value: audience-1 + documentIndex: 11 + - equal: + path: spec.origins[0].jwt.audiences[1] + value: audience-2 + documentIndex: 11 + - equal: + path: spec.origins[0].jwt.issuer + value: 'my-issuer' + documentIndex: 11 + - equal: + path: spec.origins[0].jwt.jwksUri + value: my-issuer.well-known/jwks.json + documentIndex: 11 + - equal: + path: spec.origins[0].jwt.trigger_rules[0].excluded_paths[0].exact + value: /policy-author/health + documentIndex: 11 + - equal: + path: spec.origins[0].jwt.trigger_rules[0].excluded_paths[1].exact + value: /health + documentIndex: 11 + - equal: + path: spec.principalBinding + value: USE_ORIGIN + documentIndex: 11 diff --git a/helm/mds/tests/auth-policy-jwt-audience_test.yaml b/helm/mds/tests/auth-policy-jwt-audience_test.yaml index 7503cc128..e1dfa29ce 100644 --- a/helm/mds/tests/auth-policy-jwt-audience_test.yaml +++ b/helm/mds/tests/auth-policy-jwt-audience_test.yaml @@ -1,8 +1,8 @@ -suite: test authetication jwt audience policy +suite: test authetication policy jwt audience templates: - auth.yaml tests: - - it: doc 5 + - it: doc release: namespace: mds set: @@ -15,50 +15,50 @@ tests: asserts: - isAPIVersion: of: authentication.istio.io/v1alpha1 - documentIndex: 5 + documentIndex: 10 - isKind: of: Policy - documentIndex: 5 + documentIndex: 10 - equal: path: metadata.name value: mds-policy-auth - documentIndex: 5 + documentIndex: 10 - equal: path: metadata.namespace value: mds - documentIndex: 5 + documentIndex: 10 - equal: path: spec.targets[0].name value: mds-policy - documentIndex: 5 + documentIndex: 10 - isEmpty: path: spec.peers[0].mtls - documentIndex: 5 + documentIndex: 10 - equal: path: spec.origins[0].jwt.audiences[0] value: audience-1 - documentIndex: 5 + documentIndex: 10 - equal: path: spec.origins[0].jwt.audiences[1] value: audience-2 - documentIndex: 5 + documentIndex: 10 - equal: path: spec.origins[0].jwt.issuer - value: "my-issuer" - documentIndex: 5 + value: 'my-issuer' + documentIndex: 10 - equal: path: spec.origins[0].jwt.jwksUri value: my-issuer.well-known/jwks.json - documentIndex: 5 + documentIndex: 10 - equal: path: spec.origins[0].jwt.trigger_rules[0].excluded_paths[0].exact value: /policy/health - documentIndex: 5 + documentIndex: 10 - equal: path: spec.origins[0].jwt.trigger_rules[0].excluded_paths[1].exact value: /health - documentIndex: 5 + documentIndex: 10 - equal: path: spec.principalBinding value: USE_ORIGIN - documentIndex: 5 + documentIndex: 10 diff --git a/helm/mds/tests/autoscaler-all-default_test.yaml b/helm/mds/tests/autoscaler-all_test.yaml similarity index 87% rename from helm/mds/tests/autoscaler-all-default_test.yaml rename to helm/mds/tests/autoscaler-all_test.yaml index 7f305e680..bcb82d025 100644 --- a/helm/mds/tests/autoscaler-all-default_test.yaml +++ b/helm/mds/tests/autoscaler-all_test.yaml @@ -7,4 +7,4 @@ tests: namespace: mds asserts: - hasDocuments: - count: 8 + count: 13 diff --git a/helm/mds/tests/autoscaler-daily_test.yaml b/helm/mds/tests/autoscaler-daily_test.yaml index 051c9f3b5..64b2e90e0 100644 --- a/helm/mds/tests/autoscaler-daily_test.yaml +++ b/helm/mds/tests/autoscaler-daily_test.yaml @@ -8,22 +8,22 @@ tests: asserts: - isAPIVersion: of: autoscaling/v2beta1 - documentIndex: 3 + documentIndex: 4 - isKind: of: HorizontalPodAutoscaler - documentIndex: 3 + documentIndex: 4 - equal: path: metadata.name value: mds-daily-hpa - documentIndex: 3 + documentIndex: 4 - equal: path: metadata.namespace value: mds - documentIndex: 3 + documentIndex: 4 - equal: path: metadata.annotations.metric-config\.object\.istio-requests-total\.prometheus/per-replica value: "true" - documentIndex: 3 + documentIndex: 4 - equal: path: metadata.annotations.metric-config\.object\.istio-requests-total\.prometheus/query value: | @@ -43,48 +43,48 @@ tests: } ) by (pod_name) ) - documentIndex: 3 + documentIndex: 4 - equal: path: spec.maxReplicas value: 10 - documentIndex: 3 + documentIndex: 4 - equal: path: spec.minReplicas value: 1 - documentIndex: 3 + documentIndex: 4 - equal: path: spec.scaleTargetRef.apiVersion value: apps/v1 - documentIndex: 3 + documentIndex: 4 - equal: path: spec.scaleTargetRef.kind value: Deployment - documentIndex: 3 + documentIndex: 4 - equal: path: spec.scaleTargetRef.name value: mds-daily - documentIndex: 3 + documentIndex: 4 - equal: path: spec.metrics[0].type value: Object - documentIndex: 3 + documentIndex: 4 - equal: path: spec.metrics[0].object.metricName value: istio-requests-total - documentIndex: 3 + documentIndex: 4 - equal: path: spec.metrics[0].object.target.apiVersion value: v1 - documentIndex: 3 + documentIndex: 4 - equal: path: spec.metrics[0].object.target.kind value: Pod - documentIndex: 3 + documentIndex: 4 - equal: path: spec.metrics[0].object.target.name value: mds-daily - documentIndex: 3 + documentIndex: 4 - equal: path: spec.metrics[0].object.targetValue value: 10 - documentIndex: 3 \ No newline at end of file + documentIndex: 4 \ No newline at end of file diff --git a/helm/mds/tests/autoscaler-geography-author_test.yaml b/helm/mds/tests/autoscaler-geography-author_test.yaml new file mode 100644 index 000000000..bfe43db9a --- /dev/null +++ b/helm/mds/tests/autoscaler-geography-author_test.yaml @@ -0,0 +1,90 @@ +suite: test autoscaler geography-author +templates: + - autoscaler.yaml +tests: + - it: doc 3 + release: + namespace: mds + asserts: + - isAPIVersion: + of: autoscaling/v2beta1 + documentIndex: 6 + - isKind: + of: HorizontalPodAutoscaler + documentIndex: 6 + - equal: + path: metadata.name + value: mds-geography-author-hpa + documentIndex: 6 + - equal: + path: metadata.namespace + value: mds + documentIndex: 6 + - equal: + path: metadata.annotations.metric-config\.object\.istio-requests-total\.prometheus/per-replica + value: "true" + documentIndex: 6 + - equal: + path: metadata.annotations.metric-config\.object\.istio-requests-total\.prometheus/query + value: | + sum( + rate( + istio_requests_total{ + destination_workload="mds-geography-author", + destination_workload_namespace="mds" + }[1m] + ) + ) / + count( + count( + container_memory_usage_bytes{ + namespace="mds", + pod_name=~"mds-geography-author.*" + } + ) by (pod_name) + ) + documentIndex: 6 + - equal: + path: spec.maxReplicas + value: 10 + documentIndex: 6 + - equal: + path: spec.minReplicas + value: 1 + documentIndex: 6 + - equal: + path: spec.scaleTargetRef.apiVersion + value: apps/v1 + documentIndex: 6 + - equal: + path: spec.scaleTargetRef.kind + value: Deployment + documentIndex: 6 + - equal: + path: spec.scaleTargetRef.name + value: mds-geography-author + documentIndex: 6 + - equal: + path: spec.metrics[0].type + value: Object + documentIndex: 6 + - equal: + path: spec.metrics[0].object.metricName + value: istio-requests-total + documentIndex: 6 + - equal: + path: spec.metrics[0].object.target.apiVersion + value: v1 + documentIndex: 6 + - equal: + path: spec.metrics[0].object.target.kind + value: Pod + documentIndex: 6 + - equal: + path: spec.metrics[0].object.target.name + value: mds-geography-author + documentIndex: 6 + - equal: + path: spec.metrics[0].object.targetValue + value: 10 + documentIndex: 6 \ No newline at end of file diff --git a/helm/mds/tests/autoscaler-native_test.yaml b/helm/mds/tests/autoscaler-jurisdiction_test.yaml similarity index 72% rename from helm/mds/tests/autoscaler-native_test.yaml rename to helm/mds/tests/autoscaler-jurisdiction_test.yaml index 5008e9131..f8d17d85d 100644 --- a/helm/mds/tests/autoscaler-native_test.yaml +++ b/helm/mds/tests/autoscaler-jurisdiction_test.yaml @@ -1,36 +1,36 @@ -suite: test autoscaler native +suite: test autoscaler jurisdiction templates: - autoscaler.yaml tests: - - it: doc 4 + - it: doc 3 release: namespace: mds asserts: - isAPIVersion: of: autoscaling/v2beta1 - documentIndex: 4 + documentIndex: 7 - isKind: of: HorizontalPodAutoscaler - documentIndex: 4 + documentIndex: 7 - equal: path: metadata.name - value: mds-native-hpa - documentIndex: 4 + value: mds-jurisdiction-hpa + documentIndex: 7 - equal: path: metadata.namespace value: mds - documentIndex: 4 + documentIndex: 7 - equal: path: metadata.annotations.metric-config\.object\.istio-requests-total\.prometheus/per-replica value: "true" - documentIndex: 4 + documentIndex: 7 - equal: path: metadata.annotations.metric-config\.object\.istio-requests-total\.prometheus/query value: | sum( rate( istio_requests_total{ - destination_workload="mds-native", + destination_workload="mds-jurisdiction", destination_workload_namespace="mds" }[1m] ) @@ -39,52 +39,52 @@ tests: count( container_memory_usage_bytes{ namespace="mds", - pod_name=~"mds-native.*" + pod_name=~"mds-jurisdiction.*" } ) by (pod_name) ) - documentIndex: 4 + documentIndex: 7 - equal: path: spec.maxReplicas value: 10 - documentIndex: 4 + documentIndex: 7 - equal: path: spec.minReplicas value: 1 - documentIndex: 4 + documentIndex: 7 - equal: path: spec.scaleTargetRef.apiVersion value: apps/v1 - documentIndex: 4 + documentIndex: 7 - equal: path: spec.scaleTargetRef.kind value: Deployment - documentIndex: 4 + documentIndex: 7 - equal: path: spec.scaleTargetRef.name - value: mds-native - documentIndex: 4 + value: mds-jurisdiction + documentIndex: 7 - equal: path: spec.metrics[0].type value: Object - documentIndex: 4 + documentIndex: 7 - equal: path: spec.metrics[0].object.metricName value: istio-requests-total - documentIndex: 4 + documentIndex: 7 - equal: path: spec.metrics[0].object.target.apiVersion value: v1 - documentIndex: 4 + documentIndex: 7 - equal: path: spec.metrics[0].object.target.kind value: Pod - documentIndex: 4 + documentIndex: 7 - equal: path: spec.metrics[0].object.target.name - value: mds-native - documentIndex: 4 + value: mds-jurisdiction + documentIndex: 7 - equal: path: spec.metrics[0].object.targetValue value: 10 - documentIndex: 4 \ No newline at end of file + documentIndex: 7 \ No newline at end of file diff --git a/helm/mds/tests/autoscaler-policy-author_test.yaml b/helm/mds/tests/autoscaler-policy-author_test.yaml new file mode 100644 index 000000000..49c576d05 --- /dev/null +++ b/helm/mds/tests/autoscaler-policy-author_test.yaml @@ -0,0 +1,90 @@ +suite: test autoscaler policy-author +templates: + - autoscaler.yaml +tests: + - it: doc 5 + release: + namespace: mds + asserts: + - isAPIVersion: + of: autoscaling/v2beta1 + documentIndex: 11 + - isKind: + of: HorizontalPodAutoscaler + documentIndex: 11 + - equal: + path: metadata.name + value: mds-policy-author-hpa + documentIndex: 11 + - equal: + path: metadata.namespace + value: mds + documentIndex: 11 + - equal: + path: metadata.annotations.metric-config\.object\.istio-requests-total\.prometheus/per-replica + value: "true" + documentIndex: 11 + - equal: + path: metadata.annotations.metric-config\.object\.istio-requests-total\.prometheus/query + value: | + sum( + rate( + istio_requests_total{ + destination_workload="mds-policy-author", + destination_workload_namespace="mds" + }[1m] + ) + ) / + count( + count( + container_memory_usage_bytes{ + namespace="mds", + pod_name=~"mds-policy-author.*" + } + ) by (pod_name) + ) + documentIndex: 11 + - equal: + path: spec.maxReplicas + value: 10 + documentIndex: 11 + - equal: + path: spec.minReplicas + value: 1 + documentIndex: 11 + - equal: + path: spec.scaleTargetRef.apiVersion + value: apps/v1 + documentIndex: 11 + - equal: + path: spec.scaleTargetRef.kind + value: Deployment + documentIndex: 11 + - equal: + path: spec.scaleTargetRef.name + value: mds-policy-author + documentIndex: 11 + - equal: + path: spec.metrics[0].type + value: Object + documentIndex: 11 + - equal: + path: spec.metrics[0].object.metricName + value: istio-requests-total + documentIndex: 11 + - equal: + path: spec.metrics[0].object.target.apiVersion + value: v1 + documentIndex: 11 + - equal: + path: spec.metrics[0].object.target.kind + value: Pod + documentIndex: 11 + - equal: + path: spec.metrics[0].object.target.name + value: mds-policy-author + documentIndex: 11 + - equal: + path: spec.metrics[0].object.targetValue + value: 10 + documentIndex: 11 \ No newline at end of file diff --git a/helm/mds/tests/autoscaler-policy_test.yaml b/helm/mds/tests/autoscaler-policy_test.yaml index c7d39d789..74953e612 100644 --- a/helm/mds/tests/autoscaler-policy_test.yaml +++ b/helm/mds/tests/autoscaler-policy_test.yaml @@ -8,22 +8,22 @@ tests: asserts: - isAPIVersion: of: autoscaling/v2beta1 - documentIndex: 5 + documentIndex: 10 - isKind: of: HorizontalPodAutoscaler - documentIndex: 5 + documentIndex: 10 - equal: path: metadata.name value: mds-policy-hpa - documentIndex: 5 + documentIndex: 10 - equal: path: metadata.namespace value: mds - documentIndex: 5 + documentIndex: 10 - equal: path: metadata.annotations.metric-config\.object\.istio-requests-total\.prometheus/per-replica value: "true" - documentIndex: 5 + documentIndex: 10 - equal: path: metadata.annotations.metric-config\.object\.istio-requests-total\.prometheus/query value: | @@ -43,48 +43,48 @@ tests: } ) by (pod_name) ) - documentIndex: 5 + documentIndex: 10 - equal: path: spec.maxReplicas value: 10 - documentIndex: 5 + documentIndex: 10 - equal: path: spec.minReplicas value: 1 - documentIndex: 5 + documentIndex: 10 - equal: path: spec.scaleTargetRef.apiVersion value: apps/v1 - documentIndex: 5 + documentIndex: 10 - equal: path: spec.scaleTargetRef.kind value: Deployment - documentIndex: 5 + documentIndex: 10 - equal: path: spec.scaleTargetRef.name value: mds-policy - documentIndex: 5 + documentIndex: 10 - equal: path: spec.metrics[0].type value: Object - documentIndex: 5 + documentIndex: 10 - equal: path: spec.metrics[0].object.metricName value: istio-requests-total - documentIndex: 5 + documentIndex: 10 - equal: path: spec.metrics[0].object.target.apiVersion value: v1 - documentIndex: 5 + documentIndex: 10 - equal: path: spec.metrics[0].object.target.kind value: Pod - documentIndex: 5 + documentIndex: 10 - equal: path: spec.metrics[0].object.target.name value: mds-policy - documentIndex: 5 + documentIndex: 10 - equal: path: spec.metrics[0].object.targetValue value: 10 - documentIndex: 5 \ No newline at end of file + documentIndex: 10 \ No newline at end of file diff --git a/helm/mds/tests/autoscaler-web-sockets_test.yaml b/helm/mds/tests/autoscaler-web-sockets_test.yaml new file mode 100644 index 000000000..16a5476bb --- /dev/null +++ b/helm/mds/tests/autoscaler-web-sockets_test.yaml @@ -0,0 +1,90 @@ +suite: test autoscaler web-sockets +templates: + - autoscaler.yaml +tests: + - it: doc 5 + release: + namespace: mds + asserts: + - isAPIVersion: + of: autoscaling/v2beta1 + documentIndex: 12 + - isKind: + of: HorizontalPodAutoscaler + documentIndex: 12 + - equal: + path: metadata.name + value: mds-web-sockets-hpa + documentIndex: 12 + - equal: + path: metadata.namespace + value: mds + documentIndex: 12 + - equal: + path: metadata.annotations.metric-config\.object\.istio-requests-total\.prometheus/per-replica + value: "true" + documentIndex: 12 + - equal: + path: metadata.annotations.metric-config\.object\.istio-requests-total\.prometheus/query + value: | + sum( + rate( + istio_requests_total{ + destination_workload="mds-web-sockets", + destination_workload_namespace="mds" + }[1m] + ) + ) / + count( + count( + container_memory_usage_bytes{ + namespace="mds", + pod_name=~"mds-web-sockets.*" + } + ) by (pod_name) + ) + documentIndex: 12 + - equal: + path: spec.maxReplicas + value: 10 + documentIndex: 12 + - equal: + path: spec.minReplicas + value: 1 + documentIndex: 12 + - equal: + path: spec.scaleTargetRef.apiVersion + value: apps/v1 + documentIndex: 12 + - equal: + path: spec.scaleTargetRef.kind + value: Deployment + documentIndex: 12 + - equal: + path: spec.scaleTargetRef.name + value: mds-web-sockets + documentIndex: 12 + - equal: + path: spec.metrics[0].type + value: Object + documentIndex: 12 + - equal: + path: spec.metrics[0].object.metricName + value: istio-requests-total + documentIndex: 12 + - equal: + path: spec.metrics[0].object.target.apiVersion + value: v1 + documentIndex: 12 + - equal: + path: spec.metrics[0].object.target.kind + value: Pod + documentIndex: 12 + - equal: + path: spec.metrics[0].object.target.name + value: mds-web-sockets + documentIndex: 12 + - equal: + path: spec.metrics[0].object.targetValue + value: 10 + documentIndex: 12 \ No newline at end of file diff --git a/helm/mds/tests/cronjob-metrics-sheet_test.yaml b/helm/mds/tests/cronjob-metrics-sheet_test.yaml deleted file mode 100644 index e2a14eb4d..000000000 --- a/helm/mds/tests/cronjob-metrics-sheet_test.yaml +++ /dev/null @@ -1,108 +0,0 @@ -suite: test cronjob metrics sheet -templates: - - cronjob.yaml -tests: - - it: all - release: - namespace: mds - set: - audience: audience-stub - auth0Domain: auth0Domain-stub - clientId: clientId-stub - googleClientEmail: googleClientEmail-stub - clientSecret: clientSecret-stub - googlePrivateKey: googlePrivateKey-stub - asserts: - - hasDocuments: - count: 2 - - isAPIVersion: - of: batch/v1beta1 - - isKind: - of: CronJob - - equal: - path: metadata.name - value: metrics-sheet - - equal: - path: metadata.namespace - value: mds - - equal: - path: spec.schedule - value: |- - */1 * * * * - - equal: - path: spec.jobTemplate.spec.template.spec.containers[0].name - value: metrics-sheet - - equal: - path: spec.jobTemplate.spec.template.spec.containers[0].image - value: busybox - - equal: - path: spec.jobTemplate.spec.template.spec.containers[0].env[0].name - value: AUDIENCE - - equal: - path: spec.jobTemplate.spec.template.spec.containers[0].env[0].value - value: audience-stub - - equal: - path: spec.jobTemplate.spec.template.spec.containers[0].env[1].name - value: AUTH0_DOMAIN - - equal: - path: spec.jobTemplate.spec.template.spec.containers[0].env[1].value - value: auth0Domain-stub - - equal: - path: spec.jobTemplate.spec.template.spec.containers[0].env[2].name - value: CLIENT_ID - - equal: - path: spec.jobTemplate.spec.template.spec.containers[0].env[2].value - value: clientId-stub - - equal: - path: spec.jobTemplate.spec.template.spec.containers[0].env[3].name - value: CLIENT_SECRET - - equal: - path: spec.jobTemplate.spec.template.spec.containers[0].env[3].valueFrom.secretKeyRef.key - value: CLIENT_SECRET - - equal: - path: spec.jobTemplate.spec.template.spec.containers[0].env[3].valueFrom.secretKeyRef.name - value: metrics-sheet - - equal: - path: spec.jobTemplate.spec.template.spec.containers[0].env[4].name - value: GOOGLE_CLIENT_EMAIL - - equal: - path: spec.jobTemplate.spec.template.spec.containers[0].env[4].value - value: googleClientEmail-stub - - equal: - path: spec.jobTemplate.spec.template.spec.containers[0].env[5].name - value: GOOGLE_PRIVATE_KEY - - equal: - path: spec.jobTemplate.spec.template.spec.containers[0].env[5].valueFrom.secretKeyRef.key - value: GOOGLE_PRIVATE_KEY - - equal: - path: spec.jobTemplate.spec.template.spec.containers[0].env[5].valueFrom.secretKeyRef.name - value: metrics-sheet - - equal: - path: spec.jobTemplate.spec.template.spec.containers[0].restartPolicy - value: OnFailure - - isAPIVersion: - of: v1 - documentIndex: 1 - - isKind: - of: Secret - documentIndex: 1 - - equal: - path: metadata.name - value: metrics-sheet - documentIndex: 1 - - equal: - path: metadata.namespace - value: mds - documentIndex: 1 - - equal: - path: type - value: Opaque - documentIndex: 1 - - equal: - path: data.CLIENT_SECRET - value: Y2xpZW50U2VjcmV0LXN0dWI= - documentIndex: 1 - - equal: - path: data.GOOGLE_PRIVATE_KEY - value: Z29vZ2xlUHJpdmF0ZUtleS1zdHVi - documentIndex: 1 \ No newline at end of file diff --git a/helm/mds/tests/deployment-agency-no-cache_test.yaml b/helm/mds/tests/deployment-agency-no-cache_test.yaml deleted file mode 100644 index 6c888a101..000000000 --- a/helm/mds/tests/deployment-agency-no-cache_test.yaml +++ /dev/null @@ -1,164 +0,0 @@ -suite: test deployment agency no-cache -templates: - - deployment.yaml -tests: - - it: doc 0 - release: - namespace: mds - set: - apis.mds-agency.useCache: false - asserts: - - isAPIVersion: - of: apps/v1 - documentIndex: 0 - - isKind: - of: Deployment - documentIndex: 0 - - equal: - path: metadata.name - value: mds-agency - documentIndex: 0 - - equal: - path: metadata.labels.app - value: mds-agency - documentIndex: 0 - - equal: - path: metadata.namespace - value: mds - documentIndex: 0 - - equal: - path: spec.replicas - value: 1 - documentIndex: 0 - - equal: - path: spec.selector.matchLabels.app - value: mds-agency - documentIndex: 0 - - equal: - path: spec.template.metadata.labels.app - value: mds-agency - documentIndex: 0 - - equal: - path: spec.template.spec.containers[0].name - value: mds-agency - documentIndex: 0 - - equal: - path: spec.template.spec.containers[0].image - value: mds-agency:latest - documentIndex: 0 - - equal: - path: spec.template.spec.containers[0].imagePullPolicy - value: IfNotPresent - documentIndex: 0 - - equal: - path: spec.template.spec.containers[0].resources.limits.cpu - value: 500m - documentIndex: 0 - - equal: - path: spec.template.spec.containers[0].resources.limits.memory - value: 512Mi - documentIndex: 0 - - equal: - path: spec.template.spec.containers[0].resources.requests.cpu - value: 50m - documentIndex: 0 - - equal: - path: spec.template.spec.containers[0].resources.requests.memory - value: 128Mi - documentIndex: 0 - - equal: - path: spec.template.spec.containers[0].ports[0].containerPort - value: 4001 - documentIndex: 0 - - equal: - path: spec.template.spec.containers[0].ports[0].name - value: user-port - documentIndex: 0 - - equal: - path: spec.template.spec.containers[0].ports[0].protocol - value: TCP - documentIndex: 0 - - equal: - path: spec.template.spec.containers[0].env[0].name - value: PATH_PREFIX - documentIndex: 0 - - equal: - path: spec.template.spec.containers[0].env[0].value - value: /agency - documentIndex: 0 - - equal: - path: spec.template.spec.containers[0].env[1].name - value: TIMEZONE - documentIndex: 0 - - equal: - path: spec.template.spec.containers[0].env[1].value - value: America/Los_Angeles - documentIndex: 0 - - equal: - path: spec.template.spec.containers[0].env[2].name - value: PG_USER - documentIndex: 0 - - equal: - path: spec.template.spec.containers[0].env[2].value - value: mdsadmin - documentIndex: 0 - - equal: - path: spec.template.spec.containers[0].env[3].name - value: PG_NAME - documentIndex: 0 - - equal: - path: spec.template.spec.containers[0].env[3].value - value: mds - documentIndex: 0 - - equal: - path: spec.template.spec.containers[0].env[4].name - value: PG_PASS - documentIndex: 0 - - equal: - path: spec.template.spec.containers[0].env[4].valueFrom.secretKeyRef.name - value: pg-pass - documentIndex: 0 - - equal: - path: spec.template.spec.containers[0].env[4].valueFrom.secretKeyRef.key - value: postgresql-password - documentIndex: 0 - - equal: - path: spec.template.spec.containers[0].env[5].name - value: PG_PORT - documentIndex: 0 - - equal: - path: spec.template.spec.containers[0].env[5].value - value: "5432" - documentIndex: 0 - - equal: - path: spec.template.spec.containers[0].env[6].name - value: PG_HOST - documentIndex: 0 - - equal: - path: spec.template.spec.containers[0].env[6].value - value: mds-postgresql.mds.svc.cluster.local - documentIndex: 0 - - equal: - path: spec.template.spec.containers[0].env[7].name - value: PG_HOST_READER - documentIndex: 0 - - equal: - path: spec.template.spec.containers[0].env[7].value - value: mds-postgresql.mds.svc.cluster.local - documentIndex: 0 - - equal: - path: spec.template.spec.containers[0].env[8].name - value: CE_NAME - documentIndex: 0 - - equal: - path: spec.template.spec.containers[0].env[8].value - value: mds-agency - documentIndex: 0 - - equal: - path: spec.template.spec.containers[0].env[9].name - value: SINK - documentIndex: 0 - - equal: - path: spec.template.spec.containers[0].env[9].value - value: http://default-broker.mds.svc.cluster.local - documentIndex: 0 diff --git a/helm/mds/tests/deployment-agency-no-db_test.yaml b/helm/mds/tests/deployment-agency-no-db_test.yaml deleted file mode 100644 index 82ef3a9d2..000000000 --- a/helm/mds/tests/deployment-agency-no-db_test.yaml +++ /dev/null @@ -1,128 +0,0 @@ -suite: test deployment agency no-db -templates: - - deployment.yaml -tests: - - it: doc 0 - release: - namespace: mds - set: - apis.mds-agency.useDB: false - asserts: - - isAPIVersion: - of: apps/v1 - documentIndex: 0 - - isKind: - of: Deployment - documentIndex: 0 - - equal: - path: metadata.name - value: mds-agency - documentIndex: 0 - - equal: - path: metadata.labels.app - value: mds-agency - documentIndex: 0 - - equal: - path: metadata.namespace - value: mds - documentIndex: 0 - - equal: - path: spec.replicas - value: 1 - documentIndex: 0 - - equal: - path: spec.selector.matchLabels.app - value: mds-agency - documentIndex: 0 - - equal: - path: spec.template.metadata.labels.app - value: mds-agency - documentIndex: 0 - - equal: - path: spec.template.spec.containers[0].name - value: mds-agency - documentIndex: 0 - - equal: - path: spec.template.spec.containers[0].image - value: mds-agency:latest - documentIndex: 0 - - equal: - path: spec.template.spec.containers[0].imagePullPolicy - value: IfNotPresent - documentIndex: 0 - - equal: - path: spec.template.spec.containers[0].resources.limits.cpu - value: 500m - documentIndex: 0 - - equal: - path: spec.template.spec.containers[0].resources.limits.memory - value: 512Mi - documentIndex: 0 - - equal: - path: spec.template.spec.containers[0].resources.requests.cpu - value: 50m - documentIndex: 0 - - equal: - path: spec.template.spec.containers[0].resources.requests.memory - value: 128Mi - documentIndex: 0 - - equal: - path: spec.template.spec.containers[0].ports[0].containerPort - value: 4001 - documentIndex: 0 - - equal: - path: spec.template.spec.containers[0].ports[0].name - value: user-port - documentIndex: 0 - - equal: - path: spec.template.spec.containers[0].ports[0].protocol - value: TCP - documentIndex: 0 - - equal: - path: spec.template.spec.containers[0].env[0].name - value: PATH_PREFIX - documentIndex: 0 - - equal: - path: spec.template.spec.containers[0].env[0].value - value: /agency - documentIndex: 0 - - equal: - path: spec.template.spec.containers[0].env[1].name - value: TIMEZONE - documentIndex: 0 - - equal: - path: spec.template.spec.containers[0].env[1].value - value: America/Los_Angeles - documentIndex: 0 - - equal: - path: spec.template.spec.containers[0].env[2].name - value: REDIS_HOST - documentIndex: 0 - - equal: - path: spec.template.spec.containers[0].env[2].value - value: mds-redis-master.mds.svc.cluster.local - documentIndex: 0 - - equal: - path: spec.template.spec.containers[0].env[3].name - value: REDIS_PORT - documentIndex: 0 - - equal: - path: spec.template.spec.containers[0].env[3].value - value: "6379" - documentIndex: 0 - - equal: - path: spec.template.spec.containers[0].env[4].name - value: CE_NAME - documentIndex: 0 - - equal: - path: spec.template.spec.containers[0].env[4].value - value: mds-agency - documentIndex: 0 - - equal: - path: spec.template.spec.containers[0].env[5].name - value: SINK - documentIndex: 0 - - equal: - path: spec.template.spec.containers[0].env[5].value - value: http://default-broker.mds.svc.cluster.local - documentIndex: 0 diff --git a/helm/mds/tests/deployment-agency-no-events_test.yaml b/helm/mds/tests/deployment-agency-no-events_test.yaml deleted file mode 100644 index cc4630c39..000000000 --- a/helm/mds/tests/deployment-agency-no-events_test.yaml +++ /dev/null @@ -1,168 +0,0 @@ -suite: test deployment agency no-events -templates: - - deployment.yaml -tests: - - it: doc 0 - release: - namespace: mds - set: - apis.mds-agency.useEvents: false - asserts: - - isAPIVersion: - of: apps/v1 - documentIndex: 0 - - isKind: - of: Deployment - documentIndex: 0 - - equal: - path: metadata.name - value: mds-agency - documentIndex: 0 - - equal: - path: metadata.labels.app - value: mds-agency - documentIndex: 0 - - equal: - path: metadata.namespace - value: mds - documentIndex: 0 - - equal: - path: spec.replicas - value: 1 - documentIndex: 0 - - equal: - path: spec.selector.matchLabels.app - value: mds-agency - documentIndex: 0 - - equal: - path: spec.template.metadata.labels.app - value: mds-agency - documentIndex: 0 - - equal: - path: spec.template.spec.containers[0].name - value: mds-agency - documentIndex: 0 - - equal: - path: spec.template.spec.containers[0].image - value: mds-agency:latest - documentIndex: 0 - - equal: - path: spec.template.spec.containers[0].imagePullPolicy - value: IfNotPresent - documentIndex: 0 - - equal: - path: spec.template.spec.containers[0].resources.limits.cpu - value: 500m - documentIndex: 0 - - equal: - path: spec.template.spec.containers[0].resources.limits.memory - value: 512Mi - documentIndex: 0 - - equal: - path: spec.template.spec.containers[0].resources.requests.cpu - value: 50m - documentIndex: 0 - - equal: - path: spec.template.spec.containers[0].resources.requests.memory - value: 128Mi - documentIndex: 0 - - equal: - path: spec.template.spec.containers[0].ports[0].containerPort - value: 4001 - documentIndex: 0 - - equal: - path: spec.template.spec.containers[0].ports[0].name - value: user-port - documentIndex: 0 - - equal: - path: spec.template.spec.containers[0].ports[0].protocol - value: TCP - documentIndex: 0 - - equal: - path: spec.template.spec.containers[0].env[0].name - value: PATH_PREFIX - documentIndex: 0 - - equal: - path: spec.template.spec.containers[0].env[0].value - value: /agency - documentIndex: 0 - - equal: - path: spec.template.spec.containers[0].env[1].name - value: TIMEZONE - documentIndex: 0 - - equal: - path: spec.template.spec.containers[0].env[1].value - value: America/Los_Angeles - documentIndex: 0 - - equal: - path: spec.template.spec.containers[0].env[2].name - value: PG_USER - documentIndex: 0 - - equal: - path: spec.template.spec.containers[0].env[2].value - value: mdsadmin - documentIndex: 0 - - equal: - path: spec.template.spec.containers[0].env[3].name - value: PG_NAME - documentIndex: 0 - - equal: - path: spec.template.spec.containers[0].env[3].value - value: mds - documentIndex: 0 - - equal: - path: spec.template.spec.containers[0].env[4].name - value: PG_PASS - documentIndex: 0 - - equal: - path: spec.template.spec.containers[0].env[4].valueFrom.secretKeyRef.name - value: pg-pass - documentIndex: 0 - - equal: - path: spec.template.spec.containers[0].env[4].valueFrom.secretKeyRef.key - value: postgresql-password - documentIndex: 0 - - equal: - path: spec.template.spec.containers[0].env[5].name - value: PG_PORT - documentIndex: 0 - - equal: - path: spec.template.spec.containers[0].env[5].value - value: "5432" - documentIndex: 0 - - equal: - path: spec.template.spec.containers[0].env[6].name - value: PG_HOST - documentIndex: 0 - - equal: - path: spec.template.spec.containers[0].env[6].value - value: mds-postgresql.mds.svc.cluster.local - documentIndex: 0 - - equal: - path: spec.template.spec.containers[0].env[7].name - value: PG_HOST_READER - documentIndex: 0 - - equal: - path: spec.template.spec.containers[0].env[7].value - value: mds-postgresql.mds.svc.cluster.local - documentIndex: 0 - - equal: - path: spec.template.spec.containers[0].env[8].name - value: REDIS_HOST - documentIndex: 0 - - equal: - path: spec.template.spec.containers[0].env[8].value - value: mds-redis-master.mds.svc.cluster.local - documentIndex: 0 - - equal: - path: spec.template.spec.containers[0].env[9].name - value: REDIS_PORT - documentIndex: 0 - - equal: - path: spec.template.spec.containers[0].env[9].value - value: "6379" - documentIndex: 0 - # fixme: test non-existence - # - isNull: - # path: spec.template.spec.containers[0].env[10] - # documentIndex: 1 diff --git a/helm/mds/tests/deployment-agency-env_test.yaml b/helm/mds/tests/deployment-agency-override_test.yaml similarity index 61% rename from helm/mds/tests/deployment-agency-env_test.yaml rename to helm/mds/tests/deployment-agency-override_test.yaml index cf2cdd4d4..776022de3 100644 --- a/helm/mds/tests/deployment-agency-env_test.yaml +++ b/helm/mds/tests/deployment-agency-override_test.yaml @@ -1,4 +1,4 @@ -suite: test deployment agency default +suite: test deployment agency override templates: - deployment.yaml tests: @@ -6,13 +6,36 @@ tests: release: namespace: mds set: + registry: my-registry apis: mds-agency: + version: 1.2.3 + migration: true env: - - name: foo - value: bar - - name: bop - value: bing + - name: env-name + value: env-value + resourcesLimitsCpu: 50m + resourcesLimitsMemory: 51Mi + resourcesRequestsCpu: 5m + resourcesRequestsMemory: 12Mi + timezone: my-timezone + postgresql.postgresqlUsername: my-postgresql-user-name + postgresql.postgresqlDatabase: my-postgresql-database + postgresql.internal: false + postgresql.host: my-postgresql-host + postgresql.hostReader: my-postgresql-host-reader + postgresql.port: 1234 + redis.internal: false + redis.host: my-redis-host + redis.port: 5678 + natsNamespace: my-nats + tenantId: my-tenant + global: + env: + - name: global-env-name + value: global-env-value + slack.channel: my-slack-channel + slack.token: my-slack-token asserts: - isAPIVersion: of: apps/v1 @@ -50,31 +73,27 @@ tests: documentIndex: 0 - equal: path: spec.template.spec.containers[0].image - value: mds-agency:latest - documentIndex: 0 - - equal: - path: spec.template.spec.containers[0].imagePullPolicy - value: IfNotPresent + value: my-registry/mds-agency:1.2.3 documentIndex: 0 - equal: path: spec.template.spec.containers[0].resources.limits.cpu - value: 500m + value: 50m documentIndex: 0 - equal: path: spec.template.spec.containers[0].resources.limits.memory - value: 512Mi + value: 51Mi documentIndex: 0 - equal: path: spec.template.spec.containers[0].resources.requests.cpu - value: 50m + value: 5m documentIndex: 0 - equal: path: spec.template.spec.containers[0].resources.requests.memory - value: 128Mi + value: 12Mi documentIndex: 0 - equal: path: spec.template.spec.containers[0].ports[0].containerPort - value: 4001 + value: 4000 documentIndex: 0 - equal: path: spec.template.spec.containers[0].ports[0].name @@ -86,55 +105,55 @@ tests: documentIndex: 0 - equal: path: spec.template.spec.containers[0].env[0].name - value: PATH_PREFIX + value: PORT documentIndex: 0 - equal: path: spec.template.spec.containers[0].env[0].value - value: /agency + value: "4000" documentIndex: 0 - equal: path: spec.template.spec.containers[0].env[1].name - value: TIMEZONE + value: PATH_PREFIX documentIndex: 0 - equal: path: spec.template.spec.containers[0].env[1].value - value: America/Los_Angeles + value: /agency documentIndex: 0 - equal: path: spec.template.spec.containers[0].env[2].name - value: PG_USER + value: TIMEZONE documentIndex: 0 - equal: path: spec.template.spec.containers[0].env[2].value - value: mdsadmin + value: my-timezone documentIndex: 0 - equal: path: spec.template.spec.containers[0].env[3].name - value: PG_NAME + value: PG_USER documentIndex: 0 - equal: path: spec.template.spec.containers[0].env[3].value - value: mds + value: my-postgresql-user-name documentIndex: 0 - equal: path: spec.template.spec.containers[0].env[4].name - value: PG_PASS + value: PG_NAME documentIndex: 0 - equal: - path: spec.template.spec.containers[0].env[4].valueFrom.secretKeyRef.name - value: pg-pass + path: spec.template.spec.containers[0].env[4].value + value: my-postgresql-database documentIndex: 0 - equal: - path: spec.template.spec.containers[0].env[4].valueFrom.secretKeyRef.key - value: postgresql-password + path: spec.template.spec.containers[0].env[5].name + value: PG_PASS documentIndex: 0 - equal: - path: spec.template.spec.containers[0].env[5].name - value: PG_PORT + path: spec.template.spec.containers[0].env[5].valueFrom.secretKeyRef.name + value: mds-secrets documentIndex: 0 - equal: - path: spec.template.spec.containers[0].env[5].value - value: "5432" + path: spec.template.spec.containers[0].env[5].valueFrom.secretKeyRef.key + value: postgresql-password documentIndex: 0 - equal: path: spec.template.spec.containers[0].env[6].name @@ -142,7 +161,7 @@ tests: documentIndex: 0 - equal: path: spec.template.spec.containers[0].env[6].value - value: mds-postgresql.mds.svc.cluster.local + value: my-postgresql-host documentIndex: 0 - equal: path: spec.template.spec.containers[0].env[7].name @@ -150,53 +169,97 @@ tests: documentIndex: 0 - equal: path: spec.template.spec.containers[0].env[7].value - value: mds-postgresql.mds.svc.cluster.local + value: my-postgresql-host-reader documentIndex: 0 - equal: path: spec.template.spec.containers[0].env[8].name - value: REDIS_HOST + value: PG_PORT documentIndex: 0 - equal: path: spec.template.spec.containers[0].env[8].value - value: mds-redis-master.mds.svc.cluster.local + value: "1234" documentIndex: 0 - equal: path: spec.template.spec.containers[0].env[9].name - value: REDIS_PORT + value: PG_MIGRATIONS documentIndex: 0 - equal: path: spec.template.spec.containers[0].env[9].value - value: "6379" + value: "true" documentIndex: 0 - equal: path: spec.template.spec.containers[0].env[10].name - value: CE_NAME + value: REDIS_HOST documentIndex: 0 - equal: path: spec.template.spec.containers[0].env[10].value - value: mds-agency + value: my-redis-host documentIndex: 0 - equal: path: spec.template.spec.containers[0].env[11].name - value: SINK + value: REDIS_PORT documentIndex: 0 - equal: path: spec.template.spec.containers[0].env[11].value - value: http://default-broker.mds.svc.cluster.local + value: "5678" documentIndex: 0 - equal: path: spec.template.spec.containers[0].env[12].name - value: foo + value: NATS documentIndex: 0 - equal: path: spec.template.spec.containers[0].env[12].value - value: bar + value: nats.my-nats.svc.cluster.local documentIndex: 0 - equal: path: spec.template.spec.containers[0].env[13].name - value: bop + value: STAN_CLUSTER documentIndex: 0 - equal: path: spec.template.spec.containers[0].env[13].value - value: bing + value: nats-streaming + documentIndex: 0 + - equal: + path: spec.template.spec.containers[0].env[14].name + value: TENANT_ID + documentIndex: 0 + - equal: + path: spec.template.spec.containers[0].env[14].value + value: my-tenant + documentIndex: 0 + - equal: + path: spec.template.spec.containers[0].env[15].name + value: SLACK_CHANNEL + documentIndex: 0 + - equal: + path: spec.template.spec.containers[0].env[15].value + value: my-slack-channel + documentIndex: 0 + - equal: + path: spec.template.spec.containers[0].env[16].name + value: SLACK_TOKEN + documentIndex: 0 + - equal: + path: spec.template.spec.containers[0].env[16].valueFrom.secretKeyRef.name + value: mds-secrets + documentIndex: 0 + - equal: + path: spec.template.spec.containers[0].env[16].valueFrom.secretKeyRef.key + value: slack-token + documentIndex: 0 + - equal: + path: spec.template.spec.containers[0].env[17].name + value: env-name + documentIndex: 0 + - equal: + path: spec.template.spec.containers[0].env[17].value + value: env-value + documentIndex: 0 + - equal: + path: spec.template.spec.containers[0].env[18].name + value: global-env-name + documentIndex: 0 + - equal: + path: spec.template.spec.containers[0].env[18].value + value: global-env-value documentIndex: 0 diff --git a/helm/mds/tests/deployment-agency-registry_test.yaml b/helm/mds/tests/deployment-agency-registry_test.yaml deleted file mode 100644 index 817987aec..000000000 --- a/helm/mds/tests/deployment-agency-registry_test.yaml +++ /dev/null @@ -1,179 +0,0 @@ -suite: test deployment agency registry -templates: - - deployment.yaml -tests: - - it: doc 0 - set: - registry: foo-registry - release: - namespace: mds - asserts: - - isAPIVersion: - of: apps/v1 - documentIndex: 0 - - isKind: - of: Deployment - documentIndex: 0 - - equal: - path: metadata.name - value: mds-agency - documentIndex: 0 - - equal: - path: metadata.labels.app - value: mds-agency - documentIndex: 0 - - equal: - path: metadata.namespace - value: mds - documentIndex: 0 - - equal: - path: spec.replicas - value: 1 - documentIndex: 0 - - equal: - path: spec.selector.matchLabels.app - value: mds-agency - documentIndex: 0 - - equal: - path: spec.template.metadata.labels.app - value: mds-agency - documentIndex: 0 - - equal: - path: spec.template.spec.containers[0].name - value: mds-agency - documentIndex: 0 - - equal: - path: spec.template.spec.containers[0].image - value: foo-registry/mds-agency:latest - documentIndex: 0 - - isNull: - path: spec.template.spec.containers[0].imagePullPolicy - documentIndex: 0 - - equal: - path: spec.template.spec.containers[0].resources.limits.cpu - value: 500m - documentIndex: 0 - - equal: - path: spec.template.spec.containers[0].resources.limits.memory - value: 512Mi - documentIndex: 0 - - equal: - path: spec.template.spec.containers[0].resources.requests.cpu - value: 50m - documentIndex: 0 - - equal: - path: spec.template.spec.containers[0].resources.requests.memory - value: 128Mi - documentIndex: 0 - - equal: - path: spec.template.spec.containers[0].ports[0].containerPort - value: 4001 - documentIndex: 0 - - equal: - path: spec.template.spec.containers[0].ports[0].name - value: user-port - documentIndex: 0 - - equal: - path: spec.template.spec.containers[0].ports[0].protocol - value: TCP - documentIndex: 0 - - equal: - path: spec.template.spec.containers[0].env[0].name - value: PATH_PREFIX - documentIndex: 0 - - equal: - path: spec.template.spec.containers[0].env[0].value - value: /agency - documentIndex: 0 - - equal: - path: spec.template.spec.containers[0].env[1].name - value: TIMEZONE - documentIndex: 0 - - equal: - path: spec.template.spec.containers[0].env[1].value - value: America/Los_Angeles - documentIndex: 0 - - equal: - path: spec.template.spec.containers[0].env[2].name - value: PG_USER - documentIndex: 0 - - equal: - path: spec.template.spec.containers[0].env[2].value - value: mdsadmin - documentIndex: 0 - - equal: - path: spec.template.spec.containers[0].env[3].name - value: PG_NAME - documentIndex: 0 - - equal: - path: spec.template.spec.containers[0].env[3].value - value: mds - documentIndex: 0 - - equal: - path: spec.template.spec.containers[0].env[4].name - value: PG_PASS - documentIndex: 0 - - equal: - path: spec.template.spec.containers[0].env[4].valueFrom.secretKeyRef.name - value: pg-pass - documentIndex: 0 - - equal: - path: spec.template.spec.containers[0].env[4].valueFrom.secretKeyRef.key - value: postgresql-password - documentIndex: 0 - - equal: - path: spec.template.spec.containers[0].env[5].name - value: PG_PORT - documentIndex: 0 - - equal: - path: spec.template.spec.containers[0].env[5].value - value: "5432" - documentIndex: 0 - - equal: - path: spec.template.spec.containers[0].env[6].name - value: PG_HOST - documentIndex: 0 - - equal: - path: spec.template.spec.containers[0].env[6].value - value: mds-postgresql.mds.svc.cluster.local - documentIndex: 0 - - equal: - path: spec.template.spec.containers[0].env[7].name - value: PG_HOST_READER - documentIndex: 0 - - equal: - path: spec.template.spec.containers[0].env[7].value - value: mds-postgresql.mds.svc.cluster.local - documentIndex: 0 - - equal: - path: spec.template.spec.containers[0].env[8].name - value: REDIS_HOST - documentIndex: 0 - - equal: - path: spec.template.spec.containers[0].env[8].value - value: mds-redis-master.mds.svc.cluster.local - documentIndex: 0 - - equal: - path: spec.template.spec.containers[0].env[9].name - value: REDIS_PORT - documentIndex: 0 - - equal: - path: spec.template.spec.containers[0].env[9].value - value: "6379" - documentIndex: 0 - - equal: - path: spec.template.spec.containers[0].env[10].name - value: CE_NAME - documentIndex: 0 - - equal: - path: spec.template.spec.containers[0].env[10].value - value: mds-agency - documentIndex: 0 - - equal: - path: spec.template.spec.containers[0].env[11].name - value: SINK - documentIndex: 0 - - equal: - path: spec.template.spec.containers[0].env[11].value - value: http://default-broker.mds.svc.cluster.local - documentIndex: 0 diff --git a/helm/mds/tests/deployment-agency-resource-all_test.yaml b/helm/mds/tests/deployment-agency-resource-all_test.yaml deleted file mode 100644 index 61d37fc03..000000000 --- a/helm/mds/tests/deployment-agency-resource-all_test.yaml +++ /dev/null @@ -1,183 +0,0 @@ -suite: test deployment agency default -templates: - - deployment.yaml -tests: - - it: doc 0 - set: - resourcesLimitsCpu: 250m - resourcesLimitsMemory: 256Mi - resourcesRequestsCpu: 5m - resourcesRequestsMemory: 13Mi - release: - namespace: mds - asserts: - - isAPIVersion: - of: apps/v1 - documentIndex: 0 - - isKind: - of: Deployment - documentIndex: 0 - - equal: - path: metadata.name - value: mds-agency - documentIndex: 0 - - equal: - path: metadata.labels.app - value: mds-agency - documentIndex: 0 - - equal: - path: metadata.namespace - value: mds - documentIndex: 0 - - equal: - path: spec.replicas - value: 1 - documentIndex: 0 - - equal: - path: spec.selector.matchLabels.app - value: mds-agency - documentIndex: 0 - - equal: - path: spec.template.metadata.labels.app - value: mds-agency - documentIndex: 0 - - equal: - path: spec.template.spec.containers[0].name - value: mds-agency - documentIndex: 0 - - equal: - path: spec.template.spec.containers[0].image - value: mds-agency:latest - documentIndex: 0 - - equal: - path: spec.template.spec.containers[0].imagePullPolicy - value: IfNotPresent - documentIndex: 0 - - equal: - path: spec.template.spec.containers[0].resources.limits.cpu - value: 250m - documentIndex: 0 - - equal: - path: spec.template.spec.containers[0].resources.limits.memory - value: 256Mi - documentIndex: 0 - - equal: - path: spec.template.spec.containers[0].resources.requests.cpu - value: 5m - documentIndex: 0 - - equal: - path: spec.template.spec.containers[0].resources.requests.memory - value: 13Mi - documentIndex: 0 - - equal: - path: spec.template.spec.containers[0].ports[0].containerPort - value: 4001 - documentIndex: 0 - - equal: - path: spec.template.spec.containers[0].ports[0].name - value: user-port - documentIndex: 0 - - equal: - path: spec.template.spec.containers[0].ports[0].protocol - value: TCP - documentIndex: 0 - - equal: - path: spec.template.spec.containers[0].env[0].name - value: PATH_PREFIX - documentIndex: 0 - - equal: - path: spec.template.spec.containers[0].env[0].value - value: /agency - documentIndex: 0 - - equal: - path: spec.template.spec.containers[0].env[1].name - value: TIMEZONE - documentIndex: 0 - - equal: - path: spec.template.spec.containers[0].env[1].value - value: America/Los_Angeles - documentIndex: 0 - - equal: - path: spec.template.spec.containers[0].env[2].name - value: PG_USER - documentIndex: 0 - - equal: - path: spec.template.spec.containers[0].env[2].value - value: mdsadmin - documentIndex: 0 - - equal: - path: spec.template.spec.containers[0].env[3].name - value: PG_NAME - documentIndex: 0 - - equal: - path: spec.template.spec.containers[0].env[3].value - value: mds - documentIndex: 0 - - equal: - path: spec.template.spec.containers[0].env[4].name - value: PG_PASS - documentIndex: 0 - - equal: - path: spec.template.spec.containers[0].env[4].valueFrom.secretKeyRef.name - value: pg-pass - documentIndex: 0 - - equal: - path: spec.template.spec.containers[0].env[4].valueFrom.secretKeyRef.key - value: postgresql-password - documentIndex: 0 - - equal: - path: spec.template.spec.containers[0].env[5].name - value: PG_PORT - documentIndex: 0 - - equal: - path: spec.template.spec.containers[0].env[5].value - value: "5432" - documentIndex: 0 - - equal: - path: spec.template.spec.containers[0].env[6].name - value: PG_HOST - documentIndex: 0 - - equal: - path: spec.template.spec.containers[0].env[6].value - value: mds-postgresql.mds.svc.cluster.local - documentIndex: 0 - - equal: - path: spec.template.spec.containers[0].env[7].name - value: PG_HOST_READER - documentIndex: 0 - - equal: - path: spec.template.spec.containers[0].env[7].value - value: mds-postgresql.mds.svc.cluster.local - documentIndex: 0 - - equal: - path: spec.template.spec.containers[0].env[8].name - value: REDIS_HOST - documentIndex: 0 - - equal: - path: spec.template.spec.containers[0].env[8].value - value: mds-redis-master.mds.svc.cluster.local - documentIndex: 0 - - equal: - path: spec.template.spec.containers[0].env[9].name - value: REDIS_PORT - documentIndex: 0 - - equal: - path: spec.template.spec.containers[0].env[9].value - value: "6379" - documentIndex: 0 - - equal: - path: spec.template.spec.containers[0].env[10].name - value: CE_NAME - documentIndex: 0 - - equal: - path: spec.template.spec.containers[0].env[10].value - value: mds-agency - documentIndex: 0 - - equal: - path: spec.template.spec.containers[0].env[11].name - value: SINK - documentIndex: 0 - - equal: - path: spec.template.spec.containers[0].env[11].value - value: http://default-broker.mds.svc.cluster.local - documentIndex: 0 diff --git a/helm/mds/tests/deployment-agency-default_test.yaml b/helm/mds/tests/deployment-agency_test.yaml similarity index 88% rename from helm/mds/tests/deployment-agency-default_test.yaml rename to helm/mds/tests/deployment-agency_test.yaml index 3a745ba2e..b30fea0ce 100644 --- a/helm/mds/tests/deployment-agency-default_test.yaml +++ b/helm/mds/tests/deployment-agency_test.yaml @@ -1,4 +1,4 @@ -suite: test deployment agency default +suite: test deployment agency templates: - deployment.yaml tests: @@ -44,10 +44,6 @@ tests: path: spec.template.spec.containers[0].image value: mds-agency:latest documentIndex: 0 - - equal: - path: spec.template.spec.containers[0].imagePullPolicy - value: IfNotPresent - documentIndex: 0 - equal: path: spec.template.spec.containers[0].resources.limits.cpu value: 500m @@ -66,7 +62,7 @@ tests: documentIndex: 0 - equal: path: spec.template.spec.containers[0].ports[0].containerPort - value: 4001 + value: 4000 documentIndex: 0 - equal: path: spec.template.spec.containers[0].ports[0].name @@ -78,55 +74,55 @@ tests: documentIndex: 0 - equal: path: spec.template.spec.containers[0].env[0].name - value: PATH_PREFIX + value: PORT documentIndex: 0 - equal: path: spec.template.spec.containers[0].env[0].value - value: /agency + value: "4000" documentIndex: 0 - equal: path: spec.template.spec.containers[0].env[1].name - value: TIMEZONE + value: PATH_PREFIX documentIndex: 0 - equal: path: spec.template.spec.containers[0].env[1].value - value: America/Los_Angeles + value: /agency documentIndex: 0 - equal: path: spec.template.spec.containers[0].env[2].name - value: PG_USER + value: TIMEZONE documentIndex: 0 - equal: path: spec.template.spec.containers[0].env[2].value - value: mdsadmin + value: America/Los_Angeles documentIndex: 0 - equal: path: spec.template.spec.containers[0].env[3].name - value: PG_NAME + value: PG_USER documentIndex: 0 - equal: path: spec.template.spec.containers[0].env[3].value - value: mds + value: mdsadmin documentIndex: 0 - equal: path: spec.template.spec.containers[0].env[4].name - value: PG_PASS + value: PG_NAME documentIndex: 0 - equal: - path: spec.template.spec.containers[0].env[4].valueFrom.secretKeyRef.name - value: pg-pass + path: spec.template.spec.containers[0].env[4].value + value: mds documentIndex: 0 - equal: - path: spec.template.spec.containers[0].env[4].valueFrom.secretKeyRef.key - value: postgresql-password + path: spec.template.spec.containers[0].env[5].name + value: PG_PASS documentIndex: 0 - equal: - path: spec.template.spec.containers[0].env[5].name - value: PG_PORT + path: spec.template.spec.containers[0].env[5].valueFrom.secretKeyRef.name + value: mds-secrets documentIndex: 0 - equal: - path: spec.template.spec.containers[0].env[5].value - value: "5432" + path: spec.template.spec.containers[0].env[5].valueFrom.secretKeyRef.key + value: postgresql-password documentIndex: 0 - equal: path: spec.template.spec.containers[0].env[6].name @@ -138,11 +134,11 @@ tests: documentIndex: 0 - equal: path: spec.template.spec.containers[0].env[7].name - value: PG_HOST_READER + value: PG_PORT documentIndex: 0 - equal: path: spec.template.spec.containers[0].env[7].value - value: mds-postgresql.mds.svc.cluster.local + value: "5432" documentIndex: 0 - equal: path: spec.template.spec.containers[0].env[8].name @@ -162,17 +158,24 @@ tests: documentIndex: 0 - equal: path: spec.template.spec.containers[0].env[10].name - value: CE_NAME + value: NATS documentIndex: 0 - equal: path: spec.template.spec.containers[0].env[10].value - value: mds-agency + value: nats.nats.svc.cluster.local documentIndex: 0 - equal: path: spec.template.spec.containers[0].env[11].name - value: SINK + value: STAN_CLUSTER documentIndex: 0 - equal: path: spec.template.spec.containers[0].env[11].value - value: http://default-broker.mds.svc.cluster.local + value: nats-streaming + documentIndex: 0 + - equal: + path: spec.template.spec.containers[0].env[12].name + value: TENANT_ID + documentIndex: 0 + - isEmpty: + path: spec.template.spec.containers[0].env[12].value documentIndex: 0 diff --git a/helm/mds/tests/deployment-all-no-cache_test.yaml b/helm/mds/tests/deployment-all-no-cache_test.yaml deleted file mode 100644 index fdb91c4ec..000000000 --- a/helm/mds/tests/deployment-all-no-cache_test.yaml +++ /dev/null @@ -1,10 +0,0 @@ -suite: test deployment all no-cache -templates: - - deployment.yaml -tests: - - it: all docs - release: - namespace: mds - asserts: - - hasDocuments: - count: 8 diff --git a/helm/mds/tests/deployment-all-no-db_test.yaml b/helm/mds/tests/deployment-all-no-db_test.yaml deleted file mode 100644 index dbc8f210a..000000000 --- a/helm/mds/tests/deployment-all-no-db_test.yaml +++ /dev/null @@ -1,10 +0,0 @@ -suite: test deployment all no-db -templates: - - deployment.yaml -tests: - - it: all docs - release: - namespace: mds - asserts: - - hasDocuments: - count: 8 diff --git a/helm/mds/tests/deployment-all-no-events_test.yaml b/helm/mds/tests/deployment-all-no-events_test.yaml deleted file mode 100644 index 2189818a0..000000000 --- a/helm/mds/tests/deployment-all-no-events_test.yaml +++ /dev/null @@ -1,10 +0,0 @@ -suite: test deployment all no-events -templates: - - deployment.yaml -tests: - - it: all docs - release: - namespace: mds - asserts: - - hasDocuments: - count: 8 diff --git a/helm/mds/tests/deployment-all-registry_test.yaml b/helm/mds/tests/deployment-all-registry_test.yaml deleted file mode 100644 index e2bff1f42..000000000 --- a/helm/mds/tests/deployment-all-registry_test.yaml +++ /dev/null @@ -1,12 +0,0 @@ -suite: test deployment all registry -templates: - - deployment.yaml -tests: - - it: all docs - set: - registry: foo-registry - release: - namespace: mds - asserts: - - hasDocuments: - count: 8 \ No newline at end of file diff --git a/helm/mds/tests/deployment-all-resource-all_test.yaml b/helm/mds/tests/deployment-all-resource-all_test.yaml deleted file mode 100644 index 15666d54f..000000000 --- a/helm/mds/tests/deployment-all-resource-all_test.yaml +++ /dev/null @@ -1,309 +0,0 @@ -suite: test deployment agency default -templates: - - deployment.yaml -tests: - - it: doc 0 - set: - apis: - mds-agency: - resourcesLimitsCpu: 250m - resourcesLimitsMemory: 256Mi - resourcesRequestsCpu: 5m - resourcesRequestsMemory: 13Mi - mds-audit: - resourcesLimitsCpu: 251m - resourcesLimitsMemory: 257Mi - resourcesRequestsCpu: 6m - resourcesRequestsMemory: 14Mi - mds-compliance: - resourcesLimitsCpu: 252m - resourcesLimitsMemory: 258Mi - resourcesRequestsCpu: 7m - resourcesRequestsMemory: 15Mi - mds-daily: - resourcesLimitsCpu: 253m - resourcesLimitsMemory: 259Mi - resourcesRequestsCpu: 8m - resourcesRequestsMemory: 16Mi - mds-native: - resourcesLimitsCpu: 254m - resourcesLimitsMemory: 260Mi - resourcesRequestsCpu: 9m - resourcesRequestsMemory: 17Mi - mds-policy: - resourcesLimitsCpu: 255m - resourcesLimitsMemory: 261Mi - resourcesRequestsCpu: 10m - resourcesRequestsMemory: 18Mi - mds-policy-author: - resourcesLimitsCpu: 256m - resourcesLimitsMemory: 262Mi - resourcesRequestsCpu: 11m - resourcesRequestsMemory: 19Mi - release: - namespace: mds - asserts: - - isAPIVersion: - of: apps/v1 - documentIndex: 0 - - isKind: - of: Deployment - documentIndex: 0 - - equal: - path: metadata.name - value: mds-agency - documentIndex: 0 - - equal: - path: metadata.labels.app - value: mds-agency - documentIndex: 0 - - equal: - path: metadata.namespace - value: mds - documentIndex: 0 - - equal: - path: spec.template.spec.containers[0].resources.limits.cpu - value: 250m - documentIndex: 0 - - equal: - path: spec.template.spec.containers[0].resources.limits.memory - value: 256Mi - documentIndex: 0 - - equal: - path: spec.template.spec.containers[0].resources.requests.cpu - value: 5m - documentIndex: 0 - - equal: - path: spec.template.spec.containers[0].resources.requests.memory - value: 13Mi - documentIndex: 0 - - isAPIVersion: - of: apps/v1 - documentIndex: 1 - - isKind: - of: Deployment - documentIndex: 1 - - equal: - path: metadata.name - value: mds-audit - documentIndex: 1 - - equal: - path: metadata.labels.app - value: mds-audit - documentIndex: 1 - - equal: - path: metadata.namespace - value: mds - documentIndex: 1 - - equal: - path: spec.template.spec.containers[0].resources.limits.cpu - value: 251m - documentIndex: 1 - - equal: - path: spec.template.spec.containers[0].resources.limits.memory - value: 257Mi - documentIndex: 1 - - equal: - path: spec.template.spec.containers[0].resources.requests.cpu - value: 6m - documentIndex: 1 - - equal: - path: spec.template.spec.containers[0].resources.requests.memory - value: 14Mi - documentIndex: 1 - - isAPIVersion: - of: apps/v1 - documentIndex: 2 - - isKind: - of: Deployment - documentIndex: 2 - - equal: - path: metadata.name - value: mds-compliance - documentIndex: 2 - - equal: - path: metadata.labels.app - value: mds-compliance - documentIndex: 2 - - equal: - path: metadata.namespace - value: mds - documentIndex: 2 - - equal: - path: spec.template.spec.containers[0].resources.limits.cpu - value: 252m - documentIndex: 2 - - equal: - path: spec.template.spec.containers[0].resources.limits.memory - value: 258Mi - documentIndex: 2 - - equal: - path: spec.template.spec.containers[0].resources.requests.cpu - value: 7m - documentIndex: 2 - - equal: - path: spec.template.spec.containers[0].resources.requests.memory - value: 15Mi - documentIndex: 2 - - isAPIVersion: - of: apps/v1 - documentIndex: 3 - - isKind: - of: Deployment - documentIndex: 3 - - equal: - path: metadata.name - value: mds-daily - documentIndex: 3 - - equal: - path: metadata.labels.app - value: mds-daily - documentIndex: 3 - - equal: - path: metadata.namespace - value: mds - documentIndex: 3 - - equal: - path: spec.template.spec.containers[0].resources.limits.cpu - value: 253m - documentIndex: 3 - - equal: - path: spec.template.spec.containers[0].resources.limits.memory - value: 259Mi - documentIndex: 3 - - equal: - path: spec.template.spec.containers[0].resources.requests.cpu - value: 8m - documentIndex: 3 - - equal: - path: spec.template.spec.containers[0].resources.requests.memory - value: 16Mi - documentIndex: 3 - - isAPIVersion: - of: apps/v1 - documentIndex: 4 - - isKind: - of: Deployment - documentIndex: 4 - - equal: - path: metadata.name - value: mds-native - documentIndex: 4 - - equal: - path: metadata.labels.app - value: mds-native - documentIndex: 4 - - equal: - path: metadata.namespace - value: mds - documentIndex: 4 - - equal: - path: spec.template.spec.containers[0].resources.limits.cpu - value: 254m - documentIndex: 4 - - equal: - path: spec.template.spec.containers[0].resources.limits.memory - value: 260Mi - documentIndex: 4 - - equal: - path: spec.template.spec.containers[0].resources.requests.cpu - value: 9m - documentIndex: 4 - - equal: - path: spec.template.spec.containers[0].resources.requests.memory - value: 17Mi - documentIndex: 4 - - isAPIVersion: - of: apps/v1 - documentIndex: 5 - - isKind: - of: Deployment - documentIndex: 5 - - equal: - path: metadata.name - value: mds-policy - documentIndex: 5 - - equal: - path: metadata.labels.app - value: mds-policy - documentIndex: 5 - - equal: - path: metadata.namespace - value: mds - documentIndex: 5 - - equal: - path: spec.template.spec.containers[0].resources.limits.cpu - value: 255m - documentIndex: 5 - - equal: - path: spec.template.spec.containers[0].resources.limits.memory - value: 261Mi - documentIndex: 5 - - equal: - path: spec.template.spec.containers[0].resources.requests.cpu - value: 10m - documentIndex: 5 - - equal: - path: spec.template.spec.containers[0].resources.requests.memory - value: 18Mi - documentIndex: 5 - - isAPIVersion: - of: apps/v1 - documentIndex: 6 - - isKind: - of: Deployment - documentIndex: 6 - - equal: - path: metadata.name - value: mds-policy-author - documentIndex: 6 - - equal: - path: metadata.labels.app - value: mds-policy-author - documentIndex: 6 - - equal: - path: metadata.namespace - value: mds - documentIndex: 6 - - equal: - path: spec.template.spec.containers[0].resources.limits.cpu - value: 256m - documentIndex: 6 - - equal: - path: spec.template.spec.containers[0].resources.limits.memory - value: 262Mi - documentIndex: 6 - - equal: - path: spec.template.spec.containers[0].resources.requests.cpu - value: 11m - documentIndex: 6 - - equal: - path: spec.template.spec.containers[0].resources.requests.memory - value: 19Mi - documentIndex: 6 - - isAPIVersion: - of: apps/v1 - documentIndex: 7 - - isKind: - of: Deployment - documentIndex: 7 - - equal: - path: metadata.namespace - value: mds - documentIndex: 7 - - equal: - path: spec.template.spec.containers[0].resources.limits.cpu - value: 257m - documentIndex: 7 - - equal: - path: spec.template.spec.containers[0].resources.limits.memory - value: 263Mi - documentIndex: 7 - - equal: - path: spec.template.spec.containers[0].resources.requests.cpu - value: 12m - documentIndex: 7 - - equal: - path: spec.template.spec.containers[0].resources.requests.memory - value: 20Mi - documentIndex: 7 diff --git a/helm/mds/tests/deployment-all-secret_test.yaml b/helm/mds/tests/deployment-all-secret_test.yaml deleted file mode 100644 index f178651cb..000000000 --- a/helm/mds/tests/deployment-all-secret_test.yaml +++ /dev/null @@ -1,12 +0,0 @@ -suite: test deployment all default -templates: - - deployment.yaml -tests: - - it: all - release: - namespace: mds - set: - pgPass: pg-pass - asserts: - - hasDocuments: - count: 9 diff --git a/helm/mds/tests/deployment-all-default_test.yaml b/helm/mds/tests/deployment-all_test.yaml similarity index 88% rename from helm/mds/tests/deployment-all-default_test.yaml rename to helm/mds/tests/deployment-all_test.yaml index 0af90e0a9..0fcf1abdf 100644 --- a/helm/mds/tests/deployment-all-default_test.yaml +++ b/helm/mds/tests/deployment-all_test.yaml @@ -7,4 +7,4 @@ tests: namespace: mds asserts: - hasDocuments: - count: 8 + count: 14 diff --git a/helm/mds/tests/deployment-audit-env_test.yaml b/helm/mds/tests/deployment-audit-env_test.yaml deleted file mode 100644 index b5cfbdeed..000000000 --- a/helm/mds/tests/deployment-audit-env_test.yaml +++ /dev/null @@ -1,202 +0,0 @@ -suite: test deployment audit default -templates: - - deployment.yaml -tests: - - it: doc 1 - release: - namespace: mds - set: - apis: - mds-audit: - env: - - name: foo - value: bar - - name: bop - value: bing - asserts: - - isAPIVersion: - of: apps/v1 - documentIndex: 1 - - isKind: - of: Deployment - documentIndex: 1 - - equal: - path: metadata.name - value: mds-audit - documentIndex: 1 - - equal: - path: metadata.labels.app - value: mds-audit - documentIndex: 1 - - equal: - path: metadata.namespace - value: mds - documentIndex: 1 - - equal: - path: spec.replicas - value: 1 - documentIndex: 1 - - equal: - path: spec.selector.matchLabels.app - value: mds-audit - documentIndex: 1 - - equal: - path: spec.template.metadata.labels.app - value: mds-audit - documentIndex: 1 - - equal: - path: spec.template.spec.containers[0].name - value: mds-audit - documentIndex: 1 - - equal: - path: spec.template.spec.containers[0].image - value: mds-audit:latest - documentIndex: 1 - - equal: - path: spec.template.spec.containers[0].imagePullPolicy - value: IfNotPresent - documentIndex: 1 - - equal: - path: spec.template.spec.containers[0].resources.limits.cpu - value: 500m - documentIndex: 1 - - equal: - path: spec.template.spec.containers[0].resources.limits.memory - value: 512Mi - documentIndex: 1 - - equal: - path: spec.template.spec.containers[0].resources.requests.cpu - value: 50m - documentIndex: 1 - - equal: - path: spec.template.spec.containers[0].resources.requests.memory - value: 128Mi - documentIndex: 1 - - equal: - path: spec.template.spec.containers[0].ports[0].containerPort - value: 4002 - documentIndex: 1 - - equal: - path: spec.template.spec.containers[0].ports[0].name - value: user-port - documentIndex: 1 - - equal: - path: spec.template.spec.containers[0].ports[0].protocol - value: TCP - documentIndex: 1 - - equal: - path: spec.template.spec.containers[0].env[0].name - value: PATH_PREFIX - documentIndex: 1 - - equal: - path: spec.template.spec.containers[0].env[0].value - value: /audit - documentIndex: 1 - - equal: - path: spec.template.spec.containers[0].env[1].name - value: TIMEZONE - documentIndex: 1 - - equal: - path: spec.template.spec.containers[0].env[1].value - value: America/Los_Angeles - documentIndex: 1 - - equal: - path: spec.template.spec.containers[0].env[2].name - value: PG_USER - documentIndex: 1 - - equal: - path: spec.template.spec.containers[0].env[2].value - value: mdsadmin - documentIndex: 1 - - equal: - path: spec.template.spec.containers[0].env[3].name - value: PG_NAME - documentIndex: 1 - - equal: - path: spec.template.spec.containers[0].env[3].value - value: mds - documentIndex: 1 - - equal: - path: spec.template.spec.containers[0].env[4].name - value: PG_PASS - documentIndex: 1 - - equal: - path: spec.template.spec.containers[0].env[4].valueFrom.secretKeyRef.name - value: pg-pass - documentIndex: 1 - - equal: - path: spec.template.spec.containers[0].env[4].valueFrom.secretKeyRef.key - value: postgresql-password - documentIndex: 1 - - equal: - path: spec.template.spec.containers[0].env[5].name - value: PG_PORT - documentIndex: 1 - - equal: - path: spec.template.spec.containers[0].env[5].value - value: "5432" - documentIndex: 1 - - equal: - path: spec.template.spec.containers[0].env[6].name - value: PG_HOST - documentIndex: 1 - - equal: - path: spec.template.spec.containers[0].env[6].value - value: mds-postgresql.mds.svc.cluster.local - documentIndex: 1 - - equal: - path: spec.template.spec.containers[0].env[7].name - value: PG_HOST_READER - documentIndex: 1 - - equal: - path: spec.template.spec.containers[0].env[7].value - value: mds-postgresql.mds.svc.cluster.local - documentIndex: 1 - - equal: - path: spec.template.spec.containers[0].env[8].name - value: REDIS_HOST - documentIndex: 1 - - equal: - path: spec.template.spec.containers[0].env[8].value - value: mds-redis-master.mds.svc.cluster.local - documentIndex: 1 - - equal: - path: spec.template.spec.containers[0].env[9].name - value: REDIS_PORT - documentIndex: 1 - - equal: - path: spec.template.spec.containers[0].env[9].value - value: "6379" - documentIndex: 1 - - equal: - path: spec.template.spec.containers[0].env[10].name - value: CE_NAME - documentIndex: 1 - - equal: - path: spec.template.spec.containers[0].env[10].value - value: mds-audit - documentIndex: 1 - - equal: - path: spec.template.spec.containers[0].env[11].name - value: SINK - documentIndex: 1 - - equal: - path: spec.template.spec.containers[0].env[11].value - value: http://default-broker.mds.svc.cluster.local - documentIndex: 1 - - equal: - path: spec.template.spec.containers[0].env[12].name - value: foo - documentIndex: 1 - - equal: - path: spec.template.spec.containers[0].env[12].value - value: bar - documentIndex: 1 - - equal: - path: spec.template.spec.containers[0].env[13].name - value: bop - documentIndex: 1 - - equal: - path: spec.template.spec.containers[0].env[13].value - value: bing - documentIndex: 1 \ No newline at end of file diff --git a/helm/mds/tests/deployment-audit-no-cache_test.yaml b/helm/mds/tests/deployment-audit-no-cache_test.yaml deleted file mode 100644 index af16b7b4d..000000000 --- a/helm/mds/tests/deployment-audit-no-cache_test.yaml +++ /dev/null @@ -1,164 +0,0 @@ -suite: test deployment audit no-cache -templates: - - deployment.yaml -tests: - - it: doc 1 - release: - namespace: mds - set: - apis.mds-audit.useCache: false - asserts: - - isAPIVersion: - of: apps/v1 - documentIndex: 1 - - isKind: - of: Deployment - documentIndex: 1 - - equal: - path: metadata.name - value: mds-audit - documentIndex: 1 - - equal: - path: metadata.labels.app - value: mds-audit - documentIndex: 1 - - equal: - path: metadata.namespace - value: mds - documentIndex: 1 - - equal: - path: spec.replicas - value: 1 - documentIndex: 1 - - equal: - path: spec.selector.matchLabels.app - value: mds-audit - documentIndex: 1 - - equal: - path: spec.template.metadata.labels.app - value: mds-audit - documentIndex: 1 - - equal: - path: spec.template.spec.containers[0].name - value: mds-audit - documentIndex: 1 - - equal: - path: spec.template.spec.containers[0].image - value: mds-audit:latest - documentIndex: 1 - - equal: - path: spec.template.spec.containers[0].imagePullPolicy - value: IfNotPresent - documentIndex: 1 - - equal: - path: spec.template.spec.containers[0].resources.limits.cpu - value: 500m - documentIndex: 1 - - equal: - path: spec.template.spec.containers[0].resources.limits.memory - value: 512Mi - documentIndex: 1 - - equal: - path: spec.template.spec.containers[0].resources.requests.cpu - value: 50m - documentIndex: 1 - - equal: - path: spec.template.spec.containers[0].resources.requests.memory - value: 128Mi - documentIndex: 1 - - equal: - path: spec.template.spec.containers[0].ports[0].containerPort - value: 4002 - documentIndex: 1 - - equal: - path: spec.template.spec.containers[0].ports[0].name - value: user-port - documentIndex: 1 - - equal: - path: spec.template.spec.containers[0].ports[0].protocol - value: TCP - documentIndex: 1 - - equal: - path: spec.template.spec.containers[0].env[0].name - value: PATH_PREFIX - documentIndex: 1 - - equal: - path: spec.template.spec.containers[0].env[0].value - value: /audit - documentIndex: 1 - - equal: - path: spec.template.spec.containers[0].env[1].name - value: TIMEZONE - documentIndex: 1 - - equal: - path: spec.template.spec.containers[0].env[1].value - value: America/Los_Angeles - documentIndex: 1 - - equal: - path: spec.template.spec.containers[0].env[2].name - value: PG_USER - documentIndex: 1 - - equal: - path: spec.template.spec.containers[0].env[2].value - value: mdsadmin - documentIndex: 1 - - equal: - path: spec.template.spec.containers[0].env[3].name - value: PG_NAME - documentIndex: 1 - - equal: - path: spec.template.spec.containers[0].env[3].value - value: mds - documentIndex: 1 - - equal: - path: spec.template.spec.containers[0].env[4].name - value: PG_PASS - documentIndex: 1 - - equal: - path: spec.template.spec.containers[0].env[4].valueFrom.secretKeyRef.name - value: pg-pass - documentIndex: 1 - - equal: - path: spec.template.spec.containers[0].env[4].valueFrom.secretKeyRef.key - value: postgresql-password - documentIndex: 1 - - equal: - path: spec.template.spec.containers[0].env[5].name - value: PG_PORT - documentIndex: 1 - - equal: - path: spec.template.spec.containers[0].env[5].value - value: "5432" - documentIndex: 1 - - equal: - path: spec.template.spec.containers[0].env[6].name - value: PG_HOST - documentIndex: 1 - - equal: - path: spec.template.spec.containers[0].env[6].value - value: mds-postgresql.mds.svc.cluster.local - documentIndex: 1 - - equal: - path: spec.template.spec.containers[0].env[7].name - value: PG_HOST_READER - documentIndex: 1 - - equal: - path: spec.template.spec.containers[0].env[7].value - value: mds-postgresql.mds.svc.cluster.local - documentIndex: 1 - - equal: - path: spec.template.spec.containers[0].env[8].name - value: CE_NAME - documentIndex: 1 - - equal: - path: spec.template.spec.containers[0].env[8].value - value: mds-audit - documentIndex: 1 - - equal: - path: spec.template.spec.containers[0].env[9].name - value: SINK - documentIndex: 1 - - equal: - path: spec.template.spec.containers[0].env[9].value - value: http://default-broker.mds.svc.cluster.local - documentIndex: 1 diff --git a/helm/mds/tests/deployment-audit-no-db_test.yaml b/helm/mds/tests/deployment-audit-no-db_test.yaml deleted file mode 100644 index 0e51a7fe1..000000000 --- a/helm/mds/tests/deployment-audit-no-db_test.yaml +++ /dev/null @@ -1,128 +0,0 @@ -suite: test deployment audit no-db -templates: - - deployment.yaml -tests: - - it: doc 1 - release: - namespace: mds - set: - apis.mds-audit.useDB: false - asserts: - - isAPIVersion: - of: apps/v1 - documentIndex: 1 - - isKind: - of: Deployment - documentIndex: 1 - - equal: - path: metadata.name - value: mds-audit - documentIndex: 1 - - equal: - path: metadata.labels.app - value: mds-audit - documentIndex: 1 - - equal: - path: metadata.namespace - value: mds - documentIndex: 1 - - equal: - path: spec.replicas - value: 1 - documentIndex: 1 - - equal: - path: spec.selector.matchLabels.app - value: mds-audit - documentIndex: 1 - - equal: - path: spec.template.metadata.labels.app - value: mds-audit - documentIndex: 1 - - equal: - path: spec.template.spec.containers[0].name - value: mds-audit - documentIndex: 1 - - equal: - path: spec.template.spec.containers[0].image - value: mds-audit:latest - documentIndex: 1 - - equal: - path: spec.template.spec.containers[0].imagePullPolicy - value: IfNotPresent - documentIndex: 1 - - equal: - path: spec.template.spec.containers[0].resources.limits.cpu - value: 500m - documentIndex: 1 - - equal: - path: spec.template.spec.containers[0].resources.limits.memory - value: 512Mi - documentIndex: 1 - - equal: - path: spec.template.spec.containers[0].resources.requests.cpu - value: 50m - documentIndex: 1 - - equal: - path: spec.template.spec.containers[0].resources.requests.memory - value: 128Mi - documentIndex: 1 - - equal: - path: spec.template.spec.containers[0].ports[0].containerPort - value: 4002 - documentIndex: 1 - - equal: - path: spec.template.spec.containers[0].ports[0].name - value: user-port - documentIndex: 1 - - equal: - path: spec.template.spec.containers[0].ports[0].protocol - value: TCP - documentIndex: 1 - - equal: - path: spec.template.spec.containers[0].env[0].name - value: PATH_PREFIX - documentIndex: 1 - - equal: - path: spec.template.spec.containers[0].env[0].value - value: /audit - documentIndex: 1 - - equal: - path: spec.template.spec.containers[0].env[1].name - value: TIMEZONE - documentIndex: 1 - - equal: - path: spec.template.spec.containers[0].env[1].value - value: America/Los_Angeles - documentIndex: 1 - - equal: - path: spec.template.spec.containers[0].env[2].name - value: REDIS_HOST - documentIndex: 1 - - equal: - path: spec.template.spec.containers[0].env[2].value - value: mds-redis-master.mds.svc.cluster.local - documentIndex: 1 - - equal: - path: spec.template.spec.containers[0].env[3].name - value: REDIS_PORT - documentIndex: 1 - - equal: - path: spec.template.spec.containers[0].env[3].value - value: "6379" - documentIndex: 1 - - equal: - path: spec.template.spec.containers[0].env[4].name - value: CE_NAME - documentIndex: 1 - - equal: - path: spec.template.spec.containers[0].env[4].value - value: mds-audit - documentIndex: 1 - - equal: - path: spec.template.spec.containers[0].env[5].name - value: SINK - documentIndex: 1 - - equal: - path: spec.template.spec.containers[0].env[5].value - value: http://default-broker.mds.svc.cluster.local - documentIndex: 1 diff --git a/helm/mds/tests/deployment-audit-no-events_test.yaml b/helm/mds/tests/deployment-audit-no-events_test.yaml deleted file mode 100644 index 4d006152d..000000000 --- a/helm/mds/tests/deployment-audit-no-events_test.yaml +++ /dev/null @@ -1,168 +0,0 @@ -suite: test deployment audit no-events -templates: - - deployment.yaml -tests: - - it: doc 1 - release: - namespace: mds - set: - apis.mds-audit.useEvents: false - asserts: - - isAPIVersion: - of: apps/v1 - documentIndex: 1 - - isKind: - of: Deployment - documentIndex: 1 - - equal: - path: metadata.name - value: mds-audit - documentIndex: 1 - - equal: - path: metadata.labels.app - value: mds-audit - documentIndex: 1 - - equal: - path: metadata.namespace - value: mds - documentIndex: 1 - - equal: - path: spec.replicas - value: 1 - documentIndex: 1 - - equal: - path: spec.selector.matchLabels.app - value: mds-audit - documentIndex: 1 - - equal: - path: spec.template.metadata.labels.app - value: mds-audit - documentIndex: 1 - - equal: - path: spec.template.spec.containers[0].name - value: mds-audit - documentIndex: 1 - - equal: - path: spec.template.spec.containers[0].image - value: mds-audit:latest - documentIndex: 1 - - equal: - path: spec.template.spec.containers[0].imagePullPolicy - value: IfNotPresent - documentIndex: 1 - - equal: - path: spec.template.spec.containers[0].resources.limits.cpu - value: 500m - documentIndex: 1 - - equal: - path: spec.template.spec.containers[0].resources.limits.memory - value: 512Mi - documentIndex: 1 - - equal: - path: spec.template.spec.containers[0].resources.requests.cpu - value: 50m - documentIndex: 1 - - equal: - path: spec.template.spec.containers[0].resources.requests.memory - value: 128Mi - documentIndex: 1 - - equal: - path: spec.template.spec.containers[0].ports[0].containerPort - value: 4002 - documentIndex: 1 - - equal: - path: spec.template.spec.containers[0].ports[0].name - value: user-port - documentIndex: 1 - - equal: - path: spec.template.spec.containers[0].ports[0].protocol - value: TCP - documentIndex: 1 - - equal: - path: spec.template.spec.containers[0].env[0].name - value: PATH_PREFIX - documentIndex: 1 - - equal: - path: spec.template.spec.containers[0].env[0].value - value: /audit - documentIndex: 1 - - equal: - path: spec.template.spec.containers[0].env[1].name - value: TIMEZONE - documentIndex: 1 - - equal: - path: spec.template.spec.containers[0].env[1].value - value: America/Los_Angeles - documentIndex: 1 - - equal: - path: spec.template.spec.containers[0].env[2].name - value: PG_USER - documentIndex: 1 - - equal: - path: spec.template.spec.containers[0].env[2].value - value: mdsadmin - documentIndex: 1 - - equal: - path: spec.template.spec.containers[0].env[3].name - value: PG_NAME - documentIndex: 1 - - equal: - path: spec.template.spec.containers[0].env[3].value - value: mds - documentIndex: 1 - - equal: - path: spec.template.spec.containers[0].env[4].name - value: PG_PASS - documentIndex: 1 - - equal: - path: spec.template.spec.containers[0].env[4].valueFrom.secretKeyRef.name - value: pg-pass - documentIndex: 1 - - equal: - path: spec.template.spec.containers[0].env[4].valueFrom.secretKeyRef.key - value: postgresql-password - documentIndex: 1 - - equal: - path: spec.template.spec.containers[0].env[5].name - value: PG_PORT - documentIndex: 1 - - equal: - path: spec.template.spec.containers[0].env[5].value - value: "5432" - documentIndex: 1 - - equal: - path: spec.template.spec.containers[0].env[6].name - value: PG_HOST - documentIndex: 1 - - equal: - path: spec.template.spec.containers[0].env[6].value - value: mds-postgresql.mds.svc.cluster.local - documentIndex: 1 - - equal: - path: spec.template.spec.containers[0].env[7].name - value: PG_HOST_READER - documentIndex: 1 - - equal: - path: spec.template.spec.containers[0].env[7].value - value: mds-postgresql.mds.svc.cluster.local - documentIndex: 1 - - equal: - path: spec.template.spec.containers[0].env[8].name - value: REDIS_HOST - documentIndex: 1 - - equal: - path: spec.template.spec.containers[0].env[8].value - value: mds-redis-master.mds.svc.cluster.local - documentIndex: 1 - - equal: - path: spec.template.spec.containers[0].env[9].name - value: REDIS_PORT - documentIndex: 1 - - equal: - path: spec.template.spec.containers[0].env[9].value - value: "6379" - documentIndex: 1 - # fixme: test non-existence - # - isNull: - # path: spec.template.spec.containers[0].env[10] - # documentIndex: 1 \ No newline at end of file diff --git a/helm/mds/tests/deployment-audit-registry_test.yaml b/helm/mds/tests/deployment-audit-registry_test.yaml deleted file mode 100644 index 6a651d427..000000000 --- a/helm/mds/tests/deployment-audit-registry_test.yaml +++ /dev/null @@ -1,179 +0,0 @@ -suite: test deployment audit registry -templates: - - deployment.yaml -tests: - - it: doc 1 - set: - registry: foo-registry - release: - namespace: mds - asserts: - - isAPIVersion: - of: apps/v1 - documentIndex: 1 - - isKind: - of: Deployment - documentIndex: 1 - - equal: - path: metadata.name - value: mds-audit - documentIndex: 1 - - equal: - path: metadata.labels.app - value: mds-audit - documentIndex: 1 - - equal: - path: metadata.namespace - value: mds - documentIndex: 1 - - equal: - path: spec.replicas - value: 1 - documentIndex: 1 - - equal: - path: spec.selector.matchLabels.app - value: mds-audit - documentIndex: 1 - - equal: - path: spec.template.metadata.labels.app - value: mds-audit - documentIndex: 1 - - equal: - path: spec.template.spec.containers[0].name - value: mds-audit - documentIndex: 1 - - equal: - path: spec.template.spec.containers[0].image - value: foo-registry/mds-audit:latest - documentIndex: 1 - - isNull: - path: spec.template.spec.containers[0].imagePullPolicy - documentIndex: 1 - - equal: - path: spec.template.spec.containers[0].resources.limits.cpu - value: 500m - documentIndex: 1 - - equal: - path: spec.template.spec.containers[0].resources.limits.memory - value: 512Mi - documentIndex: 1 - - equal: - path: spec.template.spec.containers[0].resources.requests.cpu - value: 50m - documentIndex: 1 - - equal: - path: spec.template.spec.containers[0].resources.requests.memory - value: 128Mi - documentIndex: 1 - - equal: - path: spec.template.spec.containers[0].ports[0].containerPort - value: 4002 - documentIndex: 1 - - equal: - path: spec.template.spec.containers[0].ports[0].name - value: user-port - documentIndex: 1 - - equal: - path: spec.template.spec.containers[0].ports[0].protocol - value: TCP - documentIndex: 1 - - equal: - path: spec.template.spec.containers[0].env[0].name - value: PATH_PREFIX - documentIndex: 1 - - equal: - path: spec.template.spec.containers[0].env[0].value - value: /audit - documentIndex: 1 - - equal: - path: spec.template.spec.containers[0].env[1].name - value: TIMEZONE - documentIndex: 1 - - equal: - path: spec.template.spec.containers[0].env[1].value - value: America/Los_Angeles - documentIndex: 1 - - equal: - path: spec.template.spec.containers[0].env[2].name - value: PG_USER - documentIndex: 1 - - equal: - path: spec.template.spec.containers[0].env[2].value - value: mdsadmin - documentIndex: 1 - - equal: - path: spec.template.spec.containers[0].env[3].name - value: PG_NAME - documentIndex: 1 - - equal: - path: spec.template.spec.containers[0].env[3].value - value: mds - documentIndex: 1 - - equal: - path: spec.template.spec.containers[0].env[4].name - value: PG_PASS - documentIndex: 1 - - equal: - path: spec.template.spec.containers[0].env[4].valueFrom.secretKeyRef.name - value: pg-pass - documentIndex: 1 - - equal: - path: spec.template.spec.containers[0].env[4].valueFrom.secretKeyRef.key - value: postgresql-password - documentIndex: 1 - - equal: - path: spec.template.spec.containers[0].env[5].name - value: PG_PORT - documentIndex: 1 - - equal: - path: spec.template.spec.containers[0].env[5].value - value: '5432' - documentIndex: 1 - - equal: - path: spec.template.spec.containers[0].env[6].name - value: PG_HOST - documentIndex: 1 - - equal: - path: spec.template.spec.containers[0].env[6].value - value: mds-postgresql.mds.svc.cluster.local - documentIndex: 1 - - equal: - path: spec.template.spec.containers[0].env[7].name - value: PG_HOST_READER - documentIndex: 1 - - equal: - path: spec.template.spec.containers[0].env[7].value - value: mds-postgresql.mds.svc.cluster.local - documentIndex: 1 - - equal: - path: spec.template.spec.containers[0].env[8].name - value: REDIS_HOST - documentIndex: 1 - - equal: - path: spec.template.spec.containers[0].env[8].value - value: mds-redis-master.mds.svc.cluster.local - documentIndex: 1 - - equal: - path: spec.template.spec.containers[0].env[9].name - value: REDIS_PORT - documentIndex: 1 - - equal: - path: spec.template.spec.containers[0].env[9].value - value: '6379' - documentIndex: 1 - - equal: - path: spec.template.spec.containers[0].env[10].name - value: CE_NAME - documentIndex: 1 - - equal: - path: spec.template.spec.containers[0].env[10].value - value: mds-audit - documentIndex: 1 - - equal: - path: spec.template.spec.containers[0].env[11].name - value: SINK - documentIndex: 1 - - equal: - path: spec.template.spec.containers[0].env[11].value - value: http://default-broker.mds.svc.cluster.local - documentIndex: 1 diff --git a/helm/mds/tests/deployment-audit-resource-all_test.yaml b/helm/mds/tests/deployment-audit-resource-all_test.yaml deleted file mode 100644 index 598dc4493..000000000 --- a/helm/mds/tests/deployment-audit-resource-all_test.yaml +++ /dev/null @@ -1,183 +0,0 @@ -suite: test deployment audit default -templates: - - deployment.yaml -tests: - - it: doc 1 - set: - resourcesLimitsCpu: 250m - resourcesLimitsMemory: 256Mi - resourcesRequestsCpu: 5m - resourcesRequestsMemory: 13Mi - release: - namespace: mds - asserts: - - isAPIVersion: - of: apps/v1 - documentIndex: 1 - - isKind: - of: Deployment - documentIndex: 1 - - equal: - path: metadata.name - value: mds-audit - documentIndex: 1 - - equal: - path: metadata.labels.app - value: mds-audit - documentIndex: 1 - - equal: - path: metadata.namespace - value: mds - documentIndex: 1 - - equal: - path: spec.replicas - value: 1 - documentIndex: 1 - - equal: - path: spec.selector.matchLabels.app - value: mds-audit - documentIndex: 1 - - equal: - path: spec.template.metadata.labels.app - value: mds-audit - documentIndex: 1 - - equal: - path: spec.template.spec.containers[0].name - value: mds-audit - documentIndex: 1 - - equal: - path: spec.template.spec.containers[0].image - value: mds-audit:latest - documentIndex: 1 - - equal: - path: spec.template.spec.containers[0].imagePullPolicy - value: IfNotPresent - documentIndex: 1 - - equal: - path: spec.template.spec.containers[0].resources.limits.cpu - value: 250m - documentIndex: 1 - - equal: - path: spec.template.spec.containers[0].resources.limits.memory - value: 256Mi - documentIndex: 1 - - equal: - path: spec.template.spec.containers[0].resources.requests.cpu - value: 5m - documentIndex: 1 - - equal: - path: spec.template.spec.containers[0].resources.requests.memory - value: 13Mi - documentIndex: 1 - - equal: - path: spec.template.spec.containers[0].ports[0].containerPort - value: 4002 - documentIndex: 1 - - equal: - path: spec.template.spec.containers[0].ports[0].name - value: user-port - documentIndex: 1 - - equal: - path: spec.template.spec.containers[0].ports[0].protocol - value: TCP - documentIndex: 1 - - equal: - path: spec.template.spec.containers[0].env[0].name - value: PATH_PREFIX - documentIndex: 1 - - equal: - path: spec.template.spec.containers[0].env[0].value - value: /audit - documentIndex: 1 - - equal: - path: spec.template.spec.containers[0].env[1].name - value: TIMEZONE - documentIndex: 1 - - equal: - path: spec.template.spec.containers[0].env[1].value - value: America/Los_Angeles - documentIndex: 1 - - equal: - path: spec.template.spec.containers[0].env[2].name - value: PG_USER - documentIndex: 1 - - equal: - path: spec.template.spec.containers[0].env[2].value - value: mdsadmin - documentIndex: 1 - - equal: - path: spec.template.spec.containers[0].env[3].name - value: PG_NAME - documentIndex: 1 - - equal: - path: spec.template.spec.containers[0].env[3].value - value: mds - documentIndex: 1 - - equal: - path: spec.template.spec.containers[0].env[4].name - value: PG_PASS - documentIndex: 1 - - equal: - path: spec.template.spec.containers[0].env[4].valueFrom.secretKeyRef.name - value: pg-pass - documentIndex: 1 - - equal: - path: spec.template.spec.containers[0].env[4].valueFrom.secretKeyRef.key - value: postgresql-password - documentIndex: 1 - - equal: - path: spec.template.spec.containers[0].env[5].name - value: PG_PORT - documentIndex: 1 - - equal: - path: spec.template.spec.containers[0].env[5].value - value: "5432" - documentIndex: 1 - - equal: - path: spec.template.spec.containers[0].env[6].name - value: PG_HOST - documentIndex: 1 - - equal: - path: spec.template.spec.containers[0].env[6].value - value: mds-postgresql.mds.svc.cluster.local - documentIndex: 1 - - equal: - path: spec.template.spec.containers[0].env[7].name - value: PG_HOST_READER - documentIndex: 1 - - equal: - path: spec.template.spec.containers[0].env[7].value - value: mds-postgresql.mds.svc.cluster.local - documentIndex: 1 - - equal: - path: spec.template.spec.containers[0].env[8].name - value: REDIS_HOST - documentIndex: 1 - - equal: - path: spec.template.spec.containers[0].env[8].value - value: mds-redis-master.mds.svc.cluster.local - documentIndex: 1 - - equal: - path: spec.template.spec.containers[0].env[9].name - value: REDIS_PORT - documentIndex: 1 - - equal: - path: spec.template.spec.containers[0].env[9].value - value: "6379" - documentIndex: 1 - - equal: - path: spec.template.spec.containers[0].env[10].name - value: CE_NAME - documentIndex: 1 - - equal: - path: spec.template.spec.containers[0].env[10].value - value: mds-audit - documentIndex: 1 - - equal: - path: spec.template.spec.containers[0].env[11].name - value: SINK - documentIndex: 1 - - equal: - path: spec.template.spec.containers[0].env[11].value - value: http://default-broker.mds.svc.cluster.local - documentIndex: 1 diff --git a/helm/mds/tests/deployment-audit-default_test.yaml b/helm/mds/tests/deployment-audit_test.yaml similarity index 88% rename from helm/mds/tests/deployment-audit-default_test.yaml rename to helm/mds/tests/deployment-audit_test.yaml index b9e3aa6eb..ee2cec34b 100644 --- a/helm/mds/tests/deployment-audit-default_test.yaml +++ b/helm/mds/tests/deployment-audit_test.yaml @@ -1,8 +1,8 @@ -suite: test deployment audit default +suite: test deployment audit templates: - deployment.yaml tests: - - it: doc 1 + - it: doc 0 release: namespace: mds asserts: @@ -44,10 +44,6 @@ tests: path: spec.template.spec.containers[0].image value: mds-audit:latest documentIndex: 1 - - equal: - path: spec.template.spec.containers[0].imagePullPolicy - value: IfNotPresent - documentIndex: 1 - equal: path: spec.template.spec.containers[0].resources.limits.cpu value: 500m @@ -66,7 +62,7 @@ tests: documentIndex: 1 - equal: path: spec.template.spec.containers[0].ports[0].containerPort - value: 4002 + value: 4000 documentIndex: 1 - equal: path: spec.template.spec.containers[0].ports[0].name @@ -78,55 +74,55 @@ tests: documentIndex: 1 - equal: path: spec.template.spec.containers[0].env[0].name - value: PATH_PREFIX + value: PORT documentIndex: 1 - equal: path: spec.template.spec.containers[0].env[0].value - value: /audit + value: "4000" documentIndex: 1 - equal: path: spec.template.spec.containers[0].env[1].name - value: TIMEZONE + value: PATH_PREFIX documentIndex: 1 - equal: path: spec.template.spec.containers[0].env[1].value - value: America/Los_Angeles + value: /audit documentIndex: 1 - equal: path: spec.template.spec.containers[0].env[2].name - value: PG_USER + value: TIMEZONE documentIndex: 1 - equal: path: spec.template.spec.containers[0].env[2].value - value: mdsadmin + value: America/Los_Angeles documentIndex: 1 - equal: path: spec.template.spec.containers[0].env[3].name - value: PG_NAME + value: PG_USER documentIndex: 1 - equal: path: spec.template.spec.containers[0].env[3].value - value: mds + value: mdsadmin documentIndex: 1 - equal: path: spec.template.spec.containers[0].env[4].name - value: PG_PASS + value: PG_NAME documentIndex: 1 - equal: - path: spec.template.spec.containers[0].env[4].valueFrom.secretKeyRef.name - value: pg-pass + path: spec.template.spec.containers[0].env[4].value + value: mds documentIndex: 1 - equal: - path: spec.template.spec.containers[0].env[4].valueFrom.secretKeyRef.key - value: postgresql-password + path: spec.template.spec.containers[0].env[5].name + value: PG_PASS documentIndex: 1 - equal: - path: spec.template.spec.containers[0].env[5].name - value: PG_PORT + path: spec.template.spec.containers[0].env[5].valueFrom.secretKeyRef.name + value: mds-secrets documentIndex: 1 - equal: - path: spec.template.spec.containers[0].env[5].value - value: "5432" + path: spec.template.spec.containers[0].env[5].valueFrom.secretKeyRef.key + value: postgresql-password documentIndex: 1 - equal: path: spec.template.spec.containers[0].env[6].name @@ -138,11 +134,11 @@ tests: documentIndex: 1 - equal: path: spec.template.spec.containers[0].env[7].name - value: PG_HOST_READER + value: PG_PORT documentIndex: 1 - equal: path: spec.template.spec.containers[0].env[7].value - value: mds-postgresql.mds.svc.cluster.local + value: "5432" documentIndex: 1 - equal: path: spec.template.spec.containers[0].env[8].name @@ -162,17 +158,24 @@ tests: documentIndex: 1 - equal: path: spec.template.spec.containers[0].env[10].name - value: CE_NAME + value: NATS documentIndex: 1 - equal: path: spec.template.spec.containers[0].env[10].value - value: mds-audit + value: nats.nats.svc.cluster.local documentIndex: 1 - equal: path: spec.template.spec.containers[0].env[11].name - value: SINK + value: STAN_CLUSTER documentIndex: 1 - equal: path: spec.template.spec.containers[0].env[11].value - value: http://default-broker.mds.svc.cluster.local + value: nats-streaming + documentIndex: 1 + - equal: + path: spec.template.spec.containers[0].env[12].name + value: TENANT_ID + documentIndex: 1 + - isEmpty: + path: spec.template.spec.containers[0].env[12].value documentIndex: 1 diff --git a/helm/mds/tests/deployment-compliance-env_test.yaml b/helm/mds/tests/deployment-compliance-env_test.yaml deleted file mode 100644 index ef2a8b93e..000000000 --- a/helm/mds/tests/deployment-compliance-env_test.yaml +++ /dev/null @@ -1,202 +0,0 @@ -suite: test deployment compliance default -templates: - - deployment.yaml -tests: - - it: doc 2 - release: - namespace: mds - set: - apis: - mds-compliance: - env: - - name: foo - value: bar - - name: bop - value: bing - asserts: - - isAPIVersion: - of: apps/v1 - documentIndex: 2 - - isKind: - of: Deployment - documentIndex: 2 - - equal: - path: metadata.name - value: mds-compliance - documentIndex: 2 - - equal: - path: metadata.labels.app - value: mds-compliance - documentIndex: 2 - - equal: - path: metadata.namespace - value: mds - documentIndex: 2 - - equal: - path: spec.replicas - value: 1 - documentIndex: 2 - - equal: - path: spec.selector.matchLabels.app - value: mds-compliance - documentIndex: 2 - - equal: - path: spec.template.metadata.labels.app - value: mds-compliance - documentIndex: 2 - - equal: - path: spec.template.spec.containers[0].name - value: mds-compliance - documentIndex: 2 - - equal: - path: spec.template.spec.containers[0].image - value: mds-compliance:latest - documentIndex: 2 - - equal: - path: spec.template.spec.containers[0].imagePullPolicy - value: IfNotPresent - documentIndex: 2 - - equal: - path: spec.template.spec.containers[0].resources.limits.cpu - value: 500m - documentIndex: 2 - - equal: - path: spec.template.spec.containers[0].resources.limits.memory - value: 512Mi - documentIndex: 2 - - equal: - path: spec.template.spec.containers[0].resources.requests.cpu - value: 50m - documentIndex: 2 - - equal: - path: spec.template.spec.containers[0].resources.requests.memory - value: 128Mi - documentIndex: 2 - - equal: - path: spec.template.spec.containers[0].ports[0].containerPort - value: 4004 - documentIndex: 2 - - equal: - path: spec.template.spec.containers[0].ports[0].name - value: user-port - documentIndex: 2 - - equal: - path: spec.template.spec.containers[0].ports[0].protocol - value: TCP - documentIndex: 2 - - equal: - path: spec.template.spec.containers[0].env[0].name - value: PATH_PREFIX - documentIndex: 2 - - equal: - path: spec.template.spec.containers[0].env[0].value - value: /compliance - documentIndex: 2 - - equal: - path: spec.template.spec.containers[0].env[1].name - value: TIMEZONE - documentIndex: 2 - - equal: - path: spec.template.spec.containers[0].env[1].value - value: America/Los_Angeles - documentIndex: 2 - - equal: - path: spec.template.spec.containers[0].env[2].name - value: PG_USER - documentIndex: 2 - - equal: - path: spec.template.spec.containers[0].env[2].value - value: mdsadmin - documentIndex: 2 - - equal: - path: spec.template.spec.containers[0].env[3].name - value: PG_NAME - documentIndex: 2 - - equal: - path: spec.template.spec.containers[0].env[3].value - value: mds - documentIndex: 2 - - equal: - path: spec.template.spec.containers[0].env[4].name - value: PG_PASS - documentIndex: 2 - - equal: - path: spec.template.spec.containers[0].env[4].valueFrom.secretKeyRef.name - value: pg-pass - documentIndex: 2 - - equal: - path: spec.template.spec.containers[0].env[4].valueFrom.secretKeyRef.key - value: postgresql-password - documentIndex: 2 - - equal: - path: spec.template.spec.containers[0].env[5].name - value: PG_PORT - documentIndex: 2 - - equal: - path: spec.template.spec.containers[0].env[5].value - value: "5432" - documentIndex: 2 - - equal: - path: spec.template.spec.containers[0].env[6].name - value: PG_HOST - documentIndex: 2 - - equal: - path: spec.template.spec.containers[0].env[6].value - value: mds-postgresql.mds.svc.cluster.local - documentIndex: 2 - - equal: - path: spec.template.spec.containers[0].env[7].name - value: PG_HOST_READER - documentIndex: 2 - - equal: - path: spec.template.spec.containers[0].env[7].value - value: mds-postgresql.mds.svc.cluster.local - documentIndex: 2 - - equal: - path: spec.template.spec.containers[0].env[8].name - value: REDIS_HOST - documentIndex: 2 - - equal: - path: spec.template.spec.containers[0].env[8].value - value: mds-redis-master.mds.svc.cluster.local - documentIndex: 2 - - equal: - path: spec.template.spec.containers[0].env[9].name - value: REDIS_PORT - documentIndex: 2 - - equal: - path: spec.template.spec.containers[0].env[9].value - value: "6379" - documentIndex: 2 - - equal: - path: spec.template.spec.containers[0].env[10].name - value: CE_NAME - documentIndex: 2 - - equal: - path: spec.template.spec.containers[0].env[10].value - value: mds-compliance - documentIndex: 2 - - equal: - path: spec.template.spec.containers[0].env[11].name - value: SINK - documentIndex: 2 - - equal: - path: spec.template.spec.containers[0].env[11].value - value: http://default-broker.mds.svc.cluster.local - documentIndex: 2 - - equal: - path: spec.template.spec.containers[0].env[12].name - value: foo - documentIndex: 2 - - equal: - path: spec.template.spec.containers[0].env[12].value - value: bar - documentIndex: 2 - - equal: - path: spec.template.spec.containers[0].env[13].name - value: bop - documentIndex: 2 - - equal: - path: spec.template.spec.containers[0].env[13].value - value: bing - documentIndex: 2 \ No newline at end of file diff --git a/helm/mds/tests/deployment-compliance-no-cache_test.yaml b/helm/mds/tests/deployment-compliance-no-cache_test.yaml deleted file mode 100644 index 4ca060066..000000000 --- a/helm/mds/tests/deployment-compliance-no-cache_test.yaml +++ /dev/null @@ -1,164 +0,0 @@ -suite: test deployment compliance no-cache -templates: - - deployment.yaml -tests: - - it: doc 2 - release: - namespace: mds - set: - apis.mds-compliance.useCache: false - asserts: - - isAPIVersion: - of: apps/v1 - documentIndex: 2 - - isKind: - of: Deployment - documentIndex: 2 - - equal: - path: metadata.name - value: mds-compliance - documentIndex: 2 - - equal: - path: metadata.labels.app - value: mds-compliance - documentIndex: 2 - - equal: - path: metadata.namespace - value: mds - documentIndex: 2 - - equal: - path: spec.replicas - value: 1 - documentIndex: 2 - - equal: - path: spec.selector.matchLabels.app - value: mds-compliance - documentIndex: 2 - - equal: - path: spec.template.metadata.labels.app - value: mds-compliance - documentIndex: 2 - - equal: - path: spec.template.spec.containers[0].name - value: mds-compliance - documentIndex: 2 - - equal: - path: spec.template.spec.containers[0].image - value: mds-compliance:latest - documentIndex: 2 - - equal: - path: spec.template.spec.containers[0].imagePullPolicy - value: IfNotPresent - documentIndex: 2 - - equal: - path: spec.template.spec.containers[0].resources.limits.cpu - value: 500m - documentIndex: 2 - - equal: - path: spec.template.spec.containers[0].resources.limits.memory - value: 512Mi - documentIndex: 2 - - equal: - path: spec.template.spec.containers[0].resources.requests.cpu - value: 50m - documentIndex: 2 - - equal: - path: spec.template.spec.containers[0].resources.requests.memory - value: 128Mi - documentIndex: 2 - - equal: - path: spec.template.spec.containers[0].ports[0].containerPort - value: 4004 - documentIndex: 2 - - equal: - path: spec.template.spec.containers[0].ports[0].name - value: user-port - documentIndex: 2 - - equal: - path: spec.template.spec.containers[0].ports[0].protocol - value: TCP - documentIndex: 2 - - equal: - path: spec.template.spec.containers[0].env[0].name - value: PATH_PREFIX - documentIndex: 2 - - equal: - path: spec.template.spec.containers[0].env[0].value - value: /compliance - documentIndex: 2 - - equal: - path: spec.template.spec.containers[0].env[1].name - value: TIMEZONE - documentIndex: 2 - - equal: - path: spec.template.spec.containers[0].env[1].value - value: America/Los_Angeles - documentIndex: 2 - - equal: - path: spec.template.spec.containers[0].env[2].name - value: PG_USER - documentIndex: 2 - - equal: - path: spec.template.spec.containers[0].env[2].value - value: mdsadmin - documentIndex: 2 - - equal: - path: spec.template.spec.containers[0].env[3].name - value: PG_NAME - documentIndex: 2 - - equal: - path: spec.template.spec.containers[0].env[3].value - value: mds - documentIndex: 2 - - equal: - path: spec.template.spec.containers[0].env[4].name - value: PG_PASS - documentIndex: 2 - - equal: - path: spec.template.spec.containers[0].env[4].valueFrom.secretKeyRef.name - value: pg-pass - documentIndex: 2 - - equal: - path: spec.template.spec.containers[0].env[4].valueFrom.secretKeyRef.key - value: postgresql-password - documentIndex: 2 - - equal: - path: spec.template.spec.containers[0].env[5].name - value: PG_PORT - documentIndex: 2 - - equal: - path: spec.template.spec.containers[0].env[5].value - value: "5432" - documentIndex: 2 - - equal: - path: spec.template.spec.containers[0].env[6].name - value: PG_HOST - documentIndex: 2 - - equal: - path: spec.template.spec.containers[0].env[6].value - value: mds-postgresql.mds.svc.cluster.local - documentIndex: 2 - - equal: - path: spec.template.spec.containers[0].env[7].name - value: PG_HOST_READER - documentIndex: 2 - - equal: - path: spec.template.spec.containers[0].env[7].value - value: mds-postgresql.mds.svc.cluster.local - documentIndex: 2 - - equal: - path: spec.template.spec.containers[0].env[8].name - value: CE_NAME - documentIndex: 2 - - equal: - path: spec.template.spec.containers[0].env[8].value - value: mds-compliance - documentIndex: 2 - - equal: - path: spec.template.spec.containers[0].env[9].name - value: SINK - documentIndex: 2 - - equal: - path: spec.template.spec.containers[0].env[9].value - value: http://default-broker.mds.svc.cluster.local - documentIndex: 2 diff --git a/helm/mds/tests/deployment-compliance-no-db_test.yaml b/helm/mds/tests/deployment-compliance-no-db_test.yaml deleted file mode 100644 index 7b4c6cd51..000000000 --- a/helm/mds/tests/deployment-compliance-no-db_test.yaml +++ /dev/null @@ -1,128 +0,0 @@ -suite: test deployment compliance no-db -templates: - - deployment.yaml -tests: - - it: doc 2 - release: - namespace: mds - set: - apis.mds-compliance.useDB: false - asserts: - - isAPIVersion: - of: apps/v1 - documentIndex: 2 - - isKind: - of: Deployment - documentIndex: 2 - - equal: - path: metadata.name - value: mds-compliance - documentIndex: 2 - - equal: - path: metadata.labels.app - value: mds-compliance - documentIndex: 2 - - equal: - path: metadata.namespace - value: mds - documentIndex: 2 - - equal: - path: spec.replicas - value: 1 - documentIndex: 2 - - equal: - path: spec.selector.matchLabels.app - value: mds-compliance - documentIndex: 2 - - equal: - path: spec.template.metadata.labels.app - value: mds-compliance - documentIndex: 2 - - equal: - path: spec.template.spec.containers[0].name - value: mds-compliance - documentIndex: 2 - - equal: - path: spec.template.spec.containers[0].image - value: mds-compliance:latest - documentIndex: 2 - - equal: - path: spec.template.spec.containers[0].imagePullPolicy - value: IfNotPresent - documentIndex: 2 - - equal: - path: spec.template.spec.containers[0].resources.limits.cpu - value: 500m - documentIndex: 2 - - equal: - path: spec.template.spec.containers[0].resources.limits.memory - value: 512Mi - documentIndex: 2 - - equal: - path: spec.template.spec.containers[0].resources.requests.cpu - value: 50m - documentIndex: 2 - - equal: - path: spec.template.spec.containers[0].resources.requests.memory - value: 128Mi - documentIndex: 2 - - equal: - path: spec.template.spec.containers[0].ports[0].containerPort - value: 4004 - documentIndex: 2 - - equal: - path: spec.template.spec.containers[0].ports[0].name - value: user-port - documentIndex: 2 - - equal: - path: spec.template.spec.containers[0].ports[0].protocol - value: TCP - documentIndex: 2 - - equal: - path: spec.template.spec.containers[0].env[0].name - value: PATH_PREFIX - documentIndex: 2 - - equal: - path: spec.template.spec.containers[0].env[0].value - value: /compliance - documentIndex: 2 - - equal: - path: spec.template.spec.containers[0].env[1].name - value: TIMEZONE - documentIndex: 2 - - equal: - path: spec.template.spec.containers[0].env[1].value - value: America/Los_Angeles - documentIndex: 2 - - equal: - path: spec.template.spec.containers[0].env[2].name - value: REDIS_HOST - documentIndex: 2 - - equal: - path: spec.template.spec.containers[0].env[2].value - value: mds-redis-master.mds.svc.cluster.local - documentIndex: 2 - - equal: - path: spec.template.spec.containers[0].env[3].name - value: REDIS_PORT - documentIndex: 2 - - equal: - path: spec.template.spec.containers[0].env[3].value - value: "6379" - documentIndex: 2 - - equal: - path: spec.template.spec.containers[0].env[4].name - value: CE_NAME - documentIndex: 2 - - equal: - path: spec.template.spec.containers[0].env[4].value - value: mds-compliance - documentIndex: 2 - - equal: - path: spec.template.spec.containers[0].env[5].name - value: SINK - documentIndex: 2 - - equal: - path: spec.template.spec.containers[0].env[5].value - value: http://default-broker.mds.svc.cluster.local - documentIndex: 2 diff --git a/helm/mds/tests/deployment-compliance-no-events_test.yaml b/helm/mds/tests/deployment-compliance-no-events_test.yaml deleted file mode 100644 index 5977db1da..000000000 --- a/helm/mds/tests/deployment-compliance-no-events_test.yaml +++ /dev/null @@ -1,168 +0,0 @@ -suite: test deployment compliance no-events -templates: - - deployment.yaml -tests: - - it: doc 2 - release: - namespace: mds - set: - apis.mds-compliance.useEvents: false - asserts: - - isAPIVersion: - of: apps/v1 - documentIndex: 2 - - isKind: - of: Deployment - documentIndex: 2 - - equal: - path: metadata.name - value: mds-compliance - documentIndex: 2 - - equal: - path: metadata.labels.app - value: mds-compliance - documentIndex: 2 - - equal: - path: metadata.namespace - value: mds - documentIndex: 2 - - equal: - path: spec.replicas - value: 1 - documentIndex: 2 - - equal: - path: spec.selector.matchLabels.app - value: mds-compliance - documentIndex: 2 - - equal: - path: spec.template.metadata.labels.app - value: mds-compliance - documentIndex: 2 - - equal: - path: spec.template.spec.containers[0].name - value: mds-compliance - documentIndex: 2 - - equal: - path: spec.template.spec.containers[0].image - value: mds-compliance:latest - documentIndex: 2 - - equal: - path: spec.template.spec.containers[0].imagePullPolicy - value: IfNotPresent - documentIndex: 2 - - equal: - path: spec.template.spec.containers[0].resources.limits.cpu - value: 500m - documentIndex: 2 - - equal: - path: spec.template.spec.containers[0].resources.limits.memory - value: 512Mi - documentIndex: 2 - - equal: - path: spec.template.spec.containers[0].resources.requests.cpu - value: 50m - documentIndex: 2 - - equal: - path: spec.template.spec.containers[0].resources.requests.memory - value: 128Mi - documentIndex: 2 - - equal: - path: spec.template.spec.containers[0].ports[0].containerPort - value: 4004 - documentIndex: 2 - - equal: - path: spec.template.spec.containers[0].ports[0].name - value: user-port - documentIndex: 2 - - equal: - path: spec.template.spec.containers[0].ports[0].protocol - value: TCP - documentIndex: 2 - - equal: - path: spec.template.spec.containers[0].env[0].name - value: PATH_PREFIX - documentIndex: 2 - - equal: - path: spec.template.spec.containers[0].env[0].value - value: /compliance - documentIndex: 2 - - equal: - path: spec.template.spec.containers[0].env[1].name - value: TIMEZONE - documentIndex: 2 - - equal: - path: spec.template.spec.containers[0].env[1].value - value: America/Los_Angeles - documentIndex: 2 - - equal: - path: spec.template.spec.containers[0].env[2].name - value: PG_USER - documentIndex: 2 - - equal: - path: spec.template.spec.containers[0].env[2].value - value: mdsadmin - documentIndex: 2 - - equal: - path: spec.template.spec.containers[0].env[3].name - value: PG_NAME - documentIndex: 2 - - equal: - path: spec.template.spec.containers[0].env[3].value - value: mds - documentIndex: 2 - - equal: - path: spec.template.spec.containers[0].env[4].name - value: PG_PASS - documentIndex: 2 - - equal: - path: spec.template.spec.containers[0].env[4].valueFrom.secretKeyRef.name - value: pg-pass - documentIndex: 2 - - equal: - path: spec.template.spec.containers[0].env[4].valueFrom.secretKeyRef.key - value: postgresql-password - documentIndex: 2 - - equal: - path: spec.template.spec.containers[0].env[5].name - value: PG_PORT - documentIndex: 2 - - equal: - path: spec.template.spec.containers[0].env[5].value - value: "5432" - documentIndex: 2 - - equal: - path: spec.template.spec.containers[0].env[6].name - value: PG_HOST - documentIndex: 2 - - equal: - path: spec.template.spec.containers[0].env[6].value - value: mds-postgresql.mds.svc.cluster.local - documentIndex: 2 - - equal: - path: spec.template.spec.containers[0].env[7].name - value: PG_HOST_READER - documentIndex: 2 - - equal: - path: spec.template.spec.containers[0].env[7].value - value: mds-postgresql.mds.svc.cluster.local - documentIndex: 2 - - equal: - path: spec.template.spec.containers[0].env[8].name - value: REDIS_HOST - documentIndex: 2 - - equal: - path: spec.template.spec.containers[0].env[8].value - value: mds-redis-master.mds.svc.cluster.local - documentIndex: 2 - - equal: - path: spec.template.spec.containers[0].env[9].name - value: REDIS_PORT - documentIndex: 2 - - equal: - path: spec.template.spec.containers[0].env[9].value - value: "6379" - documentIndex: 2 - # fixme: test non-existence - # - isNull: - # path: spec.template.spec.containers[0].env[10] - # documentIndex: 1 diff --git a/helm/mds/tests/deployment-compliance-registry_test.yaml b/helm/mds/tests/deployment-compliance-registry_test.yaml deleted file mode 100644 index 3b303dccd..000000000 --- a/helm/mds/tests/deployment-compliance-registry_test.yaml +++ /dev/null @@ -1,179 +0,0 @@ -suite: test deployment compliance registry -templates: - - deployment.yaml -tests: - - it: doc 2 - set: - registry: foo-registry - release: - namespace: mds - asserts: - - isAPIVersion: - of: apps/v1 - documentIndex: 2 - - isKind: - of: Deployment - documentIndex: 2 - - equal: - path: metadata.name - value: mds-compliance - documentIndex: 2 - - equal: - path: metadata.labels.app - value: mds-compliance - documentIndex: 2 - - equal: - path: metadata.namespace - value: mds - documentIndex: 2 - - equal: - path: spec.replicas - value: 1 - documentIndex: 2 - - equal: - path: spec.selector.matchLabels.app - value: mds-compliance - documentIndex: 2 - - equal: - path: spec.template.metadata.labels.app - value: mds-compliance - documentIndex: 2 - - equal: - path: spec.template.spec.containers[0].name - value: mds-compliance - documentIndex: 2 - - equal: - path: spec.template.spec.containers[0].image - value: foo-registry/mds-compliance:latest - documentIndex: 2 - - isNull: - path: spec.template.spec.containers[0].imagePullPolicy - documentIndex: 2 - - equal: - path: spec.template.spec.containers[0].resources.limits.cpu - value: 500m - documentIndex: 2 - - equal: - path: spec.template.spec.containers[0].resources.limits.memory - value: 512Mi - documentIndex: 2 - - equal: - path: spec.template.spec.containers[0].resources.requests.cpu - value: 50m - documentIndex: 2 - - equal: - path: spec.template.spec.containers[0].resources.requests.memory - value: 128Mi - documentIndex: 2 - - equal: - path: spec.template.spec.containers[0].ports[0].containerPort - value: 4004 - documentIndex: 2 - - equal: - path: spec.template.spec.containers[0].ports[0].name - value: user-port - documentIndex: 2 - - equal: - path: spec.template.spec.containers[0].ports[0].protocol - value: TCP - documentIndex: 2 - - equal: - path: spec.template.spec.containers[0].env[0].name - value: PATH_PREFIX - documentIndex: 2 - - equal: - path: spec.template.spec.containers[0].env[0].value - value: /compliance - documentIndex: 2 - - equal: - path: spec.template.spec.containers[0].env[1].name - value: TIMEZONE - documentIndex: 2 - - equal: - path: spec.template.spec.containers[0].env[1].value - value: America/Los_Angeles - documentIndex: 2 - - equal: - path: spec.template.spec.containers[0].env[2].name - value: PG_USER - documentIndex: 2 - - equal: - path: spec.template.spec.containers[0].env[2].value - value: mdsadmin - documentIndex: 2 - - equal: - path: spec.template.spec.containers[0].env[3].name - value: PG_NAME - documentIndex: 2 - - equal: - path: spec.template.spec.containers[0].env[3].value - value: mds - documentIndex: 2 - - equal: - path: spec.template.spec.containers[0].env[4].name - value: PG_PASS - documentIndex: 2 - - equal: - path: spec.template.spec.containers[0].env[4].valueFrom.secretKeyRef.name - value: pg-pass - documentIndex: 2 - - equal: - path: spec.template.spec.containers[0].env[4].valueFrom.secretKeyRef.key - value: postgresql-password - documentIndex: 2 - - equal: - path: spec.template.spec.containers[0].env[5].name - value: PG_PORT - documentIndex: 2 - - equal: - path: spec.template.spec.containers[0].env[5].value - value: "5432" - documentIndex: 2 - - equal: - path: spec.template.spec.containers[0].env[6].name - value: PG_HOST - documentIndex: 2 - - equal: - path: spec.template.spec.containers[0].env[6].value - value: mds-postgresql.mds.svc.cluster.local - documentIndex: 2 - - equal: - path: spec.template.spec.containers[0].env[7].name - value: PG_HOST_READER - documentIndex: 2 - - equal: - path: spec.template.spec.containers[0].env[7].value - value: mds-postgresql.mds.svc.cluster.local - documentIndex: 2 - - equal: - path: spec.template.spec.containers[0].env[8].name - value: REDIS_HOST - documentIndex: 2 - - equal: - path: spec.template.spec.containers[0].env[8].value - value: mds-redis-master.mds.svc.cluster.local - documentIndex: 2 - - equal: - path: spec.template.spec.containers[0].env[9].name - value: REDIS_PORT - documentIndex: 2 - - equal: - path: spec.template.spec.containers[0].env[9].value - value: "6379" - documentIndex: 2 - - equal: - path: spec.template.spec.containers[0].env[10].name - value: CE_NAME - documentIndex: 2 - - equal: - path: spec.template.spec.containers[0].env[10].value - value: mds-compliance - documentIndex: 2 - - equal: - path: spec.template.spec.containers[0].env[11].name - value: SINK - documentIndex: 2 - - equal: - path: spec.template.spec.containers[0].env[11].value - value: http://default-broker.mds.svc.cluster.local - documentIndex: 2 diff --git a/helm/mds/tests/deployment-compliance-resource-all_test.yaml b/helm/mds/tests/deployment-compliance-resource-all_test.yaml deleted file mode 100644 index 8fa43f359..000000000 --- a/helm/mds/tests/deployment-compliance-resource-all_test.yaml +++ /dev/null @@ -1,183 +0,0 @@ -suite: test deployment compliance default -templates: - - deployment.yaml -tests: - - it: doc 2 - set: - resourcesLimitsCpu: 250m - resourcesLimitsMemory: 256Mi - resourcesRequestsCpu: 5m - resourcesRequestsMemory: 13Mi - release: - namespace: mds - asserts: - - isAPIVersion: - of: apps/v1 - documentIndex: 2 - - isKind: - of: Deployment - documentIndex: 2 - - equal: - path: metadata.name - value: mds-compliance - documentIndex: 2 - - equal: - path: metadata.labels.app - value: mds-compliance - documentIndex: 2 - - equal: - path: metadata.namespace - value: mds - documentIndex: 2 - - equal: - path: spec.replicas - value: 1 - documentIndex: 2 - - equal: - path: spec.selector.matchLabels.app - value: mds-compliance - documentIndex: 2 - - equal: - path: spec.template.metadata.labels.app - value: mds-compliance - documentIndex: 2 - - equal: - path: spec.template.spec.containers[0].name - value: mds-compliance - documentIndex: 2 - - equal: - path: spec.template.spec.containers[0].image - value: mds-compliance:latest - documentIndex: 2 - - equal: - path: spec.template.spec.containers[0].imagePullPolicy - value: IfNotPresent - documentIndex: 2 - - equal: - path: spec.template.spec.containers[0].resources.limits.cpu - value: 250m - documentIndex: 2 - - equal: - path: spec.template.spec.containers[0].resources.limits.memory - value: 256Mi - documentIndex: 2 - - equal: - path: spec.template.spec.containers[0].resources.requests.cpu - value: 5m - documentIndex: 2 - - equal: - path: spec.template.spec.containers[0].resources.requests.memory - value: 13Mi - documentIndex: 2 - - equal: - path: spec.template.spec.containers[0].ports[0].containerPort - value: 4004 - documentIndex: 2 - - equal: - path: spec.template.spec.containers[0].ports[0].name - value: user-port - documentIndex: 2 - - equal: - path: spec.template.spec.containers[0].ports[0].protocol - value: TCP - documentIndex: 2 - - equal: - path: spec.template.spec.containers[0].env[0].name - value: PATH_PREFIX - documentIndex: 2 - - equal: - path: spec.template.spec.containers[0].env[0].value - value: /compliance - documentIndex: 2 - - equal: - path: spec.template.spec.containers[0].env[1].name - value: TIMEZONE - documentIndex: 2 - - equal: - path: spec.template.spec.containers[0].env[1].value - value: America/Los_Angeles - documentIndex: 2 - - equal: - path: spec.template.spec.containers[0].env[2].name - value: PG_USER - documentIndex: 2 - - equal: - path: spec.template.spec.containers[0].env[2].value - value: mdsadmin - documentIndex: 2 - - equal: - path: spec.template.spec.containers[0].env[3].name - value: PG_NAME - documentIndex: 2 - - equal: - path: spec.template.spec.containers[0].env[3].value - value: mds - documentIndex: 2 - - equal: - path: spec.template.spec.containers[0].env[4].name - value: PG_PASS - documentIndex: 2 - - equal: - path: spec.template.spec.containers[0].env[4].valueFrom.secretKeyRef.name - value: pg-pass - documentIndex: 2 - - equal: - path: spec.template.spec.containers[0].env[4].valueFrom.secretKeyRef.key - value: postgresql-password - documentIndex: 2 - - equal: - path: spec.template.spec.containers[0].env[5].name - value: PG_PORT - documentIndex: 2 - - equal: - path: spec.template.spec.containers[0].env[5].value - value: "5432" - documentIndex: 2 - - equal: - path: spec.template.spec.containers[0].env[6].name - value: PG_HOST - documentIndex: 2 - - equal: - path: spec.template.spec.containers[0].env[6].value - value: mds-postgresql.mds.svc.cluster.local - documentIndex: 2 - - equal: - path: spec.template.spec.containers[0].env[7].name - value: PG_HOST_READER - documentIndex: 2 - - equal: - path: spec.template.spec.containers[0].env[7].value - value: mds-postgresql.mds.svc.cluster.local - documentIndex: 2 - - equal: - path: spec.template.spec.containers[0].env[8].name - value: REDIS_HOST - documentIndex: 2 - - equal: - path: spec.template.spec.containers[0].env[8].value - value: mds-redis-master.mds.svc.cluster.local - documentIndex: 2 - - equal: - path: spec.template.spec.containers[0].env[9].name - value: REDIS_PORT - documentIndex: 2 - - equal: - path: spec.template.spec.containers[0].env[9].value - value: "6379" - documentIndex: 2 - - equal: - path: spec.template.spec.containers[0].env[10].name - value: CE_NAME - documentIndex: 2 - - equal: - path: spec.template.spec.containers[0].env[10].value - value: mds-compliance - documentIndex: 2 - - equal: - path: spec.template.spec.containers[0].env[11].name - value: SINK - documentIndex: 2 - - equal: - path: spec.template.spec.containers[0].env[11].value - value: http://default-broker.mds.svc.cluster.local - documentIndex: 2 diff --git a/helm/mds/tests/deployment-compliance-default_test.yaml b/helm/mds/tests/deployment-compliance_test.yaml similarity index 88% rename from helm/mds/tests/deployment-compliance-default_test.yaml rename to helm/mds/tests/deployment-compliance_test.yaml index 583def1dd..b61ec18a9 100644 --- a/helm/mds/tests/deployment-compliance-default_test.yaml +++ b/helm/mds/tests/deployment-compliance_test.yaml @@ -1,8 +1,8 @@ -suite: test deployment compliance default +suite: test deployment compliance templates: - deployment.yaml tests: - - it: doc 2 + - it: doc 0 release: namespace: mds asserts: @@ -44,10 +44,6 @@ tests: path: spec.template.spec.containers[0].image value: mds-compliance:latest documentIndex: 2 - - equal: - path: spec.template.spec.containers[0].imagePullPolicy - value: IfNotPresent - documentIndex: 2 - equal: path: spec.template.spec.containers[0].resources.limits.cpu value: 500m @@ -66,7 +62,7 @@ tests: documentIndex: 2 - equal: path: spec.template.spec.containers[0].ports[0].containerPort - value: 4004 + value: 4000 documentIndex: 2 - equal: path: spec.template.spec.containers[0].ports[0].name @@ -78,55 +74,55 @@ tests: documentIndex: 2 - equal: path: spec.template.spec.containers[0].env[0].name - value: PATH_PREFIX + value: PORT documentIndex: 2 - equal: path: spec.template.spec.containers[0].env[0].value - value: /compliance + value: "4000" documentIndex: 2 - equal: path: spec.template.spec.containers[0].env[1].name - value: TIMEZONE + value: PATH_PREFIX documentIndex: 2 - equal: path: spec.template.spec.containers[0].env[1].value - value: America/Los_Angeles + value: /compliance documentIndex: 2 - equal: path: spec.template.spec.containers[0].env[2].name - value: PG_USER + value: TIMEZONE documentIndex: 2 - equal: path: spec.template.spec.containers[0].env[2].value - value: mdsadmin + value: America/Los_Angeles documentIndex: 2 - equal: path: spec.template.spec.containers[0].env[3].name - value: PG_NAME + value: PG_USER documentIndex: 2 - equal: path: spec.template.spec.containers[0].env[3].value - value: mds + value: mdsadmin documentIndex: 2 - equal: path: spec.template.spec.containers[0].env[4].name - value: PG_PASS + value: PG_NAME documentIndex: 2 - equal: - path: spec.template.spec.containers[0].env[4].valueFrom.secretKeyRef.name - value: pg-pass + path: spec.template.spec.containers[0].env[4].value + value: mds documentIndex: 2 - equal: - path: spec.template.spec.containers[0].env[4].valueFrom.secretKeyRef.key - value: postgresql-password + path: spec.template.spec.containers[0].env[5].name + value: PG_PASS documentIndex: 2 - equal: - path: spec.template.spec.containers[0].env[5].name - value: PG_PORT + path: spec.template.spec.containers[0].env[5].valueFrom.secretKeyRef.name + value: mds-secrets documentIndex: 2 - equal: - path: spec.template.spec.containers[0].env[5].value - value: "5432" + path: spec.template.spec.containers[0].env[5].valueFrom.secretKeyRef.key + value: postgresql-password documentIndex: 2 - equal: path: spec.template.spec.containers[0].env[6].name @@ -138,11 +134,11 @@ tests: documentIndex: 2 - equal: path: spec.template.spec.containers[0].env[7].name - value: PG_HOST_READER + value: PG_PORT documentIndex: 2 - equal: path: spec.template.spec.containers[0].env[7].value - value: mds-postgresql.mds.svc.cluster.local + value: "5432" documentIndex: 2 - equal: path: spec.template.spec.containers[0].env[8].name @@ -162,17 +158,24 @@ tests: documentIndex: 2 - equal: path: spec.template.spec.containers[0].env[10].name - value: CE_NAME + value: NATS documentIndex: 2 - equal: path: spec.template.spec.containers[0].env[10].value - value: mds-compliance + value: nats.nats.svc.cluster.local documentIndex: 2 - equal: path: spec.template.spec.containers[0].env[11].name - value: SINK + value: STAN_CLUSTER documentIndex: 2 - equal: path: spec.template.spec.containers[0].env[11].value - value: http://default-broker.mds.svc.cluster.local + value: nats-streaming + documentIndex: 2 + - equal: + path: spec.template.spec.containers[0].env[12].name + value: TENANT_ID + documentIndex: 2 + - isEmpty: + path: spec.template.spec.containers[0].env[12].value documentIndex: 2 diff --git a/helm/mds/tests/deployment-daily-no-events_test.yaml b/helm/mds/tests/deployment-config_test.yaml similarity index 77% rename from helm/mds/tests/deployment-daily-no-events_test.yaml rename to helm/mds/tests/deployment-config_test.yaml index b50c08c9d..45f076bec 100644 --- a/helm/mds/tests/deployment-daily-no-events_test.yaml +++ b/helm/mds/tests/deployment-config_test.yaml @@ -1,12 +1,10 @@ -suite: test deployment daily no-events +suite: test deployment config templates: - deployment.yaml tests: - - it: doc 3 + - it: doc 0 release: namespace: mds - set: - apis.mds-daily.useEvents: false asserts: - isAPIVersion: of: apps/v1 @@ -14,14 +12,6 @@ tests: - isKind: of: Deployment documentIndex: 3 - - equal: - path: metadata.name - value: mds-daily - documentIndex: 3 - - equal: - path: metadata.labels.app - value: mds-daily - documentIndex: 3 - equal: path: metadata.namespace value: mds @@ -30,26 +20,6 @@ tests: path: spec.replicas value: 1 documentIndex: 3 - - equal: - path: spec.selector.matchLabels.app - value: mds-daily - documentIndex: 3 - - equal: - path: spec.template.metadata.labels.app - value: mds-daily - documentIndex: 3 - - equal: - path: spec.template.spec.containers[0].name - value: mds-daily - documentIndex: 3 - - equal: - path: spec.template.spec.containers[0].image - value: mds-daily:latest - documentIndex: 3 - - equal: - path: spec.template.spec.containers[0].imagePullPolicy - value: IfNotPresent - documentIndex: 3 - equal: path: spec.template.spec.containers[0].resources.limits.cpu value: 500m @@ -68,7 +38,7 @@ tests: documentIndex: 3 - equal: path: spec.template.spec.containers[0].ports[0].containerPort - value: 4005 + value: 4000 documentIndex: 3 - equal: path: spec.template.spec.containers[0].ports[0].name @@ -80,55 +50,55 @@ tests: documentIndex: 3 - equal: path: spec.template.spec.containers[0].env[0].name - value: PATH_PREFIX + value: PORT documentIndex: 3 - equal: path: spec.template.spec.containers[0].env[0].value - value: /daily + value: "4000" documentIndex: 3 - equal: path: spec.template.spec.containers[0].env[1].name - value: TIMEZONE + value: PATH_PREFIX documentIndex: 3 - equal: path: spec.template.spec.containers[0].env[1].value - value: America/Los_Angeles + value: /config documentIndex: 3 - equal: path: spec.template.spec.containers[0].env[2].name - value: PG_USER + value: TIMEZONE documentIndex: 3 - equal: path: spec.template.spec.containers[0].env[2].value - value: mdsadmin + value: America/Los_Angeles documentIndex: 3 - equal: path: spec.template.spec.containers[0].env[3].name - value: PG_NAME + value: PG_USER documentIndex: 3 - equal: path: spec.template.spec.containers[0].env[3].value - value: mds + value: mdsadmin documentIndex: 3 - equal: path: spec.template.spec.containers[0].env[4].name - value: PG_PASS + value: PG_NAME documentIndex: 3 - equal: - path: spec.template.spec.containers[0].env[4].valueFrom.secretKeyRef.name - value: pg-pass + path: spec.template.spec.containers[0].env[4].value + value: mds documentIndex: 3 - equal: - path: spec.template.spec.containers[0].env[4].valueFrom.secretKeyRef.key - value: postgresql-password + path: spec.template.spec.containers[0].env[5].name + value: PG_PASS documentIndex: 3 - equal: - path: spec.template.spec.containers[0].env[5].name - value: PG_PORT + path: spec.template.spec.containers[0].env[5].valueFrom.secretKeyRef.name + value: mds-secrets documentIndex: 3 - equal: - path: spec.template.spec.containers[0].env[5].value - value: "5432" + path: spec.template.spec.containers[0].env[5].valueFrom.secretKeyRef.key + value: postgresql-password documentIndex: 3 - equal: path: spec.template.spec.containers[0].env[6].name @@ -140,11 +110,11 @@ tests: documentIndex: 3 - equal: path: spec.template.spec.containers[0].env[7].name - value: PG_HOST_READER + value: PG_PORT documentIndex: 3 - equal: path: spec.template.spec.containers[0].env[7].value - value: mds-postgresql.mds.svc.cluster.local + value: "5432" documentIndex: 3 - equal: path: spec.template.spec.containers[0].env[8].name @@ -162,7 +132,26 @@ tests: path: spec.template.spec.containers[0].env[9].value value: "6379" documentIndex: 3 - # fixme: test non-existence - # - isNull: - # path: spec.template.spec.containers[0].env[10] - # documentIndex: 1 \ No newline at end of file + - equal: + path: spec.template.spec.containers[0].env[10].name + value: NATS + documentIndex: 3 + - equal: + path: spec.template.spec.containers[0].env[10].value + value: nats.nats.svc.cluster.local + documentIndex: 3 + - equal: + path: spec.template.spec.containers[0].env[11].name + value: STAN_CLUSTER + documentIndex: 3 + - equal: + path: spec.template.spec.containers[0].env[11].value + value: nats-streaming + documentIndex: 3 + - equal: + path: spec.template.spec.containers[0].env[12].name + value: TENANT_ID + documentIndex: 3 + - isEmpty: + path: spec.template.spec.containers[0].env[12].value + documentIndex: 3 diff --git a/helm/mds/tests/deployment-daily-env_test.yaml b/helm/mds/tests/deployment-daily-env_test.yaml deleted file mode 100644 index 6a22a5cd3..000000000 --- a/helm/mds/tests/deployment-daily-env_test.yaml +++ /dev/null @@ -1,202 +0,0 @@ -suite: test deployment daily default -templates: - - deployment.yaml -tests: - - it: doc 3 - release: - namespace: mds - set: - apis: - mds-daily: - env: - - name: foo - value: bar - - name: bop - value: bing - asserts: - - isAPIVersion: - of: apps/v1 - documentIndex: 3 - - isKind: - of: Deployment - documentIndex: 3 - - equal: - path: metadata.name - value: mds-daily - documentIndex: 3 - - equal: - path: metadata.labels.app - value: mds-daily - documentIndex: 3 - - equal: - path: metadata.namespace - value: mds - documentIndex: 3 - - equal: - path: spec.replicas - value: 1 - documentIndex: 3 - - equal: - path: spec.selector.matchLabels.app - value: mds-daily - documentIndex: 3 - - equal: - path: spec.template.metadata.labels.app - value: mds-daily - documentIndex: 3 - - equal: - path: spec.template.spec.containers[0].name - value: mds-daily - documentIndex: 3 - - equal: - path: spec.template.spec.containers[0].image - value: mds-daily:latest - documentIndex: 3 - - equal: - path: spec.template.spec.containers[0].imagePullPolicy - value: IfNotPresent - documentIndex: 3 - - equal: - path: spec.template.spec.containers[0].resources.limits.cpu - value: 500m - documentIndex: 3 - - equal: - path: spec.template.spec.containers[0].resources.limits.memory - value: 512Mi - documentIndex: 3 - - equal: - path: spec.template.spec.containers[0].resources.requests.cpu - value: 50m - documentIndex: 3 - - equal: - path: spec.template.spec.containers[0].resources.requests.memory - value: 128Mi - documentIndex: 3 - - equal: - path: spec.template.spec.containers[0].ports[0].containerPort - value: 4005 - documentIndex: 3 - - equal: - path: spec.template.spec.containers[0].ports[0].name - value: user-port - documentIndex: 3 - - equal: - path: spec.template.spec.containers[0].ports[0].protocol - value: TCP - documentIndex: 3 - - equal: - path: spec.template.spec.containers[0].env[0].name - value: PATH_PREFIX - documentIndex: 3 - - equal: - path: spec.template.spec.containers[0].env[0].value - value: /daily - documentIndex: 3 - - equal: - path: spec.template.spec.containers[0].env[1].name - value: TIMEZONE - documentIndex: 3 - - equal: - path: spec.template.spec.containers[0].env[1].value - value: America/Los_Angeles - documentIndex: 3 - - equal: - path: spec.template.spec.containers[0].env[2].name - value: PG_USER - documentIndex: 3 - - equal: - path: spec.template.spec.containers[0].env[2].value - value: mdsadmin - documentIndex: 3 - - equal: - path: spec.template.spec.containers[0].env[3].name - value: PG_NAME - documentIndex: 3 - - equal: - path: spec.template.spec.containers[0].env[3].value - value: mds - documentIndex: 3 - - equal: - path: spec.template.spec.containers[0].env[4].name - value: PG_PASS - documentIndex: 3 - - equal: - path: spec.template.spec.containers[0].env[4].valueFrom.secretKeyRef.name - value: pg-pass - documentIndex: 3 - - equal: - path: spec.template.spec.containers[0].env[4].valueFrom.secretKeyRef.key - value: postgresql-password - documentIndex: 3 - - equal: - path: spec.template.spec.containers[0].env[5].name - value: PG_PORT - documentIndex: 3 - - equal: - path: spec.template.spec.containers[0].env[5].value - value: "5432" - documentIndex: 3 - - equal: - path: spec.template.spec.containers[0].env[6].name - value: PG_HOST - documentIndex: 3 - - equal: - path: spec.template.spec.containers[0].env[6].value - value: mds-postgresql.mds.svc.cluster.local - documentIndex: 3 - - equal: - path: spec.template.spec.containers[0].env[7].name - value: PG_HOST_READER - documentIndex: 3 - - equal: - path: spec.template.spec.containers[0].env[7].value - value: mds-postgresql.mds.svc.cluster.local - documentIndex: 3 - - equal: - path: spec.template.spec.containers[0].env[8].name - value: REDIS_HOST - documentIndex: 3 - - equal: - path: spec.template.spec.containers[0].env[8].value - value: mds-redis-master.mds.svc.cluster.local - documentIndex: 3 - - equal: - path: spec.template.spec.containers[0].env[9].name - value: REDIS_PORT - documentIndex: 3 - - equal: - path: spec.template.spec.containers[0].env[9].value - value: "6379" - documentIndex: 3 - - equal: - path: spec.template.spec.containers[0].env[10].name - value: CE_NAME - documentIndex: 3 - - equal: - path: spec.template.spec.containers[0].env[10].value - value: mds-daily - documentIndex: 3 - - equal: - path: spec.template.spec.containers[0].env[11].name - value: SINK - documentIndex: 3 - - equal: - path: spec.template.spec.containers[0].env[11].value - value: http://default-broker.mds.svc.cluster.local - documentIndex: 3 - - equal: - path: spec.template.spec.containers[0].env[12].name - value: foo - documentIndex: 3 - - equal: - path: spec.template.spec.containers[0].env[12].value - value: bar - documentIndex: 3 - - equal: - path: spec.template.spec.containers[0].env[13].name - value: bop - documentIndex: 3 - - equal: - path: spec.template.spec.containers[0].env[13].value - value: bing - documentIndex: 3 \ No newline at end of file diff --git a/helm/mds/tests/deployment-daily-no-cache_test.yaml b/helm/mds/tests/deployment-daily-no-cache_test.yaml deleted file mode 100644 index 950ccdf8f..000000000 --- a/helm/mds/tests/deployment-daily-no-cache_test.yaml +++ /dev/null @@ -1,164 +0,0 @@ -suite: test deployment daily no-cache -templates: - - deployment.yaml -tests: - - it: doc 3 - release: - namespace: mds - set: - apis.mds-daily.useCache: false - asserts: - - isAPIVersion: - of: apps/v1 - documentIndex: 3 - - isKind: - of: Deployment - documentIndex: 3 - - equal: - path: metadata.name - value: mds-daily - documentIndex: 3 - - equal: - path: metadata.labels.app - value: mds-daily - documentIndex: 3 - - equal: - path: metadata.namespace - value: mds - documentIndex: 3 - - equal: - path: spec.replicas - value: 1 - documentIndex: 3 - - equal: - path: spec.selector.matchLabels.app - value: mds-daily - documentIndex: 3 - - equal: - path: spec.template.metadata.labels.app - value: mds-daily - documentIndex: 3 - - equal: - path: spec.template.spec.containers[0].name - value: mds-daily - documentIndex: 3 - - equal: - path: spec.template.spec.containers[0].image - value: mds-daily:latest - documentIndex: 3 - - equal: - path: spec.template.spec.containers[0].imagePullPolicy - value: IfNotPresent - documentIndex: 3 - - equal: - path: spec.template.spec.containers[0].resources.limits.cpu - value: 500m - documentIndex: 3 - - equal: - path: spec.template.spec.containers[0].resources.limits.memory - value: 512Mi - documentIndex: 3 - - equal: - path: spec.template.spec.containers[0].resources.requests.cpu - value: 50m - documentIndex: 3 - - equal: - path: spec.template.spec.containers[0].resources.requests.memory - value: 128Mi - documentIndex: 3 - - equal: - path: spec.template.spec.containers[0].ports[0].containerPort - value: 4005 - documentIndex: 3 - - equal: - path: spec.template.spec.containers[0].ports[0].name - value: user-port - documentIndex: 3 - - equal: - path: spec.template.spec.containers[0].ports[0].protocol - value: TCP - documentIndex: 3 - - equal: - path: spec.template.spec.containers[0].env[0].name - value: PATH_PREFIX - documentIndex: 3 - - equal: - path: spec.template.spec.containers[0].env[0].value - value: /daily - documentIndex: 3 - - equal: - path: spec.template.spec.containers[0].env[1].name - value: TIMEZONE - documentIndex: 3 - - equal: - path: spec.template.spec.containers[0].env[1].value - value: America/Los_Angeles - documentIndex: 3 - - equal: - path: spec.template.spec.containers[0].env[2].name - value: PG_USER - documentIndex: 3 - - equal: - path: spec.template.spec.containers[0].env[2].value - value: mdsadmin - documentIndex: 3 - - equal: - path: spec.template.spec.containers[0].env[3].name - value: PG_NAME - documentIndex: 3 - - equal: - path: spec.template.spec.containers[0].env[3].value - value: mds - documentIndex: 3 - - equal: - path: spec.template.spec.containers[0].env[4].name - value: PG_PASS - documentIndex: 3 - - equal: - path: spec.template.spec.containers[0].env[4].valueFrom.secretKeyRef.name - value: pg-pass - documentIndex: 3 - - equal: - path: spec.template.spec.containers[0].env[4].valueFrom.secretKeyRef.key - value: postgresql-password - documentIndex: 3 - - equal: - path: spec.template.spec.containers[0].env[5].name - value: PG_PORT - documentIndex: 3 - - equal: - path: spec.template.spec.containers[0].env[5].value - value: "5432" - documentIndex: 3 - - equal: - path: spec.template.spec.containers[0].env[6].name - value: PG_HOST - documentIndex: 3 - - equal: - path: spec.template.spec.containers[0].env[6].value - value: mds-postgresql.mds.svc.cluster.local - documentIndex: 3 - - equal: - path: spec.template.spec.containers[0].env[7].name - value: PG_HOST_READER - documentIndex: 3 - - equal: - path: spec.template.spec.containers[0].env[7].value - value: mds-postgresql.mds.svc.cluster.local - documentIndex: 3 - - equal: - path: spec.template.spec.containers[0].env[8].name - value: CE_NAME - documentIndex: 3 - - equal: - path: spec.template.spec.containers[0].env[8].value - value: mds-daily - documentIndex: 3 - - equal: - path: spec.template.spec.containers[0].env[9].name - value: SINK - documentIndex: 3 - - equal: - path: spec.template.spec.containers[0].env[9].value - value: http://default-broker.mds.svc.cluster.local - documentIndex: 3 diff --git a/helm/mds/tests/deployment-daily-no-db_test.yaml b/helm/mds/tests/deployment-daily-no-db_test.yaml deleted file mode 100644 index 900108588..000000000 --- a/helm/mds/tests/deployment-daily-no-db_test.yaml +++ /dev/null @@ -1,128 +0,0 @@ -suite: test deployment daily no-db -templates: - - deployment.yaml -tests: - - it: doc 3 - release: - namespace: mds - set: - apis.mds-daily.useDB: false - asserts: - - isAPIVersion: - of: apps/v1 - documentIndex: 3 - - isKind: - of: Deployment - documentIndex: 3 - - equal: - path: metadata.name - value: mds-daily - documentIndex: 3 - - equal: - path: metadata.labels.app - value: mds-daily - documentIndex: 3 - - equal: - path: metadata.namespace - value: mds - documentIndex: 3 - - equal: - path: spec.replicas - value: 1 - documentIndex: 3 - - equal: - path: spec.selector.matchLabels.app - value: mds-daily - documentIndex: 3 - - equal: - path: spec.template.metadata.labels.app - value: mds-daily - documentIndex: 3 - - equal: - path: spec.template.spec.containers[0].name - value: mds-daily - documentIndex: 3 - - equal: - path: spec.template.spec.containers[0].image - value: mds-daily:latest - documentIndex: 3 - - equal: - path: spec.template.spec.containers[0].imagePullPolicy - value: IfNotPresent - documentIndex: 3 - - equal: - path: spec.template.spec.containers[0].resources.limits.cpu - value: 500m - documentIndex: 3 - - equal: - path: spec.template.spec.containers[0].resources.limits.memory - value: 512Mi - documentIndex: 3 - - equal: - path: spec.template.spec.containers[0].resources.requests.cpu - value: 50m - documentIndex: 3 - - equal: - path: spec.template.spec.containers[0].resources.requests.memory - value: 128Mi - documentIndex: 3 - - equal: - path: spec.template.spec.containers[0].ports[0].containerPort - value: 4005 - documentIndex: 3 - - equal: - path: spec.template.spec.containers[0].ports[0].name - value: user-port - documentIndex: 3 - - equal: - path: spec.template.spec.containers[0].ports[0].protocol - value: TCP - documentIndex: 3 - - equal: - path: spec.template.spec.containers[0].env[0].name - value: PATH_PREFIX - documentIndex: 3 - - equal: - path: spec.template.spec.containers[0].env[0].value - value: /daily - documentIndex: 3 - - equal: - path: spec.template.spec.containers[0].env[1].name - value: TIMEZONE - documentIndex: 3 - - equal: - path: spec.template.spec.containers[0].env[1].value - value: America/Los_Angeles - documentIndex: 3 - - equal: - path: spec.template.spec.containers[0].env[2].name - value: REDIS_HOST - documentIndex: 3 - - equal: - path: spec.template.spec.containers[0].env[2].value - value: mds-redis-master.mds.svc.cluster.local - documentIndex: 3 - - equal: - path: spec.template.spec.containers[0].env[3].name - value: REDIS_PORT - documentIndex: 3 - - equal: - path: spec.template.spec.containers[0].env[3].value - value: "6379" - documentIndex: 3 - - equal: - path: spec.template.spec.containers[0].env[4].name - value: CE_NAME - documentIndex: 3 - - equal: - path: spec.template.spec.containers[0].env[4].value - value: mds-daily - documentIndex: 3 - - equal: - path: spec.template.spec.containers[0].env[5].name - value: SINK - documentIndex: 3 - - equal: - path: spec.template.spec.containers[0].env[5].value - value: http://default-broker.mds.svc.cluster.local - documentIndex: 3 diff --git a/helm/mds/tests/deployment-daily-registry_test.yaml b/helm/mds/tests/deployment-daily-registry_test.yaml deleted file mode 100644 index e9a132930..000000000 --- a/helm/mds/tests/deployment-daily-registry_test.yaml +++ /dev/null @@ -1,179 +0,0 @@ -suite: test deployment daily registry -templates: - - deployment.yaml -tests: - - it: doc 3 - set: - registry: foo-registry - release: - namespace: mds - asserts: - - isAPIVersion: - of: apps/v1 - documentIndex: 3 - - isKind: - of: Deployment - documentIndex: 3 - - equal: - path: metadata.name - value: mds-daily - documentIndex: 3 - - equal: - path: metadata.labels.app - value: mds-daily - documentIndex: 3 - - equal: - path: metadata.namespace - value: mds - documentIndex: 3 - - equal: - path: spec.replicas - value: 1 - documentIndex: 3 - - equal: - path: spec.selector.matchLabels.app - value: mds-daily - documentIndex: 3 - - equal: - path: spec.template.metadata.labels.app - value: mds-daily - documentIndex: 3 - - equal: - path: spec.template.spec.containers[0].name - value: mds-daily - documentIndex: 3 - - equal: - path: spec.template.spec.containers[0].image - value: foo-registry/mds-daily:latest - documentIndex: 3 - - isNull: - path: spec.template.spec.containers[0].imagePullPolicy - documentIndex: 3 - - equal: - path: spec.template.spec.containers[0].resources.limits.cpu - value: 500m - documentIndex: 3 - - equal: - path: spec.template.spec.containers[0].resources.limits.memory - value: 512Mi - documentIndex: 3 - - equal: - path: spec.template.spec.containers[0].resources.requests.cpu - value: 50m - documentIndex: 3 - - equal: - path: spec.template.spec.containers[0].resources.requests.memory - value: 128Mi - documentIndex: 3 - - equal: - path: spec.template.spec.containers[0].ports[0].containerPort - value: 4005 - documentIndex: 3 - - equal: - path: spec.template.spec.containers[0].ports[0].name - value: user-port - documentIndex: 3 - - equal: - path: spec.template.spec.containers[0].ports[0].protocol - value: TCP - documentIndex: 3 - - equal: - path: spec.template.spec.containers[0].env[0].name - value: PATH_PREFIX - documentIndex: 3 - - equal: - path: spec.template.spec.containers[0].env[0].value - value: /daily - documentIndex: 3 - - equal: - path: spec.template.spec.containers[0].env[1].name - value: TIMEZONE - documentIndex: 3 - - equal: - path: spec.template.spec.containers[0].env[1].value - value: America/Los_Angeles - documentIndex: 3 - - equal: - path: spec.template.spec.containers[0].env[2].name - value: PG_USER - documentIndex: 3 - - equal: - path: spec.template.spec.containers[0].env[2].value - value: mdsadmin - documentIndex: 3 - - equal: - path: spec.template.spec.containers[0].env[3].name - value: PG_NAME - documentIndex: 3 - - equal: - path: spec.template.spec.containers[0].env[3].value - value: mds - documentIndex: 3 - - equal: - path: spec.template.spec.containers[0].env[4].name - value: PG_PASS - documentIndex: 3 - - equal: - path: spec.template.spec.containers[0].env[4].valueFrom.secretKeyRef.name - value: pg-pass - documentIndex: 3 - - equal: - path: spec.template.spec.containers[0].env[4].valueFrom.secretKeyRef.key - value: postgresql-password - documentIndex: 3 - - equal: - path: spec.template.spec.containers[0].env[5].name - value: PG_PORT - documentIndex: 3 - - equal: - path: spec.template.spec.containers[0].env[5].value - value: "5432" - documentIndex: 3 - - equal: - path: spec.template.spec.containers[0].env[6].name - value: PG_HOST - documentIndex: 3 - - equal: - path: spec.template.spec.containers[0].env[6].value - value: mds-postgresql.mds.svc.cluster.local - documentIndex: 3 - - equal: - path: spec.template.spec.containers[0].env[7].name - value: PG_HOST_READER - documentIndex: 3 - - equal: - path: spec.template.spec.containers[0].env[7].value - value: mds-postgresql.mds.svc.cluster.local - documentIndex: 3 - - equal: - path: spec.template.spec.containers[0].env[8].name - value: REDIS_HOST - documentIndex: 3 - - equal: - path: spec.template.spec.containers[0].env[8].value - value: mds-redis-master.mds.svc.cluster.local - documentIndex: 3 - - equal: - path: spec.template.spec.containers[0].env[9].name - value: REDIS_PORT - documentIndex: 3 - - equal: - path: spec.template.spec.containers[0].env[9].value - value: "6379" - documentIndex: 3 - - equal: - path: spec.template.spec.containers[0].env[10].name - value: CE_NAME - documentIndex: 3 - - equal: - path: spec.template.spec.containers[0].env[10].value - value: mds-daily - documentIndex: 3 - - equal: - path: spec.template.spec.containers[0].env[11].name - value: SINK - documentIndex: 3 - - equal: - path: spec.template.spec.containers[0].env[11].value - value: http://default-broker.mds.svc.cluster.local - documentIndex: 3 diff --git a/helm/mds/tests/deployment-daily-resource-all_test.yaml b/helm/mds/tests/deployment-daily-resource-all_test.yaml deleted file mode 100644 index 989aaff14..000000000 --- a/helm/mds/tests/deployment-daily-resource-all_test.yaml +++ /dev/null @@ -1,183 +0,0 @@ -suite: test deployment daily default -templates: - - deployment.yaml -tests: - - it: doc 3 - set: - resourcesLimitsCpu: 250m - resourcesLimitsMemory: 256Mi - resourcesRequestsCpu: 5m - resourcesRequestsMemory: 13Mi - release: - namespace: mds - asserts: - - isAPIVersion: - of: apps/v1 - documentIndex: 3 - - isKind: - of: Deployment - documentIndex: 3 - - equal: - path: metadata.name - value: mds-daily - documentIndex: 3 - - equal: - path: metadata.labels.app - value: mds-daily - documentIndex: 3 - - equal: - path: metadata.namespace - value: mds - documentIndex: 3 - - equal: - path: spec.replicas - value: 1 - documentIndex: 3 - - equal: - path: spec.selector.matchLabels.app - value: mds-daily - documentIndex: 3 - - equal: - path: spec.template.metadata.labels.app - value: mds-daily - documentIndex: 3 - - equal: - path: spec.template.spec.containers[0].name - value: mds-daily - documentIndex: 3 - - equal: - path: spec.template.spec.containers[0].image - value: mds-daily:latest - documentIndex: 3 - - equal: - path: spec.template.spec.containers[0].imagePullPolicy - value: IfNotPresent - documentIndex: 3 - - equal: - path: spec.template.spec.containers[0].resources.limits.cpu - value: 250m - documentIndex: 3 - - equal: - path: spec.template.spec.containers[0].resources.limits.memory - value: 256Mi - documentIndex: 3 - - equal: - path: spec.template.spec.containers[0].resources.requests.cpu - value: 5m - documentIndex: 3 - - equal: - path: spec.template.spec.containers[0].resources.requests.memory - value: 13Mi - documentIndex: 3 - - equal: - path: spec.template.spec.containers[0].ports[0].containerPort - value: 4005 - documentIndex: 3 - - equal: - path: spec.template.spec.containers[0].ports[0].name - value: user-port - documentIndex: 3 - - equal: - path: spec.template.spec.containers[0].ports[0].protocol - value: TCP - documentIndex: 3 - - equal: - path: spec.template.spec.containers[0].env[0].name - value: PATH_PREFIX - documentIndex: 3 - - equal: - path: spec.template.spec.containers[0].env[0].value - value: /daily - documentIndex: 3 - - equal: - path: spec.template.spec.containers[0].env[1].name - value: TIMEZONE - documentIndex: 3 - - equal: - path: spec.template.spec.containers[0].env[1].value - value: America/Los_Angeles - documentIndex: 3 - - equal: - path: spec.template.spec.containers[0].env[2].name - value: PG_USER - documentIndex: 3 - - equal: - path: spec.template.spec.containers[0].env[2].value - value: mdsadmin - documentIndex: 3 - - equal: - path: spec.template.spec.containers[0].env[3].name - value: PG_NAME - documentIndex: 3 - - equal: - path: spec.template.spec.containers[0].env[3].value - value: mds - documentIndex: 3 - - equal: - path: spec.template.spec.containers[0].env[4].name - value: PG_PASS - documentIndex: 3 - - equal: - path: spec.template.spec.containers[0].env[4].valueFrom.secretKeyRef.name - value: pg-pass - documentIndex: 3 - - equal: - path: spec.template.spec.containers[0].env[4].valueFrom.secretKeyRef.key - value: postgresql-password - documentIndex: 3 - - equal: - path: spec.template.spec.containers[0].env[5].name - value: PG_PORT - documentIndex: 3 - - equal: - path: spec.template.spec.containers[0].env[5].value - value: "5432" - documentIndex: 3 - - equal: - path: spec.template.spec.containers[0].env[6].name - value: PG_HOST - documentIndex: 3 - - equal: - path: spec.template.spec.containers[0].env[6].value - value: mds-postgresql.mds.svc.cluster.local - documentIndex: 3 - - equal: - path: spec.template.spec.containers[0].env[7].name - value: PG_HOST_READER - documentIndex: 3 - - equal: - path: spec.template.spec.containers[0].env[7].value - value: mds-postgresql.mds.svc.cluster.local - documentIndex: 3 - - equal: - path: spec.template.spec.containers[0].env[8].name - value: REDIS_HOST - documentIndex: 3 - - equal: - path: spec.template.spec.containers[0].env[8].value - value: mds-redis-master.mds.svc.cluster.local - documentIndex: 3 - - equal: - path: spec.template.spec.containers[0].env[9].name - value: REDIS_PORT - documentIndex: 3 - - equal: - path: spec.template.spec.containers[0].env[9].value - value: "6379" - documentIndex: 3 - - equal: - path: spec.template.spec.containers[0].env[10].name - value: CE_NAME - documentIndex: 3 - - equal: - path: spec.template.spec.containers[0].env[10].value - value: mds-daily - documentIndex: 3 - - equal: - path: spec.template.spec.containers[0].env[11].name - value: SINK - documentIndex: 3 - - equal: - path: spec.template.spec.containers[0].env[11].value - value: http://default-broker.mds.svc.cluster.local - documentIndex: 3 diff --git a/helm/mds/tests/deployment-native-default_test.yaml b/helm/mds/tests/deployment-daily_test.yaml similarity index 84% rename from helm/mds/tests/deployment-native-default_test.yaml rename to helm/mds/tests/deployment-daily_test.yaml index 9619c494d..67d6db290 100644 --- a/helm/mds/tests/deployment-native-default_test.yaml +++ b/helm/mds/tests/deployment-daily_test.yaml @@ -1,8 +1,8 @@ -suite: test deployment native default +suite: test deployment daily templates: - deployment.yaml tests: - - it: doc 4 + - it: doc 0 release: namespace: mds asserts: @@ -14,11 +14,11 @@ tests: documentIndex: 4 - equal: path: metadata.name - value: mds-native + value: mds-daily documentIndex: 4 - equal: path: metadata.labels.app - value: mds-native + value: mds-daily documentIndex: 4 - equal: path: metadata.namespace @@ -30,23 +30,19 @@ tests: documentIndex: 4 - equal: path: spec.selector.matchLabels.app - value: mds-native + value: mds-daily documentIndex: 4 - equal: path: spec.template.metadata.labels.app - value: mds-native + value: mds-daily documentIndex: 4 - equal: path: spec.template.spec.containers[0].name - value: mds-native + value: mds-daily documentIndex: 4 - equal: path: spec.template.spec.containers[0].image - value: mds-native:latest - documentIndex: 4 - - equal: - path: spec.template.spec.containers[0].imagePullPolicy - value: IfNotPresent + value: mds-daily:latest documentIndex: 4 - equal: path: spec.template.spec.containers[0].resources.limits.cpu @@ -66,7 +62,7 @@ tests: documentIndex: 4 - equal: path: spec.template.spec.containers[0].ports[0].containerPort - value: 4006 + value: 4000 documentIndex: 4 - equal: path: spec.template.spec.containers[0].ports[0].name @@ -78,55 +74,55 @@ tests: documentIndex: 4 - equal: path: spec.template.spec.containers[0].env[0].name - value: PATH_PREFIX + value: PORT documentIndex: 4 - equal: path: spec.template.spec.containers[0].env[0].value - value: /native + value: "4000" documentIndex: 4 - equal: path: spec.template.spec.containers[0].env[1].name - value: TIMEZONE + value: PATH_PREFIX documentIndex: 4 - equal: path: spec.template.spec.containers[0].env[1].value - value: America/Los_Angeles + value: /daily documentIndex: 4 - equal: path: spec.template.spec.containers[0].env[2].name - value: PG_USER + value: TIMEZONE documentIndex: 4 - equal: path: spec.template.spec.containers[0].env[2].value - value: mdsadmin + value: America/Los_Angeles documentIndex: 4 - equal: path: spec.template.spec.containers[0].env[3].name - value: PG_NAME + value: PG_USER documentIndex: 4 - equal: path: spec.template.spec.containers[0].env[3].value - value: mds + value: mdsadmin documentIndex: 4 - equal: path: spec.template.spec.containers[0].env[4].name - value: PG_PASS + value: PG_NAME documentIndex: 4 - equal: - path: spec.template.spec.containers[0].env[4].valueFrom.secretKeyRef.name - value: pg-pass + path: spec.template.spec.containers[0].env[4].value + value: mds documentIndex: 4 - equal: - path: spec.template.spec.containers[0].env[4].valueFrom.secretKeyRef.key - value: postgresql-password + path: spec.template.spec.containers[0].env[5].name + value: PG_PASS documentIndex: 4 - equal: - path: spec.template.spec.containers[0].env[5].name - value: PG_PORT + path: spec.template.spec.containers[0].env[5].valueFrom.secretKeyRef.name + value: mds-secrets documentIndex: 4 - equal: - path: spec.template.spec.containers[0].env[5].value - value: "5432" + path: spec.template.spec.containers[0].env[5].valueFrom.secretKeyRef.key + value: postgresql-password documentIndex: 4 - equal: path: spec.template.spec.containers[0].env[6].name @@ -138,11 +134,11 @@ tests: documentIndex: 4 - equal: path: spec.template.spec.containers[0].env[7].name - value: PG_HOST_READER + value: PG_PORT documentIndex: 4 - equal: path: spec.template.spec.containers[0].env[7].value - value: mds-postgresql.mds.svc.cluster.local + value: "5432" documentIndex: 4 - equal: path: spec.template.spec.containers[0].env[8].name @@ -162,17 +158,24 @@ tests: documentIndex: 4 - equal: path: spec.template.spec.containers[0].env[10].name - value: CE_NAME + value: NATS documentIndex: 4 - equal: path: spec.template.spec.containers[0].env[10].value - value: mds-native + value: nats.nats.svc.cluster.local documentIndex: 4 - equal: path: spec.template.spec.containers[0].env[11].name - value: SINK + value: STAN_CLUSTER documentIndex: 4 - equal: path: spec.template.spec.containers[0].env[11].value - value: http://default-broker.mds.svc.cluster.local + value: nats-streaming + documentIndex: 4 + - equal: + path: spec.template.spec.containers[0].env[12].name + value: TENANT_ID + documentIndex: 4 + - isEmpty: + path: spec.template.spec.containers[0].env[12].value documentIndex: 4 diff --git a/helm/mds/tests/deployment-policy-author-default_test.yaml b/helm/mds/tests/deployment-geography-author_test.yaml similarity index 82% rename from helm/mds/tests/deployment-policy-author-default_test.yaml rename to helm/mds/tests/deployment-geography-author_test.yaml index 7d789dabb..c067d5065 100644 --- a/helm/mds/tests/deployment-policy-author-default_test.yaml +++ b/helm/mds/tests/deployment-geography-author_test.yaml @@ -1,8 +1,8 @@ -suite: test deployment policy-author default +suite: test deployment geography-author templates: - deployment.yaml tests: - - it: doc 6 + - it: doc 0 release: namespace: mds asserts: @@ -14,11 +14,11 @@ tests: documentIndex: 6 - equal: path: metadata.name - value: mds-policy-author + value: mds-geography-author documentIndex: 6 - equal: path: metadata.labels.app - value: mds-policy-author + value: mds-geography-author documentIndex: 6 - equal: path: metadata.namespace @@ -30,23 +30,19 @@ tests: documentIndex: 6 - equal: path: spec.selector.matchLabels.app - value: mds-policy-author + value: mds-geography-author documentIndex: 6 - equal: path: spec.template.metadata.labels.app - value: mds-policy-author + value: mds-geography-author documentIndex: 6 - equal: path: spec.template.spec.containers[0].name - value: mds-policy-author + value: mds-geography-author documentIndex: 6 - equal: path: spec.template.spec.containers[0].image - value: mds-policy-author:latest - documentIndex: 6 - - equal: - path: spec.template.spec.containers[0].imagePullPolicy - value: IfNotPresent + value: mds-geography-author:latest documentIndex: 6 - equal: path: spec.template.spec.containers[0].resources.limits.cpu @@ -66,7 +62,7 @@ tests: documentIndex: 6 - equal: path: spec.template.spec.containers[0].ports[0].containerPort - value: 4007 + value: 4000 documentIndex: 6 - equal: path: spec.template.spec.containers[0].ports[0].name @@ -78,55 +74,55 @@ tests: documentIndex: 6 - equal: path: spec.template.spec.containers[0].env[0].name - value: PATH_PREFIX + value: PORT documentIndex: 6 - equal: path: spec.template.spec.containers[0].env[0].value - value: /policy-author + value: "4000" documentIndex: 6 - equal: path: spec.template.spec.containers[0].env[1].name - value: TIMEZONE + value: PATH_PREFIX documentIndex: 6 - equal: path: spec.template.spec.containers[0].env[1].value - value: America/Los_Angeles + value: /geography-author documentIndex: 6 - equal: path: spec.template.spec.containers[0].env[2].name - value: PG_USER + value: TIMEZONE documentIndex: 6 - equal: path: spec.template.spec.containers[0].env[2].value - value: mdsadmin + value: America/Los_Angeles documentIndex: 6 - equal: path: spec.template.spec.containers[0].env[3].name - value: PG_NAME + value: PG_USER documentIndex: 6 - equal: path: spec.template.spec.containers[0].env[3].value - value: mds + value: mdsadmin documentIndex: 6 - equal: path: spec.template.spec.containers[0].env[4].name - value: PG_PASS + value: PG_NAME documentIndex: 6 - equal: - path: spec.template.spec.containers[0].env[4].valueFrom.secretKeyRef.name - value: pg-pass + path: spec.template.spec.containers[0].env[4].value + value: mds documentIndex: 6 - equal: - path: spec.template.spec.containers[0].env[4].valueFrom.secretKeyRef.key - value: postgresql-password + path: spec.template.spec.containers[0].env[5].name + value: PG_PASS documentIndex: 6 - equal: - path: spec.template.spec.containers[0].env[5].name - value: PG_PORT + path: spec.template.spec.containers[0].env[5].valueFrom.secretKeyRef.name + value: mds-secrets documentIndex: 6 - equal: - path: spec.template.spec.containers[0].env[5].value - value: '5432' + path: spec.template.spec.containers[0].env[5].valueFrom.secretKeyRef.key + value: postgresql-password documentIndex: 6 - equal: path: spec.template.spec.containers[0].env[6].name @@ -138,11 +134,11 @@ tests: documentIndex: 6 - equal: path: spec.template.spec.containers[0].env[7].name - value: PG_HOST_READER + value: PG_PORT documentIndex: 6 - equal: path: spec.template.spec.containers[0].env[7].value - value: mds-postgresql.mds.svc.cluster.local + value: "5432" documentIndex: 6 - equal: path: spec.template.spec.containers[0].env[8].name @@ -158,21 +154,28 @@ tests: documentIndex: 6 - equal: path: spec.template.spec.containers[0].env[9].value - value: '6379' + value: "6379" documentIndex: 6 - equal: path: spec.template.spec.containers[0].env[10].name - value: CE_NAME + value: NATS documentIndex: 6 - equal: path: spec.template.spec.containers[0].env[10].value - value: mds-policy-author + value: nats.nats.svc.cluster.local documentIndex: 6 - equal: path: spec.template.spec.containers[0].env[11].name - value: SINK + value: STAN_CLUSTER documentIndex: 6 - equal: path: spec.template.spec.containers[0].env[11].value - value: http://default-broker.mds.svc.cluster.local + value: nats-streaming + documentIndex: 6 + - equal: + path: spec.template.spec.containers[0].env[12].name + value: TENANT_ID + documentIndex: 6 + - isEmpty: + path: spec.template.spec.containers[0].env[12].value documentIndex: 6 diff --git a/helm/mds/tests/deployment-daily-default_test.yaml b/helm/mds/tests/deployment-jurisdiction_test.yaml similarity index 65% rename from helm/mds/tests/deployment-daily-default_test.yaml rename to helm/mds/tests/deployment-jurisdiction_test.yaml index 94e34bb59..71d349a72 100644 --- a/helm/mds/tests/deployment-daily-default_test.yaml +++ b/helm/mds/tests/deployment-jurisdiction_test.yaml @@ -1,178 +1,181 @@ -suite: test deployment daily default +suite: test deployment jurisdiction templates: - deployment.yaml tests: - - it: doc 3 + - it: doc 0 release: namespace: mds asserts: - isAPIVersion: of: apps/v1 - documentIndex: 3 + documentIndex: 7 - isKind: of: Deployment - documentIndex: 3 + documentIndex: 7 - equal: path: metadata.name - value: mds-daily - documentIndex: 3 + value: mds-jurisdiction + documentIndex: 7 - equal: path: metadata.labels.app - value: mds-daily - documentIndex: 3 + value: mds-jurisdiction + documentIndex: 7 - equal: path: metadata.namespace value: mds - documentIndex: 3 + documentIndex: 7 - equal: path: spec.replicas value: 1 - documentIndex: 3 + documentIndex: 7 - equal: path: spec.selector.matchLabels.app - value: mds-daily - documentIndex: 3 + value: mds-jurisdiction + documentIndex: 7 - equal: path: spec.template.metadata.labels.app - value: mds-daily - documentIndex: 3 + value: mds-jurisdiction + documentIndex: 7 - equal: path: spec.template.spec.containers[0].name - value: mds-daily - documentIndex: 3 + value: mds-jurisdiction + documentIndex: 7 - equal: path: spec.template.spec.containers[0].image - value: mds-daily:latest - documentIndex: 3 - - equal: - path: spec.template.spec.containers[0].imagePullPolicy - value: IfNotPresent - documentIndex: 3 + value: mds-jurisdiction:latest + documentIndex: 7 - equal: path: spec.template.spec.containers[0].resources.limits.cpu value: 500m - documentIndex: 3 + documentIndex: 7 - equal: path: spec.template.spec.containers[0].resources.limits.memory value: 512Mi - documentIndex: 3 + documentIndex: 7 - equal: path: spec.template.spec.containers[0].resources.requests.cpu value: 50m - documentIndex: 3 + documentIndex: 7 - equal: path: spec.template.spec.containers[0].resources.requests.memory value: 128Mi - documentIndex: 3 + documentIndex: 7 - equal: path: spec.template.spec.containers[0].ports[0].containerPort - value: 4005 - documentIndex: 3 + value: 4000 + documentIndex: 7 - equal: path: spec.template.spec.containers[0].ports[0].name value: user-port - documentIndex: 3 + documentIndex: 7 - equal: path: spec.template.spec.containers[0].ports[0].protocol value: TCP - documentIndex: 3 + documentIndex: 7 - equal: path: spec.template.spec.containers[0].env[0].name - value: PATH_PREFIX - documentIndex: 3 + value: PORT + documentIndex: 7 - equal: path: spec.template.spec.containers[0].env[0].value - value: /daily - documentIndex: 3 + value: "4000" + documentIndex: 7 - equal: path: spec.template.spec.containers[0].env[1].name - value: TIMEZONE - documentIndex: 3 + value: PATH_PREFIX + documentIndex: 7 - equal: path: spec.template.spec.containers[0].env[1].value - value: America/Los_Angeles - documentIndex: 3 + value: /jurisdiction + documentIndex: 7 - equal: path: spec.template.spec.containers[0].env[2].name - value: PG_USER - documentIndex: 3 + value: TIMEZONE + documentIndex: 7 - equal: path: spec.template.spec.containers[0].env[2].value - value: mdsadmin - documentIndex: 3 + value: America/Los_Angeles + documentIndex: 7 - equal: path: spec.template.spec.containers[0].env[3].name - value: PG_NAME - documentIndex: 3 + value: PG_USER + documentIndex: 7 - equal: path: spec.template.spec.containers[0].env[3].value - value: mds - documentIndex: 3 + value: mdsadmin + documentIndex: 7 - equal: path: spec.template.spec.containers[0].env[4].name - value: PG_PASS - documentIndex: 3 - - equal: - path: spec.template.spec.containers[0].env[4].valueFrom.secretKeyRef.name - value: pg-pass - documentIndex: 3 + value: PG_NAME + documentIndex: 7 - equal: - path: spec.template.spec.containers[0].env[4].valueFrom.secretKeyRef.key - value: postgresql-password - documentIndex: 3 + path: spec.template.spec.containers[0].env[4].value + value: mds + documentIndex: 7 - equal: path: spec.template.spec.containers[0].env[5].name - value: PG_PORT - documentIndex: 3 + value: PG_PASS + documentIndex: 7 - equal: - path: spec.template.spec.containers[0].env[5].value - value: "5432" - documentIndex: 3 + path: spec.template.spec.containers[0].env[5].valueFrom.secretKeyRef.name + value: mds-secrets + documentIndex: 7 + - equal: + path: spec.template.spec.containers[0].env[5].valueFrom.secretKeyRef.key + value: postgresql-password + documentIndex: 7 - equal: path: spec.template.spec.containers[0].env[6].name value: PG_HOST - documentIndex: 3 + documentIndex: 7 - equal: path: spec.template.spec.containers[0].env[6].value value: mds-postgresql.mds.svc.cluster.local - documentIndex: 3 + documentIndex: 7 - equal: path: spec.template.spec.containers[0].env[7].name - value: PG_HOST_READER - documentIndex: 3 + value: PG_PORT + documentIndex: 7 - equal: path: spec.template.spec.containers[0].env[7].value - value: mds-postgresql.mds.svc.cluster.local - documentIndex: 3 + value: "5432" + documentIndex: 7 - equal: path: spec.template.spec.containers[0].env[8].name value: REDIS_HOST - documentIndex: 3 + documentIndex: 7 - equal: path: spec.template.spec.containers[0].env[8].value value: mds-redis-master.mds.svc.cluster.local - documentIndex: 3 + documentIndex: 7 - equal: path: spec.template.spec.containers[0].env[9].name value: REDIS_PORT - documentIndex: 3 + documentIndex: 7 - equal: path: spec.template.spec.containers[0].env[9].value value: "6379" - documentIndex: 3 + documentIndex: 7 - equal: path: spec.template.spec.containers[0].env[10].name - value: CE_NAME - documentIndex: 3 + value: NATS + documentIndex: 7 - equal: path: spec.template.spec.containers[0].env[10].value - value: mds-daily - documentIndex: 3 + value: nats.nats.svc.cluster.local + documentIndex: 7 - equal: path: spec.template.spec.containers[0].env[11].name - value: SINK - documentIndex: 3 + value: STAN_CLUSTER + documentIndex: 7 - equal: path: spec.template.spec.containers[0].env[11].value - value: http://default-broker.mds.svc.cluster.local - documentIndex: 3 + value: nats-streaming + documentIndex: 7 + - equal: + path: spec.template.spec.containers[0].env[12].name + value: TENANT_ID + documentIndex: 7 + - isEmpty: + path: spec.template.spec.containers[0].env[12].value + documentIndex: 7 diff --git a/helm/mds/tests/deployment-native-env_test.yaml b/helm/mds/tests/deployment-native-env_test.yaml deleted file mode 100644 index 271c983c0..000000000 --- a/helm/mds/tests/deployment-native-env_test.yaml +++ /dev/null @@ -1,202 +0,0 @@ -suite: test deployment native default -templates: - - deployment.yaml -tests: - - it: doc 4 - release: - namespace: mds - set: - apis: - mds-native: - env: - - name: foo - value: bar - - name: bop - value: bing - asserts: - - isAPIVersion: - of: apps/v1 - documentIndex: 4 - - isKind: - of: Deployment - documentIndex: 4 - - equal: - path: metadata.name - value: mds-native - documentIndex: 4 - - equal: - path: metadata.labels.app - value: mds-native - documentIndex: 4 - - equal: - path: metadata.namespace - value: mds - documentIndex: 4 - - equal: - path: spec.replicas - value: 1 - documentIndex: 4 - - equal: - path: spec.selector.matchLabels.app - value: mds-native - documentIndex: 4 - - equal: - path: spec.template.metadata.labels.app - value: mds-native - documentIndex: 4 - - equal: - path: spec.template.spec.containers[0].name - value: mds-native - documentIndex: 4 - - equal: - path: spec.template.spec.containers[0].image - value: mds-native:latest - documentIndex: 4 - - equal: - path: spec.template.spec.containers[0].imagePullPolicy - value: IfNotPresent - documentIndex: 4 - - equal: - path: spec.template.spec.containers[0].resources.limits.cpu - value: 500m - documentIndex: 4 - - equal: - path: spec.template.spec.containers[0].resources.limits.memory - value: 512Mi - documentIndex: 4 - - equal: - path: spec.template.spec.containers[0].resources.requests.cpu - value: 50m - documentIndex: 4 - - equal: - path: spec.template.spec.containers[0].resources.requests.memory - value: 128Mi - documentIndex: 4 - - equal: - path: spec.template.spec.containers[0].ports[0].containerPort - value: 4006 - documentIndex: 4 - - equal: - path: spec.template.spec.containers[0].ports[0].name - value: user-port - documentIndex: 4 - - equal: - path: spec.template.spec.containers[0].ports[0].protocol - value: TCP - documentIndex: 4 - - equal: - path: spec.template.spec.containers[0].env[0].name - value: PATH_PREFIX - documentIndex: 4 - - equal: - path: spec.template.spec.containers[0].env[0].value - value: /native - documentIndex: 4 - - equal: - path: spec.template.spec.containers[0].env[1].name - value: TIMEZONE - documentIndex: 4 - - equal: - path: spec.template.spec.containers[0].env[1].value - value: America/Los_Angeles - documentIndex: 4 - - equal: - path: spec.template.spec.containers[0].env[2].name - value: PG_USER - documentIndex: 4 - - equal: - path: spec.template.spec.containers[0].env[2].value - value: mdsadmin - documentIndex: 4 - - equal: - path: spec.template.spec.containers[0].env[3].name - value: PG_NAME - documentIndex: 4 - - equal: - path: spec.template.spec.containers[0].env[3].value - value: mds - documentIndex: 4 - - equal: - path: spec.template.spec.containers[0].env[4].name - value: PG_PASS - documentIndex: 4 - - equal: - path: spec.template.spec.containers[0].env[4].valueFrom.secretKeyRef.name - value: pg-pass - documentIndex: 4 - - equal: - path: spec.template.spec.containers[0].env[4].valueFrom.secretKeyRef.key - value: postgresql-password - documentIndex: 4 - - equal: - path: spec.template.spec.containers[0].env[5].name - value: PG_PORT - documentIndex: 4 - - equal: - path: spec.template.spec.containers[0].env[5].value - value: "5432" - documentIndex: 4 - - equal: - path: spec.template.spec.containers[0].env[6].name - value: PG_HOST - documentIndex: 4 - - equal: - path: spec.template.spec.containers[0].env[6].value - value: mds-postgresql.mds.svc.cluster.local - documentIndex: 4 - - equal: - path: spec.template.spec.containers[0].env[7].name - value: PG_HOST_READER - documentIndex: 4 - - equal: - path: spec.template.spec.containers[0].env[7].value - value: mds-postgresql.mds.svc.cluster.local - documentIndex: 4 - - equal: - path: spec.template.spec.containers[0].env[8].name - value: REDIS_HOST - documentIndex: 4 - - equal: - path: spec.template.spec.containers[0].env[8].value - value: mds-redis-master.mds.svc.cluster.local - documentIndex: 4 - - equal: - path: spec.template.spec.containers[0].env[9].name - value: REDIS_PORT - documentIndex: 4 - - equal: - path: spec.template.spec.containers[0].env[9].value - value: "6379" - documentIndex: 4 - - equal: - path: spec.template.spec.containers[0].env[10].name - value: CE_NAME - documentIndex: 4 - - equal: - path: spec.template.spec.containers[0].env[10].value - value: mds-native - documentIndex: 4 - - equal: - path: spec.template.spec.containers[0].env[11].name - value: SINK - documentIndex: 4 - - equal: - path: spec.template.spec.containers[0].env[11].value - value: http://default-broker.mds.svc.cluster.local - documentIndex: 4 - - equal: - path: spec.template.spec.containers[0].env[12].name - value: foo - documentIndex: 4 - - equal: - path: spec.template.spec.containers[0].env[12].value - value: bar - documentIndex: 4 - - equal: - path: spec.template.spec.containers[0].env[13].name - value: bop - documentIndex: 4 - - equal: - path: spec.template.spec.containers[0].env[13].value - value: bing - documentIndex: 4 \ No newline at end of file diff --git a/helm/mds/tests/deployment-native-no-cache_test.yaml b/helm/mds/tests/deployment-native-no-cache_test.yaml deleted file mode 100644 index d8f5da54f..000000000 --- a/helm/mds/tests/deployment-native-no-cache_test.yaml +++ /dev/null @@ -1,164 +0,0 @@ -suite: test deployment native no-cache -templates: - - deployment.yaml -tests: - - it: doc 4 - release: - namespace: mds - set: - apis.mds-native.useCache: false - asserts: - - isAPIVersion: - of: apps/v1 - documentIndex: 4 - - isKind: - of: Deployment - documentIndex: 4 - - equal: - path: metadata.name - value: mds-native - documentIndex: 4 - - equal: - path: metadata.labels.app - value: mds-native - documentIndex: 4 - - equal: - path: metadata.namespace - value: mds - documentIndex: 4 - - equal: - path: spec.replicas - value: 1 - documentIndex: 4 - - equal: - path: spec.selector.matchLabels.app - value: mds-native - documentIndex: 4 - - equal: - path: spec.template.metadata.labels.app - value: mds-native - documentIndex: 4 - - equal: - path: spec.template.spec.containers[0].name - value: mds-native - documentIndex: 4 - - equal: - path: spec.template.spec.containers[0].image - value: mds-native:latest - documentIndex: 4 - - equal: - path: spec.template.spec.containers[0].imagePullPolicy - value: IfNotPresent - documentIndex: 4 - - equal: - path: spec.template.spec.containers[0].resources.limits.cpu - value: 500m - documentIndex: 4 - - equal: - path: spec.template.spec.containers[0].resources.limits.memory - value: 512Mi - documentIndex: 4 - - equal: - path: spec.template.spec.containers[0].resources.requests.cpu - value: 50m - documentIndex: 4 - - equal: - path: spec.template.spec.containers[0].resources.requests.memory - value: 128Mi - documentIndex: 4 - - equal: - path: spec.template.spec.containers[0].ports[0].containerPort - value: 4006 - documentIndex: 4 - - equal: - path: spec.template.spec.containers[0].ports[0].name - value: user-port - documentIndex: 4 - - equal: - path: spec.template.spec.containers[0].ports[0].protocol - value: TCP - documentIndex: 4 - - equal: - path: spec.template.spec.containers[0].env[0].name - value: PATH_PREFIX - documentIndex: 4 - - equal: - path: spec.template.spec.containers[0].env[0].value - value: /native - documentIndex: 4 - - equal: - path: spec.template.spec.containers[0].env[1].name - value: TIMEZONE - documentIndex: 4 - - equal: - path: spec.template.spec.containers[0].env[1].value - value: America/Los_Angeles - documentIndex: 4 - - equal: - path: spec.template.spec.containers[0].env[2].name - value: PG_USER - documentIndex: 4 - - equal: - path: spec.template.spec.containers[0].env[2].value - value: mdsadmin - documentIndex: 4 - - equal: - path: spec.template.spec.containers[0].env[3].name - value: PG_NAME - documentIndex: 4 - - equal: - path: spec.template.spec.containers[0].env[3].value - value: mds - documentIndex: 4 - - equal: - path: spec.template.spec.containers[0].env[4].name - value: PG_PASS - documentIndex: 4 - - equal: - path: spec.template.spec.containers[0].env[4].valueFrom.secretKeyRef.name - value: pg-pass - documentIndex: 4 - - equal: - path: spec.template.spec.containers[0].env[4].valueFrom.secretKeyRef.key - value: postgresql-password - documentIndex: 4 - - equal: - path: spec.template.spec.containers[0].env[5].name - value: PG_PORT - documentIndex: 4 - - equal: - path: spec.template.spec.containers[0].env[5].value - value: "5432" - documentIndex: 4 - - equal: - path: spec.template.spec.containers[0].env[6].name - value: PG_HOST - documentIndex: 4 - - equal: - path: spec.template.spec.containers[0].env[6].value - value: mds-postgresql.mds.svc.cluster.local - documentIndex: 4 - - equal: - path: spec.template.spec.containers[0].env[7].name - value: PG_HOST_READER - documentIndex: 4 - - equal: - path: spec.template.spec.containers[0].env[7].value - value: mds-postgresql.mds.svc.cluster.local - documentIndex: 4 - - equal: - path: spec.template.spec.containers[0].env[8].name - value: CE_NAME - documentIndex: 4 - - equal: - path: spec.template.spec.containers[0].env[8].value - value: mds-native - documentIndex: 4 - - equal: - path: spec.template.spec.containers[0].env[9].name - value: SINK - documentIndex: 4 - - equal: - path: spec.template.spec.containers[0].env[9].value - value: http://default-broker.mds.svc.cluster.local - documentIndex: 4 diff --git a/helm/mds/tests/deployment-native-no-db_test.yaml b/helm/mds/tests/deployment-native-no-db_test.yaml deleted file mode 100644 index 9238d1b78..000000000 --- a/helm/mds/tests/deployment-native-no-db_test.yaml +++ /dev/null @@ -1,128 +0,0 @@ -suite: test deployment native no-db -templates: - - deployment.yaml -tests: - - it: doc 4 - release: - namespace: mds - set: - apis.mds-native.useDB: false - asserts: - - isAPIVersion: - of: apps/v1 - documentIndex: 4 - - isKind: - of: Deployment - documentIndex: 4 - - equal: - path: metadata.name - value: mds-native - documentIndex: 4 - - equal: - path: metadata.labels.app - value: mds-native - documentIndex: 4 - - equal: - path: metadata.namespace - value: mds - documentIndex: 4 - - equal: - path: spec.replicas - value: 1 - documentIndex: 4 - - equal: - path: spec.selector.matchLabels.app - value: mds-native - documentIndex: 4 - - equal: - path: spec.template.metadata.labels.app - value: mds-native - documentIndex: 4 - - equal: - path: spec.template.spec.containers[0].name - value: mds-native - documentIndex: 4 - - equal: - path: spec.template.spec.containers[0].image - value: mds-native:latest - documentIndex: 4 - - equal: - path: spec.template.spec.containers[0].imagePullPolicy - value: IfNotPresent - documentIndex: 4 - - equal: - path: spec.template.spec.containers[0].resources.limits.cpu - value: 500m - documentIndex: 4 - - equal: - path: spec.template.spec.containers[0].resources.limits.memory - value: 512Mi - documentIndex: 4 - - equal: - path: spec.template.spec.containers[0].resources.requests.cpu - value: 50m - documentIndex: 4 - - equal: - path: spec.template.spec.containers[0].resources.requests.memory - value: 128Mi - documentIndex: 4 - - equal: - path: spec.template.spec.containers[0].ports[0].containerPort - value: 4006 - documentIndex: 4 - - equal: - path: spec.template.spec.containers[0].ports[0].name - value: user-port - documentIndex: 4 - - equal: - path: spec.template.spec.containers[0].ports[0].protocol - value: TCP - documentIndex: 4 - - equal: - path: spec.template.spec.containers[0].env[0].name - value: PATH_PREFIX - documentIndex: 4 - - equal: - path: spec.template.spec.containers[0].env[0].value - value: /native - documentIndex: 4 - - equal: - path: spec.template.spec.containers[0].env[1].name - value: TIMEZONE - documentIndex: 4 - - equal: - path: spec.template.spec.containers[0].env[1].value - value: America/Los_Angeles - documentIndex: 4 - - equal: - path: spec.template.spec.containers[0].env[2].name - value: REDIS_HOST - documentIndex: 4 - - equal: - path: spec.template.spec.containers[0].env[2].value - value: mds-redis-master.mds.svc.cluster.local - documentIndex: 4 - - equal: - path: spec.template.spec.containers[0].env[3].name - value: REDIS_PORT - documentIndex: 4 - - equal: - path: spec.template.spec.containers[0].env[3].value - value: "6379" - documentIndex: 4 - - equal: - path: spec.template.spec.containers[0].env[4].name - value: CE_NAME - documentIndex: 4 - - equal: - path: spec.template.spec.containers[0].env[4].value - value: mds-native - documentIndex: 4 - - equal: - path: spec.template.spec.containers[0].env[5].name - value: SINK - documentIndex: 4 - - equal: - path: spec.template.spec.containers[0].env[5].value - value: http://default-broker.mds.svc.cluster.local - documentIndex: 4 diff --git a/helm/mds/tests/deployment-native-no-events_test.yaml b/helm/mds/tests/deployment-native-no-events_test.yaml deleted file mode 100644 index 2015e4b1f..000000000 --- a/helm/mds/tests/deployment-native-no-events_test.yaml +++ /dev/null @@ -1,168 +0,0 @@ -suite: test deployment native no-events -templates: - - deployment.yaml -tests: - - it: doc 4 - release: - namespace: mds - set: - apis.mds-native.useEvents: false - asserts: - - isAPIVersion: - of: apps/v1 - documentIndex: 4 - - isKind: - of: Deployment - documentIndex: 4 - - equal: - path: metadata.name - value: mds-native - documentIndex: 4 - - equal: - path: metadata.labels.app - value: mds-native - documentIndex: 4 - - equal: - path: metadata.namespace - value: mds - documentIndex: 4 - - equal: - path: spec.replicas - value: 1 - documentIndex: 4 - - equal: - path: spec.selector.matchLabels.app - value: mds-native - documentIndex: 4 - - equal: - path: spec.template.metadata.labels.app - value: mds-native - documentIndex: 4 - - equal: - path: spec.template.spec.containers[0].name - value: mds-native - documentIndex: 4 - - equal: - path: spec.template.spec.containers[0].image - value: mds-native:latest - documentIndex: 4 - - equal: - path: spec.template.spec.containers[0].imagePullPolicy - value: IfNotPresent - documentIndex: 4 - - equal: - path: spec.template.spec.containers[0].resources.limits.cpu - value: 500m - documentIndex: 4 - - equal: - path: spec.template.spec.containers[0].resources.limits.memory - value: 512Mi - documentIndex: 4 - - equal: - path: spec.template.spec.containers[0].resources.requests.cpu - value: 50m - documentIndex: 4 - - equal: - path: spec.template.spec.containers[0].resources.requests.memory - value: 128Mi - documentIndex: 4 - - equal: - path: spec.template.spec.containers[0].ports[0].containerPort - value: 4006 - documentIndex: 4 - - equal: - path: spec.template.spec.containers[0].ports[0].name - value: user-port - documentIndex: 4 - - equal: - path: spec.template.spec.containers[0].ports[0].protocol - value: TCP - documentIndex: 4 - - equal: - path: spec.template.spec.containers[0].env[0].name - value: PATH_PREFIX - documentIndex: 4 - - equal: - path: spec.template.spec.containers[0].env[0].value - value: /native - documentIndex: 4 - - equal: - path: spec.template.spec.containers[0].env[1].name - value: TIMEZONE - documentIndex: 4 - - equal: - path: spec.template.spec.containers[0].env[1].value - value: America/Los_Angeles - documentIndex: 4 - - equal: - path: spec.template.spec.containers[0].env[2].name - value: PG_USER - documentIndex: 4 - - equal: - path: spec.template.spec.containers[0].env[2].value - value: mdsadmin - documentIndex: 4 - - equal: - path: spec.template.spec.containers[0].env[3].name - value: PG_NAME - documentIndex: 4 - - equal: - path: spec.template.spec.containers[0].env[3].value - value: mds - documentIndex: 4 - - equal: - path: spec.template.spec.containers[0].env[4].name - value: PG_PASS - documentIndex: 4 - - equal: - path: spec.template.spec.containers[0].env[4].valueFrom.secretKeyRef.name - value: pg-pass - documentIndex: 4 - - equal: - path: spec.template.spec.containers[0].env[4].valueFrom.secretKeyRef.key - value: postgresql-password - documentIndex: 4 - - equal: - path: spec.template.spec.containers[0].env[5].name - value: PG_PORT - documentIndex: 4 - - equal: - path: spec.template.spec.containers[0].env[5].value - value: "5432" - documentIndex: 4 - - equal: - path: spec.template.spec.containers[0].env[6].name - value: PG_HOST - documentIndex: 4 - - equal: - path: spec.template.spec.containers[0].env[6].value - value: mds-postgresql.mds.svc.cluster.local - documentIndex: 4 - - equal: - path: spec.template.spec.containers[0].env[7].name - value: PG_HOST_READER - documentIndex: 4 - - equal: - path: spec.template.spec.containers[0].env[7].value - value: mds-postgresql.mds.svc.cluster.local - documentIndex: 4 - - equal: - path: spec.template.spec.containers[0].env[8].name - value: REDIS_HOST - documentIndex: 4 - - equal: - path: spec.template.spec.containers[0].env[8].value - value: mds-redis-master.mds.svc.cluster.local - documentIndex: 4 - - equal: - path: spec.template.spec.containers[0].env[9].name - value: REDIS_PORT - documentIndex: 4 - - equal: - path: spec.template.spec.containers[0].env[9].value - value: "6379" - documentIndex: 4 - # fixme: test non-existence - # - isNull: - # path: spec.template.spec.containers[0].env[10] - # documentIndex: 1 \ No newline at end of file diff --git a/helm/mds/tests/deployment-native-registry_test.yaml b/helm/mds/tests/deployment-native-registry_test.yaml deleted file mode 100644 index 8470c68da..000000000 --- a/helm/mds/tests/deployment-native-registry_test.yaml +++ /dev/null @@ -1,179 +0,0 @@ -suite: test deployment native registry -templates: - - deployment.yaml -tests: - - it: doc 4 - set: - registry: foo-registry - release: - namespace: mds - asserts: - - isAPIVersion: - of: apps/v1 - documentIndex: 4 - - isKind: - of: Deployment - documentIndex: 4 - - equal: - path: metadata.name - value: mds-native - documentIndex: 4 - - equal: - path: metadata.labels.app - value: mds-native - documentIndex: 4 - - equal: - path: metadata.namespace - value: mds - documentIndex: 4 - - equal: - path: spec.replicas - value: 1 - documentIndex: 4 - - equal: - path: spec.selector.matchLabels.app - value: mds-native - documentIndex: 4 - - equal: - path: spec.template.metadata.labels.app - value: mds-native - documentIndex: 4 - - equal: - path: spec.template.spec.containers[0].name - value: mds-native - documentIndex: 4 - - equal: - path: spec.template.spec.containers[0].image - value: foo-registry/mds-native:latest - documentIndex: 4 - - isNull: - path: spec.template.spec.containers[0].imagePullPolicy - documentIndex: 4 - - equal: - path: spec.template.spec.containers[0].resources.limits.cpu - value: 500m - documentIndex: 4 - - equal: - path: spec.template.spec.containers[0].resources.limits.memory - value: 512Mi - documentIndex: 4 - - equal: - path: spec.template.spec.containers[0].resources.requests.cpu - value: 50m - documentIndex: 4 - - equal: - path: spec.template.spec.containers[0].resources.requests.memory - value: 128Mi - documentIndex: 4 - - equal: - path: spec.template.spec.containers[0].ports[0].containerPort - value: 4006 - documentIndex: 4 - - equal: - path: spec.template.spec.containers[0].ports[0].name - value: user-port - documentIndex: 4 - - equal: - path: spec.template.spec.containers[0].ports[0].protocol - value: TCP - documentIndex: 4 - - equal: - path: spec.template.spec.containers[0].env[0].name - value: PATH_PREFIX - documentIndex: 4 - - equal: - path: spec.template.spec.containers[0].env[0].value - value: /native - documentIndex: 4 - - equal: - path: spec.template.spec.containers[0].env[1].name - value: TIMEZONE - documentIndex: 4 - - equal: - path: spec.template.spec.containers[0].env[1].value - value: America/Los_Angeles - documentIndex: 4 - - equal: - path: spec.template.spec.containers[0].env[2].name - value: PG_USER - documentIndex: 4 - - equal: - path: spec.template.spec.containers[0].env[2].value - value: mdsadmin - documentIndex: 4 - - equal: - path: spec.template.spec.containers[0].env[3].name - value: PG_NAME - documentIndex: 4 - - equal: - path: spec.template.spec.containers[0].env[3].value - value: mds - documentIndex: 4 - - equal: - path: spec.template.spec.containers[0].env[4].name - value: PG_PASS - documentIndex: 4 - - equal: - path: spec.template.spec.containers[0].env[4].valueFrom.secretKeyRef.name - value: pg-pass - documentIndex: 4 - - equal: - path: spec.template.spec.containers[0].env[4].valueFrom.secretKeyRef.key - value: postgresql-password - documentIndex: 4 - - equal: - path: spec.template.spec.containers[0].env[5].name - value: PG_PORT - documentIndex: 4 - - equal: - path: spec.template.spec.containers[0].env[5].value - value: "5432" - documentIndex: 4 - - equal: - path: spec.template.spec.containers[0].env[6].name - value: PG_HOST - documentIndex: 4 - - equal: - path: spec.template.spec.containers[0].env[6].value - value: mds-postgresql.mds.svc.cluster.local - documentIndex: 4 - - equal: - path: spec.template.spec.containers[0].env[7].name - value: PG_HOST_READER - documentIndex: 4 - - equal: - path: spec.template.spec.containers[0].env[7].value - value: mds-postgresql.mds.svc.cluster.local - documentIndex: 4 - - equal: - path: spec.template.spec.containers[0].env[8].name - value: REDIS_HOST - documentIndex: 4 - - equal: - path: spec.template.spec.containers[0].env[8].value - value: mds-redis-master.mds.svc.cluster.local - documentIndex: 4 - - equal: - path: spec.template.spec.containers[0].env[9].name - value: REDIS_PORT - documentIndex: 4 - - equal: - path: spec.template.spec.containers[0].env[9].value - value: "6379" - documentIndex: 4 - - equal: - path: spec.template.spec.containers[0].env[10].name - value: CE_NAME - documentIndex: 4 - - equal: - path: spec.template.spec.containers[0].env[10].value - value: mds-native - documentIndex: 4 - - equal: - path: spec.template.spec.containers[0].env[11].name - value: SINK - documentIndex: 4 - - equal: - path: spec.template.spec.containers[0].env[11].value - value: http://default-broker.mds.svc.cluster.local - documentIndex: 4 diff --git a/helm/mds/tests/deployment-native-resource-all_test.yaml b/helm/mds/tests/deployment-native-resource-all_test.yaml deleted file mode 100644 index d10471266..000000000 --- a/helm/mds/tests/deployment-native-resource-all_test.yaml +++ /dev/null @@ -1,183 +0,0 @@ -suite: test deployment native default -templates: - - deployment.yaml -tests: - - it: doc 4 - set: - resourcesLimitsCpu: 250m - resourcesLimitsMemory: 256Mi - resourcesRequestsCpu: 5m - resourcesRequestsMemory: 13Mi - release: - namespace: mds - asserts: - - isAPIVersion: - of: apps/v1 - documentIndex: 4 - - isKind: - of: Deployment - documentIndex: 4 - - equal: - path: metadata.name - value: mds-native - documentIndex: 4 - - equal: - path: metadata.labels.app - value: mds-native - documentIndex: 4 - - equal: - path: metadata.namespace - value: mds - documentIndex: 4 - - equal: - path: spec.replicas - value: 1 - documentIndex: 4 - - equal: - path: spec.selector.matchLabels.app - value: mds-native - documentIndex: 4 - - equal: - path: spec.template.metadata.labels.app - value: mds-native - documentIndex: 4 - - equal: - path: spec.template.spec.containers[0].name - value: mds-native - documentIndex: 4 - - equal: - path: spec.template.spec.containers[0].image - value: mds-native:latest - documentIndex: 4 - - equal: - path: spec.template.spec.containers[0].imagePullPolicy - value: IfNotPresent - documentIndex: 4 - - equal: - path: spec.template.spec.containers[0].resources.limits.cpu - value: 250m - documentIndex: 4 - - equal: - path: spec.template.spec.containers[0].resources.limits.memory - value: 256Mi - documentIndex: 4 - - equal: - path: spec.template.spec.containers[0].resources.requests.cpu - value: 5m - documentIndex: 4 - - equal: - path: spec.template.spec.containers[0].resources.requests.memory - value: 13Mi - documentIndex: 4 - - equal: - path: spec.template.spec.containers[0].ports[0].containerPort - value: 4006 - documentIndex: 4 - - equal: - path: spec.template.spec.containers[0].ports[0].name - value: user-port - documentIndex: 4 - - equal: - path: spec.template.spec.containers[0].ports[0].protocol - value: TCP - documentIndex: 4 - - equal: - path: spec.template.spec.containers[0].env[0].name - value: PATH_PREFIX - documentIndex: 4 - - equal: - path: spec.template.spec.containers[0].env[0].value - value: /native - documentIndex: 4 - - equal: - path: spec.template.spec.containers[0].env[1].name - value: TIMEZONE - documentIndex: 4 - - equal: - path: spec.template.spec.containers[0].env[1].value - value: America/Los_Angeles - documentIndex: 4 - - equal: - path: spec.template.spec.containers[0].env[2].name - value: PG_USER - documentIndex: 4 - - equal: - path: spec.template.spec.containers[0].env[2].value - value: mdsadmin - documentIndex: 4 - - equal: - path: spec.template.spec.containers[0].env[3].name - value: PG_NAME - documentIndex: 4 - - equal: - path: spec.template.spec.containers[0].env[3].value - value: mds - documentIndex: 4 - - equal: - path: spec.template.spec.containers[0].env[4].name - value: PG_PASS - documentIndex: 4 - - equal: - path: spec.template.spec.containers[0].env[4].valueFrom.secretKeyRef.name - value: pg-pass - documentIndex: 4 - - equal: - path: spec.template.spec.containers[0].env[4].valueFrom.secretKeyRef.key - value: postgresql-password - documentIndex: 4 - - equal: - path: spec.template.spec.containers[0].env[5].name - value: PG_PORT - documentIndex: 4 - - equal: - path: spec.template.spec.containers[0].env[5].value - value: "5432" - documentIndex: 4 - - equal: - path: spec.template.spec.containers[0].env[6].name - value: PG_HOST - documentIndex: 4 - - equal: - path: spec.template.spec.containers[0].env[6].value - value: mds-postgresql.mds.svc.cluster.local - documentIndex: 4 - - equal: - path: spec.template.spec.containers[0].env[7].name - value: PG_HOST_READER - documentIndex: 4 - - equal: - path: spec.template.spec.containers[0].env[7].value - value: mds-postgresql.mds.svc.cluster.local - documentIndex: 4 - - equal: - path: spec.template.spec.containers[0].env[8].name - value: REDIS_HOST - documentIndex: 4 - - equal: - path: spec.template.spec.containers[0].env[8].value - value: mds-redis-master.mds.svc.cluster.local - documentIndex: 4 - - equal: - path: spec.template.spec.containers[0].env[9].name - value: REDIS_PORT - documentIndex: 4 - - equal: - path: spec.template.spec.containers[0].env[9].value - value: "6379" - documentIndex: 4 - - equal: - path: spec.template.spec.containers[0].env[10].name - value: CE_NAME - documentIndex: 4 - - equal: - path: spec.template.spec.containers[0].env[10].value - value: mds-native - documentIndex: 4 - - equal: - path: spec.template.spec.containers[0].env[11].name - value: SINK - documentIndex: 4 - - equal: - path: spec.template.spec.containers[0].env[11].value - value: http://default-broker.mds.svc.cluster.local - documentIndex: 4 diff --git a/helm/mds/tests/deployment-policy-author-env_test.yaml b/helm/mds/tests/deployment-policy-author-env_test.yaml deleted file mode 100644 index c74a738fb..000000000 --- a/helm/mds/tests/deployment-policy-author-env_test.yaml +++ /dev/null @@ -1,202 +0,0 @@ -suite: test deployment policy-author default -templates: - - deployment.yaml -tests: - - it: doc 6 - release: - namespace: mds - set: - apis: - mds-policy-author: - env: - - name: foo - value: bar - - name: bop - value: bing - asserts: - - isAPIVersion: - of: apps/v1 - documentIndex: 6 - - isKind: - of: Deployment - documentIndex: 6 - - equal: - path: metadata.name - value: mds-policy-author - documentIndex: 6 - - equal: - path: metadata.labels.app - value: mds-policy-author - documentIndex: 6 - - equal: - path: metadata.namespace - value: mds - documentIndex: 6 - - equal: - path: spec.replicas - value: 1 - documentIndex: 6 - - equal: - path: spec.selector.matchLabels.app - value: mds-policy-author - documentIndex: 6 - - equal: - path: spec.template.metadata.labels.app - value: mds-policy-author - documentIndex: 6 - - equal: - path: spec.template.spec.containers[0].name - value: mds-policy-author - documentIndex: 6 - - equal: - path: spec.template.spec.containers[0].image - value: mds-policy-author:latest - documentIndex: 6 - - equal: - path: spec.template.spec.containers[0].imagePullPolicy - value: IfNotPresent - documentIndex: 6 - - equal: - path: spec.template.spec.containers[0].resources.limits.cpu - value: 500m - documentIndex: 6 - - equal: - path: spec.template.spec.containers[0].resources.limits.memory - value: 512Mi - documentIndex: 6 - - equal: - path: spec.template.spec.containers[0].resources.requests.cpu - value: 50m - documentIndex: 6 - - equal: - path: spec.template.spec.containers[0].resources.requests.memory - value: 128Mi - documentIndex: 6 - - equal: - path: spec.template.spec.containers[0].ports[0].containerPort - value: 4007 - documentIndex: 6 - - equal: - path: spec.template.spec.containers[0].ports[0].name - value: user-port - documentIndex: 6 - - equal: - path: spec.template.spec.containers[0].ports[0].protocol - value: TCP - documentIndex: 6 - - equal: - path: spec.template.spec.containers[0].env[0].name - value: PATH_PREFIX - documentIndex: 6 - - equal: - path: spec.template.spec.containers[0].env[0].value - value: /policy-author - documentIndex: 6 - - equal: - path: spec.template.spec.containers[0].env[1].name - value: TIMEZONE - documentIndex: 6 - - equal: - path: spec.template.spec.containers[0].env[1].value - value: America/Los_Angeles - documentIndex: 6 - - equal: - path: spec.template.spec.containers[0].env[2].name - value: PG_USER - documentIndex: 6 - - equal: - path: spec.template.spec.containers[0].env[2].value - value: mdsadmin - documentIndex: 6 - - equal: - path: spec.template.spec.containers[0].env[3].name - value: PG_NAME - documentIndex: 6 - - equal: - path: spec.template.spec.containers[0].env[3].value - value: mds - documentIndex: 6 - - equal: - path: spec.template.spec.containers[0].env[4].name - value: PG_PASS - documentIndex: 6 - - equal: - path: spec.template.spec.containers[0].env[4].valueFrom.secretKeyRef.name - value: pg-pass - documentIndex: 6 - - equal: - path: spec.template.spec.containers[0].env[4].valueFrom.secretKeyRef.key - value: postgresql-password - documentIndex: 6 - - equal: - path: spec.template.spec.containers[0].env[5].name - value: PG_PORT - documentIndex: 6 - - equal: - path: spec.template.spec.containers[0].env[5].value - value: '5432' - documentIndex: 6 - - equal: - path: spec.template.spec.containers[0].env[6].name - value: PG_HOST - documentIndex: 6 - - equal: - path: spec.template.spec.containers[0].env[6].value - value: mds-postgresql.mds.svc.cluster.local - documentIndex: 6 - - equal: - path: spec.template.spec.containers[0].env[7].name - value: PG_HOST_READER - documentIndex: 6 - - equal: - path: spec.template.spec.containers[0].env[7].value - value: mds-postgresql.mds.svc.cluster.local - documentIndex: 6 - - equal: - path: spec.template.spec.containers[0].env[8].name - value: REDIS_HOST - documentIndex: 6 - - equal: - path: spec.template.spec.containers[0].env[8].value - value: mds-redis-master.mds.svc.cluster.local - documentIndex: 6 - - equal: - path: spec.template.spec.containers[0].env[9].name - value: REDIS_PORT - documentIndex: 6 - - equal: - path: spec.template.spec.containers[0].env[9].value - value: '6379' - documentIndex: 6 - - equal: - path: spec.template.spec.containers[0].env[10].name - value: CE_NAME - documentIndex: 6 - - equal: - path: spec.template.spec.containers[0].env[10].value - value: mds-policy-author - documentIndex: 6 - - equal: - path: spec.template.spec.containers[0].env[11].name - value: SINK - documentIndex: 6 - - equal: - path: spec.template.spec.containers[0].env[11].value - value: http://default-broker.mds.svc.cluster.local - documentIndex: 6 - - equal: - path: spec.template.spec.containers[0].env[12].name - value: foo - documentIndex: 6 - - equal: - path: spec.template.spec.containers[0].env[12].value - value: bar - documentIndex: 6 - - equal: - path: spec.template.spec.containers[0].env[13].name - value: bop - documentIndex: 6 - - equal: - path: spec.template.spec.containers[0].env[13].value - value: bing - documentIndex: 6 \ No newline at end of file diff --git a/helm/mds/tests/deployment-policy-author-no-cache_test.yaml b/helm/mds/tests/deployment-policy-author-no-cache_test.yaml deleted file mode 100644 index 74db8c6c2..000000000 --- a/helm/mds/tests/deployment-policy-author-no-cache_test.yaml +++ /dev/null @@ -1,164 +0,0 @@ -suite: test deployment policy-author no-cache -templates: - - deployment.yaml -tests: - - it: doc 6 - release: - namespace: mds - set: - apis.mds-policy-author.useCache: false - asserts: - - isAPIVersion: - of: apps/v1 - documentIndex: 6 - - isKind: - of: Deployment - documentIndex: 6 - - equal: - path: metadata.name - value: mds-policy-author - documentIndex: 6 - - equal: - path: metadata.labels.app - value: mds-policy-author - documentIndex: 6 - - equal: - path: metadata.namespace - value: mds - documentIndex: 6 - - equal: - path: spec.replicas - value: 1 - documentIndex: 6 - - equal: - path: spec.selector.matchLabels.app - value: mds-policy-author - documentIndex: 6 - - equal: - path: spec.template.metadata.labels.app - value: mds-policy-author - documentIndex: 6 - - equal: - path: spec.template.spec.containers[0].name - value: mds-policy-author - documentIndex: 6 - - equal: - path: spec.template.spec.containers[0].image - value: mds-policy-author:latest - documentIndex: 6 - - equal: - path: spec.template.spec.containers[0].imagePullPolicy - value: IfNotPresent - documentIndex: 6 - - equal: - path: spec.template.spec.containers[0].resources.limits.cpu - value: 500m - documentIndex: 6 - - equal: - path: spec.template.spec.containers[0].resources.limits.memory - value: 512Mi - documentIndex: 6 - - equal: - path: spec.template.spec.containers[0].resources.requests.cpu - value: 50m - documentIndex: 6 - - equal: - path: spec.template.spec.containers[0].resources.requests.memory - value: 128Mi - documentIndex: 6 - - equal: - path: spec.template.spec.containers[0].ports[0].containerPort - value: 4007 - documentIndex: 6 - - equal: - path: spec.template.spec.containers[0].ports[0].name - value: user-port - documentIndex: 6 - - equal: - path: spec.template.spec.containers[0].ports[0].protocol - value: TCP - documentIndex: 6 - - equal: - path: spec.template.spec.containers[0].env[0].name - value: PATH_PREFIX - documentIndex: 6 - - equal: - path: spec.template.spec.containers[0].env[0].value - value: /policy-author - documentIndex: 6 - - equal: - path: spec.template.spec.containers[0].env[1].name - value: TIMEZONE - documentIndex: 6 - - equal: - path: spec.template.spec.containers[0].env[1].value - value: America/Los_Angeles - documentIndex: 6 - - equal: - path: spec.template.spec.containers[0].env[2].name - value: PG_USER - documentIndex: 6 - - equal: - path: spec.template.spec.containers[0].env[2].value - value: mdsadmin - documentIndex: 6 - - equal: - path: spec.template.spec.containers[0].env[3].name - value: PG_NAME - documentIndex: 6 - - equal: - path: spec.template.spec.containers[0].env[3].value - value: mds - documentIndex: 6 - - equal: - path: spec.template.spec.containers[0].env[4].name - value: PG_PASS - documentIndex: 6 - - equal: - path: spec.template.spec.containers[0].env[4].valueFrom.secretKeyRef.name - value: pg-pass - documentIndex: 6 - - equal: - path: spec.template.spec.containers[0].env[4].valueFrom.secretKeyRef.key - value: postgresql-password - documentIndex: 6 - - equal: - path: spec.template.spec.containers[0].env[5].name - value: PG_PORT - documentIndex: 6 - - equal: - path: spec.template.spec.containers[0].env[5].value - value: "5432" - documentIndex: 6 - - equal: - path: spec.template.spec.containers[0].env[6].name - value: PG_HOST - documentIndex: 6 - - equal: - path: spec.template.spec.containers[0].env[6].value - value: mds-postgresql.mds.svc.cluster.local - documentIndex: 6 - - equal: - path: spec.template.spec.containers[0].env[7].name - value: PG_HOST_READER - documentIndex: 6 - - equal: - path: spec.template.spec.containers[0].env[7].value - value: mds-postgresql.mds.svc.cluster.local - documentIndex: 6 - - equal: - path: spec.template.spec.containers[0].env[8].name - value: CE_NAME - documentIndex: 6 - - equal: - path: spec.template.spec.containers[0].env[8].value - value: mds-policy-author - documentIndex: 6 - - equal: - path: spec.template.spec.containers[0].env[9].name - value: SINK - documentIndex: 6 - - equal: - path: spec.template.spec.containers[0].env[9].value - value: http://default-broker.mds.svc.cluster.local - documentIndex: 6 diff --git a/helm/mds/tests/deployment-policy-author-no-db_test.yaml b/helm/mds/tests/deployment-policy-author-no-db_test.yaml deleted file mode 100644 index 63c99317e..000000000 --- a/helm/mds/tests/deployment-policy-author-no-db_test.yaml +++ /dev/null @@ -1,128 +0,0 @@ -suite: test deployment policy-author no-db -templates: - - deployment.yaml -tests: - - it: doc 6 - release: - namespace: mds - set: - apis.mds-policy-author.useDB: false - asserts: - - isAPIVersion: - of: apps/v1 - documentIndex: 6 - - isKind: - of: Deployment - documentIndex: 6 - - equal: - path: metadata.name - value: mds-policy-author - documentIndex: 6 - - equal: - path: metadata.labels.app - value: mds-policy-author - documentIndex: 6 - - equal: - path: metadata.namespace - value: mds - documentIndex: 6 - - equal: - path: spec.replicas - value: 1 - documentIndex: 6 - - equal: - path: spec.selector.matchLabels.app - value: mds-policy-author - documentIndex: 6 - - equal: - path: spec.template.metadata.labels.app - value: mds-policy-author - documentIndex: 6 - - equal: - path: spec.template.spec.containers[0].name - value: mds-policy-author - documentIndex: 6 - - equal: - path: spec.template.spec.containers[0].image - value: mds-policy-author:latest - documentIndex: 6 - - equal: - path: spec.template.spec.containers[0].imagePullPolicy - value: IfNotPresent - documentIndex: 6 - - equal: - path: spec.template.spec.containers[0].resources.limits.cpu - value: 500m - documentIndex: 6 - - equal: - path: spec.template.spec.containers[0].resources.limits.memory - value: 512Mi - documentIndex: 6 - - equal: - path: spec.template.spec.containers[0].resources.requests.cpu - value: 50m - documentIndex: 6 - - equal: - path: spec.template.spec.containers[0].resources.requests.memory - value: 128Mi - documentIndex: 6 - - equal: - path: spec.template.spec.containers[0].ports[0].containerPort - value: 4007 - documentIndex: 6 - - equal: - path: spec.template.spec.containers[0].ports[0].name - value: user-port - documentIndex: 6 - - equal: - path: spec.template.spec.containers[0].ports[0].protocol - value: TCP - documentIndex: 6 - - equal: - path: spec.template.spec.containers[0].env[0].name - value: PATH_PREFIX - documentIndex: 6 - - equal: - path: spec.template.spec.containers[0].env[0].value - value: /policy-author - documentIndex: 6 - - equal: - path: spec.template.spec.containers[0].env[1].name - value: TIMEZONE - documentIndex: 6 - - equal: - path: spec.template.spec.containers[0].env[1].value - value: America/Los_Angeles - documentIndex: 6 - - equal: - path: spec.template.spec.containers[0].env[2].name - value: REDIS_HOST - documentIndex: 6 - - equal: - path: spec.template.spec.containers[0].env[2].value - value: mds-redis-master.mds.svc.cluster.local - documentIndex: 6 - - equal: - path: spec.template.spec.containers[0].env[3].name - value: REDIS_PORT - documentIndex: 6 - - equal: - path: spec.template.spec.containers[0].env[3].value - value: '6379' - documentIndex: 6 - - equal: - path: spec.template.spec.containers[0].env[4].name - value: CE_NAME - documentIndex: 6 - - equal: - path: spec.template.spec.containers[0].env[4].value - value: mds-policy-author - documentIndex: 6 - - equal: - path: spec.template.spec.containers[0].env[5].name - value: SINK - documentIndex: 6 - - equal: - path: spec.template.spec.containers[0].env[5].value - value: http://default-broker.mds.svc.cluster.local - documentIndex: 6 diff --git a/helm/mds/tests/deployment-policy-author-no-events_test.yaml b/helm/mds/tests/deployment-policy-author-no-events_test.yaml deleted file mode 100644 index 2a841c11f..000000000 --- a/helm/mds/tests/deployment-policy-author-no-events_test.yaml +++ /dev/null @@ -1,168 +0,0 @@ -suite: test deployment policy-author no-events -templates: - - deployment.yaml -tests: - - it: doc 6 - release: - namespace: mds - set: - apis.mds-policy-author.useEvents: false - asserts: - - isAPIVersion: - of: apps/v1 - documentIndex: 6 - - isKind: - of: Deployment - documentIndex: 6 - - equal: - path: metadata.name - value: mds-policy-author - documentIndex: 6 - - equal: - path: metadata.labels.app - value: mds-policy-author - documentIndex: 6 - - equal: - path: metadata.namespace - value: mds - documentIndex: 6 - - equal: - path: spec.replicas - value: 1 - documentIndex: 6 - - equal: - path: spec.selector.matchLabels.app - value: mds-policy-author - documentIndex: 6 - - equal: - path: spec.template.metadata.labels.app - value: mds-policy-author - documentIndex: 6 - - equal: - path: spec.template.spec.containers[0].name - value: mds-policy-author - documentIndex: 6 - - equal: - path: spec.template.spec.containers[0].image - value: mds-policy-author:latest - documentIndex: 6 - - equal: - path: spec.template.spec.containers[0].imagePullPolicy - value: IfNotPresent - documentIndex: 6 - - equal: - path: spec.template.spec.containers[0].resources.limits.cpu - value: 500m - documentIndex: 6 - - equal: - path: spec.template.spec.containers[0].resources.limits.memory - value: 512Mi - documentIndex: 6 - - equal: - path: spec.template.spec.containers[0].resources.requests.cpu - value: 50m - documentIndex: 6 - - equal: - path: spec.template.spec.containers[0].resources.requests.memory - value: 128Mi - documentIndex: 6 - - equal: - path: spec.template.spec.containers[0].ports[0].containerPort - value: 4007 - documentIndex: 6 - - equal: - path: spec.template.spec.containers[0].ports[0].name - value: user-port - documentIndex: 6 - - equal: - path: spec.template.spec.containers[0].ports[0].protocol - value: TCP - documentIndex: 6 - - equal: - path: spec.template.spec.containers[0].env[0].name - value: PATH_PREFIX - documentIndex: 6 - - equal: - path: spec.template.spec.containers[0].env[0].value - value: /policy-author - documentIndex: 6 - - equal: - path: spec.template.spec.containers[0].env[1].name - value: TIMEZONE - documentIndex: 6 - - equal: - path: spec.template.spec.containers[0].env[1].value - value: America/Los_Angeles - documentIndex: 6 - - equal: - path: spec.template.spec.containers[0].env[2].name - value: PG_USER - documentIndex: 6 - - equal: - path: spec.template.spec.containers[0].env[2].value - value: mdsadmin - documentIndex: 6 - - equal: - path: spec.template.spec.containers[0].env[3].name - value: PG_NAME - documentIndex: 6 - - equal: - path: spec.template.spec.containers[0].env[3].value - value: mds - documentIndex: 6 - - equal: - path: spec.template.spec.containers[0].env[4].name - value: PG_PASS - documentIndex: 6 - - equal: - path: spec.template.spec.containers[0].env[4].valueFrom.secretKeyRef.name - value: pg-pass - documentIndex: 6 - - equal: - path: spec.template.spec.containers[0].env[4].valueFrom.secretKeyRef.key - value: postgresql-password - documentIndex: 6 - - equal: - path: spec.template.spec.containers[0].env[5].name - value: PG_PORT - documentIndex: 6 - - equal: - path: spec.template.spec.containers[0].env[5].value - value: "5432" - documentIndex: 6 - - equal: - path: spec.template.spec.containers[0].env[6].name - value: PG_HOST - documentIndex: 6 - - equal: - path: spec.template.spec.containers[0].env[6].value - value: mds-postgresql.mds.svc.cluster.local - documentIndex: 6 - - equal: - path: spec.template.spec.containers[0].env[7].name - value: PG_HOST_READER - documentIndex: 6 - - equal: - path: spec.template.spec.containers[0].env[7].value - value: mds-postgresql.mds.svc.cluster.local - documentIndex: 6 - - equal: - path: spec.template.spec.containers[0].env[8].name - value: REDIS_HOST - documentIndex: 6 - - equal: - path: spec.template.spec.containers[0].env[8].value - value: mds-redis-master.mds.svc.cluster.local - documentIndex: 6 - - equal: - path: spec.template.spec.containers[0].env[9].name - value: REDIS_PORT - documentIndex: 6 - - equal: - path: spec.template.spec.containers[0].env[9].value - value: "6379" - documentIndex: 6 - # fixme: test non-existence - # - isNull: - # path: spec.template.spec.containers[0].env[10] - # documentIndex: 1 \ No newline at end of file diff --git a/helm/mds/tests/deployment-policy-author-resource-all_test.yaml b/helm/mds/tests/deployment-policy-author-resource-all_test.yaml deleted file mode 100644 index d9b0c3fb9..000000000 --- a/helm/mds/tests/deployment-policy-author-resource-all_test.yaml +++ /dev/null @@ -1,183 +0,0 @@ -suite: test deployment policy-author default -templates: - - deployment.yaml -tests: - - it: doc 6 - set: - resourcesLimitsCpu: 250m - resourcesLimitsMemory: 256Mi - resourcesRequestsCpu: 5m - resourcesRequestsMemory: 13Mi - release: - namespace: mds - asserts: - - isAPIVersion: - of: apps/v1 - documentIndex: 6 - - isKind: - of: Deployment - documentIndex: 6 - - equal: - path: metadata.name - value: mds-policy-author - documentIndex: 6 - - equal: - path: metadata.labels.app - value: mds-policy-author - documentIndex: 6 - - equal: - path: metadata.namespace - value: mds - documentIndex: 6 - - equal: - path: spec.replicas - value: 1 - documentIndex: 6 - - equal: - path: spec.selector.matchLabels.app - value: mds-policy-author - documentIndex: 6 - - equal: - path: spec.template.metadata.labels.app - value: mds-policy-author - documentIndex: 6 - - equal: - path: spec.template.spec.containers[0].name - value: mds-policy-author - documentIndex: 6 - - equal: - path: spec.template.spec.containers[0].image - value: mds-policy-author:latest - documentIndex: 6 - - equal: - path: spec.template.spec.containers[0].imagePullPolicy - value: IfNotPresent - documentIndex: 6 - - equal: - path: spec.template.spec.containers[0].resources.limits.cpu - value: 250m - documentIndex: 6 - - equal: - path: spec.template.spec.containers[0].resources.limits.memory - value: 256Mi - documentIndex: 6 - - equal: - path: spec.template.spec.containers[0].resources.requests.cpu - value: 5m - documentIndex: 6 - - equal: - path: spec.template.spec.containers[0].resources.requests.memory - value: 13Mi - documentIndex: 6 - - equal: - path: spec.template.spec.containers[0].ports[0].containerPort - value: 4007 - documentIndex: 6 - - equal: - path: spec.template.spec.containers[0].ports[0].name - value: user-port - documentIndex: 6 - - equal: - path: spec.template.spec.containers[0].ports[0].protocol - value: TCP - documentIndex: 6 - - equal: - path: spec.template.spec.containers[0].env[0].name - value: PATH_PREFIX - documentIndex: 6 - - equal: - path: spec.template.spec.containers[0].env[0].value - value: /policy-author - documentIndex: 6 - - equal: - path: spec.template.spec.containers[0].env[1].name - value: TIMEZONE - documentIndex: 6 - - equal: - path: spec.template.spec.containers[0].env[1].value - value: America/Los_Angeles - documentIndex: 6 - - equal: - path: spec.template.spec.containers[0].env[2].name - value: PG_USER - documentIndex: 6 - - equal: - path: spec.template.spec.containers[0].env[2].value - value: mdsadmin - documentIndex: 6 - - equal: - path: spec.template.spec.containers[0].env[3].name - value: PG_NAME - documentIndex: 6 - - equal: - path: spec.template.spec.containers[0].env[3].value - value: mds - documentIndex: 6 - - equal: - path: spec.template.spec.containers[0].env[4].name - value: PG_PASS - documentIndex: 6 - - equal: - path: spec.template.spec.containers[0].env[4].valueFrom.secretKeyRef.name - value: pg-pass - documentIndex: 6 - - equal: - path: spec.template.spec.containers[0].env[4].valueFrom.secretKeyRef.key - value: postgresql-password - documentIndex: 6 - - equal: - path: spec.template.spec.containers[0].env[5].name - value: PG_PORT - documentIndex: 6 - - equal: - path: spec.template.spec.containers[0].env[5].value - value: '5432' - documentIndex: 6 - - equal: - path: spec.template.spec.containers[0].env[6].name - value: PG_HOST - documentIndex: 6 - - equal: - path: spec.template.spec.containers[0].env[6].value - value: mds-postgresql.mds.svc.cluster.local - documentIndex: 6 - - equal: - path: spec.template.spec.containers[0].env[7].name - value: PG_HOST_READER - documentIndex: 6 - - equal: - path: spec.template.spec.containers[0].env[7].value - value: mds-postgresql.mds.svc.cluster.local - documentIndex: 6 - - equal: - path: spec.template.spec.containers[0].env[8].name - value: REDIS_HOST - documentIndex: 6 - - equal: - path: spec.template.spec.containers[0].env[8].value - value: mds-redis-master.mds.svc.cluster.local - documentIndex: 6 - - equal: - path: spec.template.spec.containers[0].env[9].name - value: REDIS_PORT - documentIndex: 6 - - equal: - path: spec.template.spec.containers[0].env[9].value - value: '6379' - documentIndex: 6 - - equal: - path: spec.template.spec.containers[0].env[10].name - value: CE_NAME - documentIndex: 6 - - equal: - path: spec.template.spec.containers[0].env[10].value - value: mds-policy-author - documentIndex: 6 - - equal: - path: spec.template.spec.containers[0].env[11].name - value: SINK - documentIndex: 6 - - equal: - path: spec.template.spec.containers[0].env[11].value - value: http://default-broker.mds.svc.cluster.local - documentIndex: 6 diff --git a/helm/mds/tests/deployment-policy-author-registry_test.yaml b/helm/mds/tests/deployment-policy-author_test.yaml similarity index 67% rename from helm/mds/tests/deployment-policy-author-registry_test.yaml rename to helm/mds/tests/deployment-policy-author_test.yaml index 87fbdb43b..c5835e2ad 100644 --- a/helm/mds/tests/deployment-policy-author-registry_test.yaml +++ b/helm/mds/tests/deployment-policy-author_test.yaml @@ -1,179 +1,181 @@ -suite: test deployment policy-author registry +suite: test deployment policy-author templates: - deployment.yaml tests: - - it: doc 6 - set: - registry: foo-registry + - it: doc 0 release: namespace: mds asserts: - isAPIVersion: of: apps/v1 - documentIndex: 6 + documentIndex: 11 - isKind: of: Deployment - documentIndex: 6 + documentIndex: 11 - equal: path: metadata.name value: mds-policy-author - documentIndex: 6 + documentIndex: 11 - equal: path: metadata.labels.app value: mds-policy-author - documentIndex: 6 + documentIndex: 11 - equal: path: metadata.namespace value: mds - documentIndex: 6 + documentIndex: 11 - equal: path: spec.replicas value: 1 - documentIndex: 6 + documentIndex: 11 - equal: path: spec.selector.matchLabels.app value: mds-policy-author - documentIndex: 6 + documentIndex: 11 - equal: path: spec.template.metadata.labels.app value: mds-policy-author - documentIndex: 6 + documentIndex: 11 - equal: path: spec.template.spec.containers[0].name value: mds-policy-author - documentIndex: 6 + documentIndex: 11 - equal: path: spec.template.spec.containers[0].image - value: foo-registry/mds-policy-author:latest - documentIndex: 6 - - isNull: - path: spec.template.spec.containers[0].imagePullPolicy - documentIndex: 6 + value: mds-policy-author:latest + documentIndex: 11 - equal: path: spec.template.spec.containers[0].resources.limits.cpu value: 500m - documentIndex: 6 + documentIndex: 11 - equal: path: spec.template.spec.containers[0].resources.limits.memory value: 512Mi - documentIndex: 6 + documentIndex: 11 - equal: path: spec.template.spec.containers[0].resources.requests.cpu value: 50m - documentIndex: 6 + documentIndex: 11 - equal: path: spec.template.spec.containers[0].resources.requests.memory value: 128Mi - documentIndex: 6 + documentIndex: 11 - equal: path: spec.template.spec.containers[0].ports[0].containerPort - value: 4007 - documentIndex: 6 + value: 4000 + documentIndex: 11 - equal: path: spec.template.spec.containers[0].ports[0].name value: user-port - documentIndex: 6 + documentIndex: 11 - equal: path: spec.template.spec.containers[0].ports[0].protocol value: TCP - documentIndex: 6 + documentIndex: 11 - equal: path: spec.template.spec.containers[0].env[0].name - value: PATH_PREFIX - documentIndex: 6 + value: PORT + documentIndex: 11 - equal: path: spec.template.spec.containers[0].env[0].value - value: /policy-author - documentIndex: 6 + value: "4000" + documentIndex: 11 - equal: path: spec.template.spec.containers[0].env[1].name - value: TIMEZONE - documentIndex: 6 + value: PATH_PREFIX + documentIndex: 11 - equal: path: spec.template.spec.containers[0].env[1].value - value: America/Los_Angeles - documentIndex: 6 + value: /policy-author + documentIndex: 11 - equal: path: spec.template.spec.containers[0].env[2].name - value: PG_USER - documentIndex: 6 + value: TIMEZONE + documentIndex: 11 - equal: path: spec.template.spec.containers[0].env[2].value - value: mdsadmin - documentIndex: 6 + value: America/Los_Angeles + documentIndex: 11 - equal: path: spec.template.spec.containers[0].env[3].name - value: PG_NAME - documentIndex: 6 + value: PG_USER + documentIndex: 11 - equal: path: spec.template.spec.containers[0].env[3].value - value: mds - documentIndex: 6 + value: mdsadmin + documentIndex: 11 - equal: path: spec.template.spec.containers[0].env[4].name - value: PG_PASS - documentIndex: 6 - - equal: - path: spec.template.spec.containers[0].env[4].valueFrom.secretKeyRef.name - value: pg-pass - documentIndex: 6 + value: PG_NAME + documentIndex: 11 - equal: - path: spec.template.spec.containers[0].env[4].valueFrom.secretKeyRef.key - value: postgresql-password - documentIndex: 6 + path: spec.template.spec.containers[0].env[4].value + value: mds + documentIndex: 11 - equal: path: spec.template.spec.containers[0].env[5].name - value: PG_PORT - documentIndex: 6 + value: PG_PASS + documentIndex: 11 + - equal: + path: spec.template.spec.containers[0].env[5].valueFrom.secretKeyRef.name + value: mds-secrets + documentIndex: 11 - equal: - path: spec.template.spec.containers[0].env[5].value - value: '5432' - documentIndex: 6 + path: spec.template.spec.containers[0].env[5].valueFrom.secretKeyRef.key + value: postgresql-password + documentIndex: 11 - equal: path: spec.template.spec.containers[0].env[6].name value: PG_HOST - documentIndex: 6 + documentIndex: 11 - equal: path: spec.template.spec.containers[0].env[6].value value: mds-postgresql.mds.svc.cluster.local - documentIndex: 6 + documentIndex: 11 - equal: path: spec.template.spec.containers[0].env[7].name - value: PG_HOST_READER - documentIndex: 6 + value: PG_PORT + documentIndex: 11 - equal: path: spec.template.spec.containers[0].env[7].value - value: mds-postgresql.mds.svc.cluster.local - documentIndex: 6 + value: "5432" + documentIndex: 11 - equal: path: spec.template.spec.containers[0].env[8].name value: REDIS_HOST - documentIndex: 6 + documentIndex: 11 - equal: path: spec.template.spec.containers[0].env[8].value value: mds-redis-master.mds.svc.cluster.local - documentIndex: 6 + documentIndex: 11 - equal: path: spec.template.spec.containers[0].env[9].name value: REDIS_PORT - documentIndex: 6 + documentIndex: 11 - equal: path: spec.template.spec.containers[0].env[9].value - value: '6379' - documentIndex: 6 + value: "6379" + documentIndex: 11 - equal: path: spec.template.spec.containers[0].env[10].name - value: CE_NAME - documentIndex: 6 + value: NATS + documentIndex: 11 - equal: path: spec.template.spec.containers[0].env[10].value - value: mds-policy-author - documentIndex: 6 + value: nats.nats.svc.cluster.local + documentIndex: 11 - equal: path: spec.template.spec.containers[0].env[11].name - value: SINK - documentIndex: 6 + value: STAN_CLUSTER + documentIndex: 11 - equal: path: spec.template.spec.containers[0].env[11].value - value: http://default-broker.mds.svc.cluster.local - documentIndex: 6 + value: nats-streaming + documentIndex: 11 + - equal: + path: spec.template.spec.containers[0].env[12].name + value: TENANT_ID + documentIndex: 11 + - isEmpty: + path: spec.template.spec.containers[0].env[12].value + documentIndex: 11 diff --git a/helm/mds/tests/deployment-policy-env_test.yaml b/helm/mds/tests/deployment-policy-env_test.yaml deleted file mode 100644 index e123f893f..000000000 --- a/helm/mds/tests/deployment-policy-env_test.yaml +++ /dev/null @@ -1,202 +0,0 @@ -suite: test deployment policy default -templates: - - deployment.yaml -tests: - - it: doc 5 - release: - namespace: mds - set: - apis: - mds-policy: - env: - - name: foo - value: bar - - name: bop - value: bing - asserts: - - isAPIVersion: - of: apps/v1 - documentIndex: 5 - - isKind: - of: Deployment - documentIndex: 5 - - equal: - path: metadata.name - value: mds-policy - documentIndex: 5 - - equal: - path: metadata.labels.app - value: mds-policy - documentIndex: 5 - - equal: - path: metadata.namespace - value: mds - documentIndex: 5 - - equal: - path: spec.replicas - value: 1 - documentIndex: 5 - - equal: - path: spec.selector.matchLabels.app - value: mds-policy - documentIndex: 5 - - equal: - path: spec.template.metadata.labels.app - value: mds-policy - documentIndex: 5 - - equal: - path: spec.template.spec.containers[0].name - value: mds-policy - documentIndex: 5 - - equal: - path: spec.template.spec.containers[0].image - value: mds-policy:latest - documentIndex: 5 - - equal: - path: spec.template.spec.containers[0].imagePullPolicy - value: IfNotPresent - documentIndex: 5 - - equal: - path: spec.template.spec.containers[0].resources.limits.cpu - value: 500m - documentIndex: 5 - - equal: - path: spec.template.spec.containers[0].resources.limits.memory - value: 512Mi - documentIndex: 5 - - equal: - path: spec.template.spec.containers[0].resources.requests.cpu - value: 50m - documentIndex: 5 - - equal: - path: spec.template.spec.containers[0].resources.requests.memory - value: 128Mi - documentIndex: 5 - - equal: - path: spec.template.spec.containers[0].ports[0].containerPort - value: 4003 - documentIndex: 5 - - equal: - path: spec.template.spec.containers[0].ports[0].name - value: user-port - documentIndex: 5 - - equal: - path: spec.template.spec.containers[0].ports[0].protocol - value: TCP - documentIndex: 5 - - equal: - path: spec.template.spec.containers[0].env[0].name - value: PATH_PREFIX - documentIndex: 5 - - equal: - path: spec.template.spec.containers[0].env[0].value - value: /policy - documentIndex: 5 - - equal: - path: spec.template.spec.containers[0].env[1].name - value: TIMEZONE - documentIndex: 5 - - equal: - path: spec.template.spec.containers[0].env[1].value - value: America/Los_Angeles - documentIndex: 5 - - equal: - path: spec.template.spec.containers[0].env[2].name - value: PG_USER - documentIndex: 5 - - equal: - path: spec.template.spec.containers[0].env[2].value - value: mdsadmin - documentIndex: 5 - - equal: - path: spec.template.spec.containers[0].env[3].name - value: PG_NAME - documentIndex: 5 - - equal: - path: spec.template.spec.containers[0].env[3].value - value: mds - documentIndex: 5 - - equal: - path: spec.template.spec.containers[0].env[4].name - value: PG_PASS - documentIndex: 5 - - equal: - path: spec.template.spec.containers[0].env[4].valueFrom.secretKeyRef.name - value: pg-pass - documentIndex: 5 - - equal: - path: spec.template.spec.containers[0].env[4].valueFrom.secretKeyRef.key - value: postgresql-password - documentIndex: 5 - - equal: - path: spec.template.spec.containers[0].env[5].name - value: PG_PORT - documentIndex: 5 - - equal: - path: spec.template.spec.containers[0].env[5].value - value: '5432' - documentIndex: 5 - - equal: - path: spec.template.spec.containers[0].env[6].name - value: PG_HOST - documentIndex: 5 - - equal: - path: spec.template.spec.containers[0].env[6].value - value: mds-postgresql.mds.svc.cluster.local - documentIndex: 5 - - equal: - path: spec.template.spec.containers[0].env[7].name - value: PG_HOST_READER - documentIndex: 5 - - equal: - path: spec.template.spec.containers[0].env[7].value - value: mds-postgresql.mds.svc.cluster.local - documentIndex: 5 - - equal: - path: spec.template.spec.containers[0].env[8].name - value: REDIS_HOST - documentIndex: 5 - - equal: - path: spec.template.spec.containers[0].env[8].value - value: mds-redis-master.mds.svc.cluster.local - documentIndex: 5 - - equal: - path: spec.template.spec.containers[0].env[9].name - value: REDIS_PORT - documentIndex: 5 - - equal: - path: spec.template.spec.containers[0].env[9].value - value: '6379' - documentIndex: 5 - - equal: - path: spec.template.spec.containers[0].env[10].name - value: CE_NAME - documentIndex: 5 - - equal: - path: spec.template.spec.containers[0].env[10].value - value: mds-policy - documentIndex: 5 - - equal: - path: spec.template.spec.containers[0].env[11].name - value: SINK - documentIndex: 5 - - equal: - path: spec.template.spec.containers[0].env[11].value - value: http://default-broker.mds.svc.cluster.local - 1documentIndex: 5 - - equal: - path: spec.template.spec.containers[0].env[12].name - value: foo - documentIndex: 5 - - equal: - path: spec.template.spec.containers[0].env[12].value - value: bar - documentIndex: 5 - - equal: - path: spec.template.spec.containers[0].env[13].name - value: bop - documentIndex: 5 - - equal: - path: spec.template.spec.containers[0].env[13].value - value: bing - documentIndex: 5 \ No newline at end of file diff --git a/helm/mds/tests/deployment-policy-no-cache_test.yaml b/helm/mds/tests/deployment-policy-no-cache_test.yaml deleted file mode 100644 index 3804cfbd2..000000000 --- a/helm/mds/tests/deployment-policy-no-cache_test.yaml +++ /dev/null @@ -1,164 +0,0 @@ -suite: test deployment policy no-cache -templates: - - deployment.yaml -tests: - - it: doc 5 - release: - namespace: mds - set: - apis.mds-policy.useCache: false - asserts: - - isAPIVersion: - of: apps/v1 - documentIndex: 5 - - isKind: - of: Deployment - documentIndex: 5 - - equal: - path: metadata.name - value: mds-policy - documentIndex: 5 - - equal: - path: metadata.labels.app - value: mds-policy - documentIndex: 5 - - equal: - path: metadata.namespace - value: mds - documentIndex: 5 - - equal: - path: spec.replicas - value: 1 - documentIndex: 5 - - equal: - path: spec.selector.matchLabels.app - value: mds-policy - documentIndex: 5 - - equal: - path: spec.template.metadata.labels.app - value: mds-policy - documentIndex: 5 - - equal: - path: spec.template.spec.containers[0].name - value: mds-policy - documentIndex: 5 - - equal: - path: spec.template.spec.containers[0].image - value: mds-policy:latest - documentIndex: 5 - - equal: - path: spec.template.spec.containers[0].imagePullPolicy - value: IfNotPresent - documentIndex: 5 - - equal: - path: spec.template.spec.containers[0].resources.limits.cpu - value: 500m - documentIndex: 5 - - equal: - path: spec.template.spec.containers[0].resources.limits.memory - value: 512Mi - documentIndex: 5 - - equal: - path: spec.template.spec.containers[0].resources.requests.cpu - value: 50m - documentIndex: 5 - - equal: - path: spec.template.spec.containers[0].resources.requests.memory - value: 128Mi - documentIndex: 5 - - equal: - path: spec.template.spec.containers[0].ports[0].containerPort - value: 4003 - documentIndex: 5 - - equal: - path: spec.template.spec.containers[0].ports[0].name - value: user-port - documentIndex: 5 - - equal: - path: spec.template.spec.containers[0].ports[0].protocol - value: TCP - documentIndex: 5 - - equal: - path: spec.template.spec.containers[0].env[0].name - value: PATH_PREFIX - documentIndex: 5 - - equal: - path: spec.template.spec.containers[0].env[0].value - value: /policy - documentIndex: 5 - - equal: - path: spec.template.spec.containers[0].env[1].name - value: TIMEZONE - documentIndex: 5 - - equal: - path: spec.template.spec.containers[0].env[1].value - value: America/Los_Angeles - documentIndex: 5 - - equal: - path: spec.template.spec.containers[0].env[2].name - value: PG_USER - documentIndex: 5 - - equal: - path: spec.template.spec.containers[0].env[2].value - value: mdsadmin - documentIndex: 5 - - equal: - path: spec.template.spec.containers[0].env[3].name - value: PG_NAME - documentIndex: 5 - - equal: - path: spec.template.spec.containers[0].env[3].value - value: mds - documentIndex: 5 - - equal: - path: spec.template.spec.containers[0].env[4].name - value: PG_PASS - documentIndex: 5 - - equal: - path: spec.template.spec.containers[0].env[4].valueFrom.secretKeyRef.name - value: pg-pass - documentIndex: 5 - - equal: - path: spec.template.spec.containers[0].env[4].valueFrom.secretKeyRef.key - value: postgresql-password - documentIndex: 5 - - equal: - path: spec.template.spec.containers[0].env[5].name - value: PG_PORT - documentIndex: 5 - - equal: - path: spec.template.spec.containers[0].env[5].value - value: "5432" - documentIndex: 5 - - equal: - path: spec.template.spec.containers[0].env[6].name - value: PG_HOST - documentIndex: 5 - - equal: - path: spec.template.spec.containers[0].env[6].value - value: mds-postgresql.mds.svc.cluster.local - documentIndex: 5 - - equal: - path: spec.template.spec.containers[0].env[7].name - value: PG_HOST_READER - documentIndex: 5 - - equal: - path: spec.template.spec.containers[0].env[7].value - value: mds-postgresql.mds.svc.cluster.local - documentIndex: 5 - - equal: - path: spec.template.spec.containers[0].env[8].name - value: CE_NAME - documentIndex: 5 - - equal: - path: spec.template.spec.containers[0].env[8].value - value: mds-policy - documentIndex: 5 - - equal: - path: spec.template.spec.containers[0].env[9].name - value: SINK - documentIndex: 5 - - equal: - path: spec.template.spec.containers[0].env[9].value - value: http://default-broker.mds.svc.cluster.local - documentIndex: 5 diff --git a/helm/mds/tests/deployment-policy-no-db_test.yaml b/helm/mds/tests/deployment-policy-no-db_test.yaml deleted file mode 100644 index e37091074..000000000 --- a/helm/mds/tests/deployment-policy-no-db_test.yaml +++ /dev/null @@ -1,128 +0,0 @@ -suite: test deployment policy no-db -templates: - - deployment.yaml -tests: - - it: doc 5 - release: - namespace: mds - set: - apis.mds-policy.useDB: false - asserts: - - isAPIVersion: - of: apps/v1 - documentIndex: 5 - - isKind: - of: Deployment - documentIndex: 5 - - equal: - path: metadata.name - value: mds-policy - documentIndex: 5 - - equal: - path: metadata.labels.app - value: mds-policy - documentIndex: 5 - - equal: - path: metadata.namespace - value: mds - documentIndex: 5 - - equal: - path: spec.replicas - value: 1 - documentIndex: 5 - - equal: - path: spec.selector.matchLabels.app - value: mds-policy - documentIndex: 5 - - equal: - path: spec.template.metadata.labels.app - value: mds-policy - documentIndex: 5 - - equal: - path: spec.template.spec.containers[0].name - value: mds-policy - documentIndex: 5 - - equal: - path: spec.template.spec.containers[0].image - value: mds-policy:latest - documentIndex: 5 - - equal: - path: spec.template.spec.containers[0].imagePullPolicy - value: IfNotPresent - documentIndex: 5 - - equal: - path: spec.template.spec.containers[0].resources.limits.cpu - value: 500m - documentIndex: 5 - - equal: - path: spec.template.spec.containers[0].resources.limits.memory - value: 512Mi - documentIndex: 5 - - equal: - path: spec.template.spec.containers[0].resources.requests.cpu - value: 50m - documentIndex: 5 - - equal: - path: spec.template.spec.containers[0].resources.requests.memory - value: 128Mi - documentIndex: 5 - - equal: - path: spec.template.spec.containers[0].ports[0].containerPort - value: 4003 - documentIndex: 5 - - equal: - path: spec.template.spec.containers[0].ports[0].name - value: user-port - documentIndex: 5 - - equal: - path: spec.template.spec.containers[0].ports[0].protocol - value: TCP - documentIndex: 5 - - equal: - path: spec.template.spec.containers[0].env[0].name - value: PATH_PREFIX - documentIndex: 5 - - equal: - path: spec.template.spec.containers[0].env[0].value - value: /policy - documentIndex: 5 - - equal: - path: spec.template.spec.containers[0].env[1].name - value: TIMEZONE - documentIndex: 5 - - equal: - path: spec.template.spec.containers[0].env[1].value - value: America/Los_Angeles - documentIndex: 5 - - equal: - path: spec.template.spec.containers[0].env[2].name - value: REDIS_HOST - documentIndex: 5 - - equal: - path: spec.template.spec.containers[0].env[2].value - value: mds-redis-master.mds.svc.cluster.local - documentIndex: 5 - - equal: - path: spec.template.spec.containers[0].env[3].name - value: REDIS_PORT - documentIndex: 5 - - equal: - path: spec.template.spec.containers[0].env[3].value - value: '6379' - documentIndex: 5 - - equal: - path: spec.template.spec.containers[0].env[4].name - value: CE_NAME - documentIndex: 5 - - equal: - path: spec.template.spec.containers[0].env[4].value - value: mds-policy - documentIndex: 5 - - equal: - path: spec.template.spec.containers[0].env[5].name - value: SINK - documentIndex: 5 - - equal: - path: spec.template.spec.containers[0].env[5].value - value: http://default-broker.mds.svc.cluster.local - documentIndex: 5 diff --git a/helm/mds/tests/deployment-policy-no-events_test.yaml b/helm/mds/tests/deployment-policy-no-events_test.yaml deleted file mode 100644 index c9455b0d0..000000000 --- a/helm/mds/tests/deployment-policy-no-events_test.yaml +++ /dev/null @@ -1,168 +0,0 @@ -suite: test deployment policy no-events -templates: - - deployment.yaml -tests: - - it: doc 5 - release: - namespace: mds - set: - apis.mds-policy.useEvents: false - asserts: - - isAPIVersion: - of: apps/v1 - documentIndex: 5 - - isKind: - of: Deployment - documentIndex: 5 - - equal: - path: metadata.name - value: mds-policy - documentIndex: 5 - - equal: - path: metadata.labels.app - value: mds-policy - documentIndex: 5 - - equal: - path: metadata.namespace - value: mds - documentIndex: 5 - - equal: - path: spec.replicas - value: 1 - documentIndex: 5 - - equal: - path: spec.selector.matchLabels.app - value: mds-policy - documentIndex: 5 - - equal: - path: spec.template.metadata.labels.app - value: mds-policy - documentIndex: 5 - - equal: - path: spec.template.spec.containers[0].name - value: mds-policy - documentIndex: 5 - - equal: - path: spec.template.spec.containers[0].image - value: mds-policy:latest - documentIndex: 5 - - equal: - path: spec.template.spec.containers[0].imagePullPolicy - value: IfNotPresent - documentIndex: 5 - - equal: - path: spec.template.spec.containers[0].resources.limits.cpu - value: 500m - documentIndex: 5 - - equal: - path: spec.template.spec.containers[0].resources.limits.memory - value: 512Mi - documentIndex: 5 - - equal: - path: spec.template.spec.containers[0].resources.requests.cpu - value: 50m - documentIndex: 5 - - equal: - path: spec.template.spec.containers[0].resources.requests.memory - value: 128Mi - documentIndex: 5 - - equal: - path: spec.template.spec.containers[0].ports[0].containerPort - value: 4003 - documentIndex: 5 - - equal: - path: spec.template.spec.containers[0].ports[0].name - value: user-port - documentIndex: 5 - - equal: - path: spec.template.spec.containers[0].ports[0].protocol - value: TCP - documentIndex: 5 - - equal: - path: spec.template.spec.containers[0].env[0].name - value: PATH_PREFIX - documentIndex: 5 - - equal: - path: spec.template.spec.containers[0].env[0].value - value: /policy - documentIndex: 5 - - equal: - path: spec.template.spec.containers[0].env[1].name - value: TIMEZONE - documentIndex: 5 - - equal: - path: spec.template.spec.containers[0].env[1].value - value: America/Los_Angeles - documentIndex: 5 - - equal: - path: spec.template.spec.containers[0].env[2].name - value: PG_USER - documentIndex: 5 - - equal: - path: spec.template.spec.containers[0].env[2].value - value: mdsadmin - documentIndex: 5 - - equal: - path: spec.template.spec.containers[0].env[3].name - value: PG_NAME - documentIndex: 5 - - equal: - path: spec.template.spec.containers[0].env[3].value - value: mds - documentIndex: 5 - - equal: - path: spec.template.spec.containers[0].env[4].name - value: PG_PASS - documentIndex: 5 - - equal: - path: spec.template.spec.containers[0].env[4].valueFrom.secretKeyRef.name - value: pg-pass - documentIndex: 5 - - equal: - path: spec.template.spec.containers[0].env[4].valueFrom.secretKeyRef.key - value: postgresql-password - documentIndex: 5 - - equal: - path: spec.template.spec.containers[0].env[5].name - value: PG_PORT - documentIndex: 5 - - equal: - path: spec.template.spec.containers[0].env[5].value - value: "5432" - documentIndex: 5 - - equal: - path: spec.template.spec.containers[0].env[6].name - value: PG_HOST - documentIndex: 5 - - equal: - path: spec.template.spec.containers[0].env[6].value - value: mds-postgresql.mds.svc.cluster.local - documentIndex: 5 - - equal: - path: spec.template.spec.containers[0].env[7].name - value: PG_HOST_READER - documentIndex: 5 - - equal: - path: spec.template.spec.containers[0].env[7].value - value: mds-postgresql.mds.svc.cluster.local - documentIndex: 5 - - equal: - path: spec.template.spec.containers[0].env[8].name - value: REDIS_HOST - documentIndex: 5 - - equal: - path: spec.template.spec.containers[0].env[8].value - value: mds-redis-master.mds.svc.cluster.local - documentIndex: 5 - - equal: - path: spec.template.spec.containers[0].env[9].name - value: REDIS_PORT - documentIndex: 5 - - equal: - path: spec.template.spec.containers[0].env[9].value - value: "6379" - documentIndex: 5 - # fixme: test non-existence - # - isNull: - # path: spec.template.spec.containers[0].env[10] - # documentIndex: 1 \ No newline at end of file diff --git a/helm/mds/tests/deployment-policy-registry_test.yaml b/helm/mds/tests/deployment-policy-registry_test.yaml deleted file mode 100644 index 919152596..000000000 --- a/helm/mds/tests/deployment-policy-registry_test.yaml +++ /dev/null @@ -1,179 +0,0 @@ -suite: test deployment policy registry -templates: - - deployment.yaml -tests: - - it: doc 7 - set: - registry: foo-registry - release: - namespace: mds - asserts: - - isAPIVersion: - of: apps/v1 - documentIndex: 5 - - isKind: - of: Deployment - documentIndex: 5 - - equal: - path: metadata.name - value: mds-policy - documentIndex: 5 - - equal: - path: metadata.labels.app - value: mds-policy - documentIndex: 5 - - equal: - path: metadata.namespace - value: mds - documentIndex: 5 - - equal: - path: spec.replicas - value: 1 - documentIndex: 5 - - equal: - path: spec.selector.matchLabels.app - value: mds-policy - documentIndex: 5 - - equal: - path: spec.template.metadata.labels.app - value: mds-policy - documentIndex: 5 - - equal: - path: spec.template.spec.containers[0].name - value: mds-policy - documentIndex: 5 - - equal: - path: spec.template.spec.containers[0].image - value: foo-registry/mds-policy:latest - documentIndex: 5 - - isNull: - path: spec.template.spec.containers[0].imagePullPolicy - documentIndex: 5 - - equal: - path: spec.template.spec.containers[0].resources.limits.cpu - value: 500m - documentIndex: 5 - - equal: - path: spec.template.spec.containers[0].resources.limits.memory - value: 512Mi - documentIndex: 5 - - equal: - path: spec.template.spec.containers[0].resources.requests.cpu - value: 50m - documentIndex: 5 - - equal: - path: spec.template.spec.containers[0].resources.requests.memory - value: 128Mi - documentIndex: 5 - - equal: - path: spec.template.spec.containers[0].ports[0].containerPort - value: 4003 - documentIndex: 5 - - equal: - path: spec.template.spec.containers[0].ports[0].name - value: user-port - documentIndex: 5 - - equal: - path: spec.template.spec.containers[0].ports[0].protocol - value: TCP - documentIndex: 5 - - equal: - path: spec.template.spec.containers[0].env[0].name - value: PATH_PREFIX - documentIndex: 5 - - equal: - path: spec.template.spec.containers[0].env[0].value - value: /policy - documentIndex: 5 - - equal: - path: spec.template.spec.containers[0].env[1].name - value: TIMEZONE - documentIndex: 5 - - equal: - path: spec.template.spec.containers[0].env[1].value - value: America/Los_Angeles - documentIndex: 5 - - equal: - path: spec.template.spec.containers[0].env[2].name - value: PG_USER - documentIndex: 5 - - equal: - path: spec.template.spec.containers[0].env[2].value - value: mdsadmin - documentIndex: 5 - - equal: - path: spec.template.spec.containers[0].env[3].name - value: PG_NAME - documentIndex: 5 - - equal: - path: spec.template.spec.containers[0].env[3].value - value: mds - documentIndex: 5 - - equal: - path: spec.template.spec.containers[0].env[4].name - value: PG_PASS - documentIndex: 5 - - equal: - path: spec.template.spec.containers[0].env[4].valueFrom.secretKeyRef.name - value: pg-pass - documentIndex: 5 - - equal: - path: spec.template.spec.containers[0].env[4].valueFrom.secretKeyRef.key - value: postgresql-password - documentIndex: 5 - - equal: - path: spec.template.spec.containers[0].env[5].name - value: PG_PORT - documentIndex: 5 - - equal: - path: spec.template.spec.containers[0].env[5].value - value: "5432" - documentIndex: 5 - - equal: - path: spec.template.spec.containers[0].env[6].name - value: PG_HOST - documentIndex: 5 - - equal: - path: spec.template.spec.containers[0].env[6].value - value: mds-postgresql.mds.svc.cluster.local - documentIndex: 5 - - equal: - path: spec.template.spec.containers[0].env[7].name - value: PG_HOST_READER - documentIndex: 5 - - equal: - path: spec.template.spec.containers[0].env[7].value - value: mds-postgresql.mds.svc.cluster.local - documentIndex: 5 - - equal: - path: spec.template.spec.containers[0].env[8].name - value: REDIS_HOST - documentIndex: 5 - - equal: - path: spec.template.spec.containers[0].env[8].value - value: mds-redis-master.mds.svc.cluster.local - documentIndex: 5 - - equal: - path: spec.template.spec.containers[0].env[9].name - value: REDIS_PORT - documentIndex: 5 - - equal: - path: spec.template.spec.containers[0].env[9].value - value: "6379" - documentIndex: 5 - - equal: - path: spec.template.spec.containers[0].env[10].name - value: CE_NAME - documentIndex: 5 - - equal: - path: spec.template.spec.containers[0].env[10].value - value: mds-policy - documentIndex: 5 - - equal: - path: spec.template.spec.containers[0].env[11].name - value: SINK - documentIndex: 5 - - equal: - path: spec.template.spec.containers[0].env[11].value - value: http://default-broker.mds.svc.cluster.local - 1documentIndex: 5 diff --git a/helm/mds/tests/deployment-policy-resource-all_test.yaml b/helm/mds/tests/deployment-policy-resource-all_test.yaml deleted file mode 100644 index acf575105..000000000 --- a/helm/mds/tests/deployment-policy-resource-all_test.yaml +++ /dev/null @@ -1,183 +0,0 @@ -suite: test deployment policy default -templates: - - deployment.yaml -tests: - - it: doc 5 - set: - resourcesLimitsCpu: 250m - resourcesLimitsMemory: 256Mi - resourcesRequestsCpu: 5m - resourcesRequestsMemory: 13Mi - release: - namespace: mds - asserts: - - isAPIVersion: - of: apps/v1 - documentIndex: 5 - - isKind: - of: Deployment - documentIndex: 5 - - equal: - path: metadata.name - value: mds-policy - documentIndex: 5 - - equal: - path: metadata.labels.app - value: mds-policy - documentIndex: 5 - - equal: - path: metadata.namespace - value: mds - documentIndex: 5 - - equal: - path: spec.replicas - value: 1 - documentIndex: 5 - - equal: - path: spec.selector.matchLabels.app - value: mds-policy - documentIndex: 5 - - equal: - path: spec.template.metadata.labels.app - value: mds-policy - documentIndex: 5 - - equal: - path: spec.template.spec.containers[0].name - value: mds-policy - documentIndex: 5 - - equal: - path: spec.template.spec.containers[0].image - value: mds-policy:latest - documentIndex: 5 - - equal: - path: spec.template.spec.containers[0].imagePullPolicy - value: IfNotPresent - documentIndex: 5 - - equal: - path: spec.template.spec.containers[0].resources.limits.cpu - value: 250m - documentIndex: 5 - - equal: - path: spec.template.spec.containers[0].resources.limits.memory - value: 256Mi - documentIndex: 5 - - equal: - path: spec.template.spec.containers[0].resources.requests.cpu - value: 5m - documentIndex: 5 - - equal: - path: spec.template.spec.containers[0].resources.requests.memory - value: 13Mi - documentIndex: 5 - - equal: - path: spec.template.spec.containers[0].ports[0].containerPort - value: 4003 - documentIndex: 5 - - equal: - path: spec.template.spec.containers[0].ports[0].name - value: user-port - documentIndex: 5 - - equal: - path: spec.template.spec.containers[0].ports[0].protocol - value: TCP - documentIndex: 5 - - equal: - path: spec.template.spec.containers[0].env[0].name - value: PATH_PREFIX - documentIndex: 5 - - equal: - path: spec.template.spec.containers[0].env[0].value - value: /policy - documentIndex: 5 - - equal: - path: spec.template.spec.containers[0].env[1].name - value: TIMEZONE - documentIndex: 5 - - equal: - path: spec.template.spec.containers[0].env[1].value - value: America/Los_Angeles - documentIndex: 5 - - equal: - path: spec.template.spec.containers[0].env[2].name - value: PG_USER - documentIndex: 5 - - equal: - path: spec.template.spec.containers[0].env[2].value - value: mdsadmin - documentIndex: 5 - - equal: - path: spec.template.spec.containers[0].env[3].name - value: PG_NAME - documentIndex: 5 - - equal: - path: spec.template.spec.containers[0].env[3].value - value: mds - documentIndex: 5 - - equal: - path: spec.template.spec.containers[0].env[4].name - value: PG_PASS - documentIndex: 5 - - equal: - path: spec.template.spec.containers[0].env[4].valueFrom.secretKeyRef.name - value: pg-pass - documentIndex: 5 - - equal: - path: spec.template.spec.containers[0].env[4].valueFrom.secretKeyRef.key - value: postgresql-password - documentIndex: 5 - - equal: - path: spec.template.spec.containers[0].env[5].name - value: PG_PORT - documentIndex: 5 - - equal: - path: spec.template.spec.containers[0].env[5].value - value: '5432' - documentIndex: 5 - - equal: - path: spec.template.spec.containers[0].env[6].name - value: PG_HOST - documentIndex: 5 - - equal: - path: spec.template.spec.containers[0].env[6].value - value: mds-postgresql.mds.svc.cluster.local - documentIndex: 5 - - equal: - path: spec.template.spec.containers[0].env[7].name - value: PG_HOST_READER - documentIndex: 5 - - equal: - path: spec.template.spec.containers[0].env[7].value - value: mds-postgresql.mds.svc.cluster.local - documentIndex: 5 - - equal: - path: spec.template.spec.containers[0].env[8].name - value: REDIS_HOST - documentIndex: 5 - - equal: - path: spec.template.spec.containers[0].env[8].value - value: mds-redis-master.mds.svc.cluster.local - documentIndex: 5 - - equal: - path: spec.template.spec.containers[0].env[9].name - value: REDIS_PORT - documentIndex: 5 - - equal: - path: spec.template.spec.containers[0].env[9].value - value: '6379' - documentIndex: 5 - - equal: - path: spec.template.spec.containers[0].env[10].name - value: CE_NAME - documentIndex: 5 - - equal: - path: spec.template.spec.containers[0].env[10].value - value: mds-policy - documentIndex: 5 - - equal: - path: spec.template.spec.containers[0].env[11].name - value: SINK - documentIndex: 5 - - equal: - path: spec.template.spec.containers[0].env[11].value - value: http://default-broker.mds.svc.cluster.local - 1documentIndex: 5 diff --git a/helm/mds/tests/deployment-policy-default_test.yaml b/helm/mds/tests/deployment-policy_test.yaml similarity index 67% rename from helm/mds/tests/deployment-policy-default_test.yaml rename to helm/mds/tests/deployment-policy_test.yaml index 8a6fbe91d..16ec3025e 100644 --- a/helm/mds/tests/deployment-policy-default_test.yaml +++ b/helm/mds/tests/deployment-policy_test.yaml @@ -1,178 +1,181 @@ -suite: test deployment policy default +suite: test deployment policy templates: - deployment.yaml tests: - - it: doc 5 + - it: doc 0 release: namespace: mds asserts: - isAPIVersion: of: apps/v1 - documentIndex: 5 + documentIndex: 10 - isKind: of: Deployment - documentIndex: 5 + documentIndex: 10 - equal: path: metadata.name value: mds-policy - documentIndex: 5 + documentIndex: 10 - equal: path: metadata.labels.app value: mds-policy - documentIndex: 5 + documentIndex: 10 - equal: path: metadata.namespace value: mds - documentIndex: 5 + documentIndex: 10 - equal: path: spec.replicas value: 1 - documentIndex: 5 + documentIndex: 10 - equal: path: spec.selector.matchLabels.app value: mds-policy - documentIndex: 5 + documentIndex: 10 - equal: path: spec.template.metadata.labels.app value: mds-policy - documentIndex: 5 + documentIndex: 10 - equal: path: spec.template.spec.containers[0].name value: mds-policy - documentIndex: 5 + documentIndex: 10 - equal: path: spec.template.spec.containers[0].image value: mds-policy:latest - documentIndex: 5 - - equal: - path: spec.template.spec.containers[0].imagePullPolicy - value: IfNotPresent - documentIndex: 5 + documentIndex: 10 - equal: path: spec.template.spec.containers[0].resources.limits.cpu value: 500m - documentIndex: 5 + documentIndex: 10 - equal: path: spec.template.spec.containers[0].resources.limits.memory value: 512Mi - documentIndex: 5 + documentIndex: 10 - equal: path: spec.template.spec.containers[0].resources.requests.cpu value: 50m - documentIndex: 5 + documentIndex: 10 - equal: path: spec.template.spec.containers[0].resources.requests.memory value: 128Mi - documentIndex: 5 + documentIndex: 10 - equal: path: spec.template.spec.containers[0].ports[0].containerPort - value: 4003 - documentIndex: 5 + value: 4000 + documentIndex: 10 - equal: path: spec.template.spec.containers[0].ports[0].name value: user-port - documentIndex: 5 + documentIndex: 10 - equal: path: spec.template.spec.containers[0].ports[0].protocol value: TCP - documentIndex: 5 + documentIndex: 10 - equal: path: spec.template.spec.containers[0].env[0].name - value: PATH_PREFIX - documentIndex: 5 + value: PORT + documentIndex: 10 - equal: path: spec.template.spec.containers[0].env[0].value - value: /policy - documentIndex: 5 + value: "4000" + documentIndex: 10 - equal: path: spec.template.spec.containers[0].env[1].name - value: TIMEZONE - documentIndex: 5 + value: PATH_PREFIX + documentIndex: 10 - equal: path: spec.template.spec.containers[0].env[1].value - value: America/Los_Angeles - documentIndex: 5 + value: /policy + documentIndex: 10 - equal: path: spec.template.spec.containers[0].env[2].name - value: PG_USER - documentIndex: 5 + value: TIMEZONE + documentIndex: 10 - equal: path: spec.template.spec.containers[0].env[2].value - value: mdsadmin - documentIndex: 5 + value: America/Los_Angeles + documentIndex: 10 - equal: path: spec.template.spec.containers[0].env[3].name - value: PG_NAME - documentIndex: 5 + value: PG_USER + documentIndex: 10 - equal: path: spec.template.spec.containers[0].env[3].value - value: mds - documentIndex: 5 + value: mdsadmin + documentIndex: 10 - equal: path: spec.template.spec.containers[0].env[4].name - value: PG_PASS - documentIndex: 5 - - equal: - path: spec.template.spec.containers[0].env[4].valueFrom.secretKeyRef.name - value: pg-pass - documentIndex: 5 + value: PG_NAME + documentIndex: 10 - equal: - path: spec.template.spec.containers[0].env[4].valueFrom.secretKeyRef.key - value: postgresql-password - documentIndex: 5 + path: spec.template.spec.containers[0].env[4].value + value: mds + documentIndex: 10 - equal: path: spec.template.spec.containers[0].env[5].name - value: PG_PORT - documentIndex: 5 + value: PG_PASS + documentIndex: 10 + - equal: + path: spec.template.spec.containers[0].env[5].valueFrom.secretKeyRef.name + value: mds-secrets + documentIndex: 10 - equal: - path: spec.template.spec.containers[0].env[5].value - value: '5432' - documentIndex: 5 + path: spec.template.spec.containers[0].env[5].valueFrom.secretKeyRef.key + value: postgresql-password + documentIndex: 10 - equal: path: spec.template.spec.containers[0].env[6].name value: PG_HOST - documentIndex: 5 + documentIndex: 10 - equal: path: spec.template.spec.containers[0].env[6].value value: mds-postgresql.mds.svc.cluster.local - documentIndex: 5 + documentIndex: 10 - equal: path: spec.template.spec.containers[0].env[7].name - value: PG_HOST_READER - documentIndex: 5 + value: PG_PORT + documentIndex: 10 - equal: path: spec.template.spec.containers[0].env[7].value - value: mds-postgresql.mds.svc.cluster.local - documentIndex: 5 + value: "5432" + documentIndex: 10 - equal: path: spec.template.spec.containers[0].env[8].name value: REDIS_HOST - documentIndex: 5 + documentIndex: 10 - equal: path: spec.template.spec.containers[0].env[8].value value: mds-redis-master.mds.svc.cluster.local - documentIndex: 5 + documentIndex: 10 - equal: path: spec.template.spec.containers[0].env[9].name value: REDIS_PORT - documentIndex: 5 + documentIndex: 10 - equal: path: spec.template.spec.containers[0].env[9].value - value: '6379' - documentIndex: 5 + value: "6379" + documentIndex: 10 - equal: path: spec.template.spec.containers[0].env[10].name - value: CE_NAME - documentIndex: 5 + value: NATS + documentIndex: 10 - equal: path: spec.template.spec.containers[0].env[10].value - value: mds-policy - documentIndex: 5 + value: nats.nats.svc.cluster.local + documentIndex: 10 - equal: path: spec.template.spec.containers[0].env[11].name - value: SINK - documentIndex: 5 + value: STAN_CLUSTER + documentIndex: 10 - equal: path: spec.template.spec.containers[0].env[11].value - value: http://default-broker.mds.svc.cluster.local - 1documentIndex: 5 + value: nats-streaming + documentIndex: 10 + - equal: + path: spec.template.spec.containers[0].env[12].name + value: TENANT_ID + documentIndex: 10 + - isEmpty: + path: spec.template.spec.containers[0].env[12].value + documentIndex: 10 diff --git a/helm/mds/tests/deployment-secret-override_test.yaml b/helm/mds/tests/deployment-secret-override_test.yaml new file mode 100644 index 000000000..ef198c317 --- /dev/null +++ b/helm/mds/tests/deployment-secret-override_test.yaml @@ -0,0 +1,38 @@ +suite: test deployment secret +templates: + - deployment.yaml +tests: + - it: all + release: + namespace: mds + set: + slack.channel: my-slack-channel + slack.token: my-slack-token + postgresql.password: my-postgresql-password + asserts: + - isAPIVersion: + of: v1 + documentIndex: 13 + - isKind: + of: Secret + documentIndex: 13 + - equal: + path: metadata.name + value: mds-secrets + documentIndex: 13 + - equal: + path: metadata.namespace + value: mds + documentIndex: 13 + - equal: + path: data.slack-token + value: bXktc2xhY2stdG9rZW4= + documentIndex: 13 + - equal: + path: data.postgresql-password + value: bXktcG9zdGdyZXNxbC1wYXNzd29yZA== + documentIndex: 13 + - equal: + path: type + value: Opaque + documentIndex: 13 \ No newline at end of file diff --git a/helm/mds/tests/deployment-secret_test.yaml b/helm/mds/tests/deployment-secret_test.yaml index 04ba7886d..79e1c4463 100644 --- a/helm/mds/tests/deployment-secret_test.yaml +++ b/helm/mds/tests/deployment-secret_test.yaml @@ -1,32 +1,29 @@ -suite: test deployment all default +suite: test deployment secret templates: - deployment.yaml tests: - it: all release: namespace: mds - set: - pgPass: foo asserts: - isAPIVersion: of: v1 - documentIndex: 8 + documentIndex: 13 - isKind: of: Secret - documentIndex: 8 + documentIndex: 13 - equal: path: metadata.name - value: pg-pass - documentIndex: 8 + value: mds-secrets + documentIndex: 13 - equal: path: metadata.namespace value: mds - documentIndex: 8 + documentIndex: 13 + - isEmpty: + path: data + documentIndex: 13 - equal: path: type value: Opaque - documentIndex: 8 - - equal: - path: data.postgresql-password - value: Zm9v - documentIndex: 8 + documentIndex: 13 \ No newline at end of file diff --git a/helm/mds/tests/deployment-web-sockets_test.yaml b/helm/mds/tests/deployment-web-sockets_test.yaml new file mode 100644 index 000000000..8e8dee417 --- /dev/null +++ b/helm/mds/tests/deployment-web-sockets_test.yaml @@ -0,0 +1,181 @@ +suite: test deployment web-sockets +templates: + - deployment.yaml +tests: + - it: doc 0 + release: + namespace: mds + asserts: + - isAPIVersion: + of: apps/v1 + documentIndex: 12 + - isKind: + of: Deployment + documentIndex: 12 + - equal: + path: metadata.name + value: mds-web-sockets + documentIndex: 12 + - equal: + path: metadata.labels.app + value: mds-web-sockets + documentIndex: 12 + - equal: + path: metadata.namespace + value: mds + documentIndex: 12 + - equal: + path: spec.replicas + value: 1 + documentIndex: 12 + - equal: + path: spec.selector.matchLabels.app + value: mds-web-sockets + documentIndex: 12 + - equal: + path: spec.template.metadata.labels.app + value: mds-web-sockets + documentIndex: 12 + - equal: + path: spec.template.spec.containers[0].name + value: mds-web-sockets + documentIndex: 12 + - equal: + path: spec.template.spec.containers[0].image + value: mds-web-sockets:latest + documentIndex: 12 + - equal: + path: spec.template.spec.containers[0].resources.limits.cpu + value: 500m + documentIndex: 12 + - equal: + path: spec.template.spec.containers[0].resources.limits.memory + value: 512Mi + documentIndex: 12 + - equal: + path: spec.template.spec.containers[0].resources.requests.cpu + value: 50m + documentIndex: 12 + - equal: + path: spec.template.spec.containers[0].resources.requests.memory + value: 128Mi + documentIndex: 12 + - equal: + path: spec.template.spec.containers[0].ports[0].containerPort + value: 4000 + documentIndex: 12 + - equal: + path: spec.template.spec.containers[0].ports[0].name + value: user-port + documentIndex: 12 + - equal: + path: spec.template.spec.containers[0].ports[0].protocol + value: TCP + documentIndex: 12 + - equal: + path: spec.template.spec.containers[0].env[0].name + value: PORT + documentIndex: 12 + - equal: + path: spec.template.spec.containers[0].env[0].value + value: "4000" + documentIndex: 12 + - equal: + path: spec.template.spec.containers[0].env[1].name + value: PATH_PREFIX + documentIndex: 12 + - equal: + path: spec.template.spec.containers[0].env[1].value + value: /web-sockets + documentIndex: 12 + - equal: + path: spec.template.spec.containers[0].env[2].name + value: TIMEZONE + documentIndex: 12 + - equal: + path: spec.template.spec.containers[0].env[2].value + value: America/Los_Angeles + documentIndex: 12 + - equal: + path: spec.template.spec.containers[0].env[3].name + value: PG_USER + documentIndex: 12 + - equal: + path: spec.template.spec.containers[0].env[3].value + value: mdsadmin + documentIndex: 12 + - equal: + path: spec.template.spec.containers[0].env[4].name + value: PG_NAME + documentIndex: 12 + - equal: + path: spec.template.spec.containers[0].env[4].value + value: mds + documentIndex: 12 + - equal: + path: spec.template.spec.containers[0].env[5].name + value: PG_PASS + documentIndex: 12 + - equal: + path: spec.template.spec.containers[0].env[5].valueFrom.secretKeyRef.name + value: mds-secrets + documentIndex: 12 + - equal: + path: spec.template.spec.containers[0].env[5].valueFrom.secretKeyRef.key + value: postgresql-password + documentIndex: 12 + - equal: + path: spec.template.spec.containers[0].env[6].name + value: PG_HOST + documentIndex: 12 + - equal: + path: spec.template.spec.containers[0].env[6].value + value: mds-postgresql.mds.svc.cluster.local + documentIndex: 12 + - equal: + path: spec.template.spec.containers[0].env[7].name + value: PG_PORT + documentIndex: 12 + - equal: + path: spec.template.spec.containers[0].env[7].value + value: "5432" + documentIndex: 12 + - equal: + path: spec.template.spec.containers[0].env[8].name + value: REDIS_HOST + documentIndex: 12 + - equal: + path: spec.template.spec.containers[0].env[8].value + value: mds-redis-master.mds.svc.cluster.local + documentIndex: 12 + - equal: + path: spec.template.spec.containers[0].env[9].name + value: REDIS_PORT + documentIndex: 12 + - equal: + path: spec.template.spec.containers[0].env[9].value + value: "6379" + documentIndex: 12 + - equal: + path: spec.template.spec.containers[0].env[10].name + value: NATS + documentIndex: 12 + - equal: + path: spec.template.spec.containers[0].env[10].value + value: nats.nats.svc.cluster.local + documentIndex: 12 + - equal: + path: spec.template.spec.containers[0].env[11].name + value: STAN_CLUSTER + documentIndex: 12 + - equal: + path: spec.template.spec.containers[0].env[11].value + value: nats-streaming + documentIndex: 12 + - equal: + path: spec.template.spec.containers[0].env[12].name + value: TENANT_ID + documentIndex: 12 + - isEmpty: + path: spec.template.spec.containers[0].env[12].value + documentIndex: 12 diff --git a/helm/mds/tests/egress-all-default_test.yaml b/helm/mds/tests/egress-all_test.yaml similarity index 87% rename from helm/mds/tests/egress-all-default_test.yaml rename to helm/mds/tests/egress-all_test.yaml index 8af1f0fda..9d00300ad 100644 --- a/helm/mds/tests/egress-all-default_test.yaml +++ b/helm/mds/tests/egress-all_test.yaml @@ -7,4 +7,4 @@ tests: namespace: mds asserts: - hasDocuments: - count: 0 + count: 2 diff --git a/helm/mds/tests/egress-nats-natsNamespace_test.yaml b/helm/mds/tests/egress-nats-natsNamespace_test.yaml new file mode 100644 index 000000000..0d6031e42 --- /dev/null +++ b/helm/mds/tests/egress-nats-natsNamespace_test.yaml @@ -0,0 +1,68 @@ +suite: test egress nats natsNamespace +templates: + - egress.yaml +tests: + - it: all + release: + namespace: mds + set: + natsNamespace: foo + asserts: + - hasDocuments: + count: 2 + - isAPIVersion: + of: networking.istio.io/v1alpha3 + documentIndex: 0 + - isKind: + of: ServiceEntry + documentIndex: 0 + - equal: + path: metadata.name + value: nats + documentIndex: 0 + - equal: + path: metadata.namespace + value: mds + documentIndex: 0 + - equal: + path: spec.hosts[0] + value: nats.foo.svc.cluster.local + documentIndex: 0 + - equal: + path: spec.ports[0].number + value: 4222 + documentIndex: 0 + - equal: + path: spec.ports[0].name + value: client + documentIndex: 0 + - equal: + path: spec.ports[0].protocol + value: TCP + documentIndex: 0 + - equal: + path: spec.location + value: MESH_EXTERNAL + documentIndex: 0 + - isAPIVersion: + of: networking.istio.io/v1alpha3 + documentIndex: 1 + - isKind: + of: DestinationRule + documentIndex: 1 + - equal: + path: metadata.name + value: nats + documentIndex: 1 + - equal: + path: metadata.namespace + value: mds + documentIndex: 1 + - equal: + path: spec.host + value: "*.foo.svc.cluster.local" + documentIndex: 1 + - equal: + path: spec.trafficPolicy.tls.mode + value: DISABLE + documentIndex: 1 \ No newline at end of file diff --git a/helm/mds/tests/egress-nats_test.yaml b/helm/mds/tests/egress-nats_test.yaml new file mode 100644 index 000000000..7125960b5 --- /dev/null +++ b/helm/mds/tests/egress-nats_test.yaml @@ -0,0 +1,66 @@ +suite: test egress nats +templates: + - egress.yaml +tests: + - it: all + release: + namespace: mds + asserts: + - hasDocuments: + count: 2 + - isAPIVersion: + of: networking.istio.io/v1alpha3 + documentIndex: 0 + - isKind: + of: ServiceEntry + documentIndex: 0 + - equal: + path: metadata.name + value: nats + documentIndex: 0 + - equal: + path: metadata.namespace + value: mds + documentIndex: 0 + - equal: + path: spec.hosts[0] + value: nats.nats.svc.cluster.local + documentIndex: 0 + - equal: + path: spec.ports[0].number + value: 4222 + documentIndex: 0 + - equal: + path: spec.ports[0].name + value: client + documentIndex: 0 + - equal: + path: spec.ports[0].protocol + value: TCP + documentIndex: 0 + - equal: + path: spec.location + value: MESH_EXTERNAL + documentIndex: 0 + - isAPIVersion: + of: networking.istio.io/v1alpha3 + documentIndex: 1 + - isKind: + of: DestinationRule + documentIndex: 1 + - equal: + path: metadata.name + value: nats + documentIndex: 1 + - equal: + path: metadata.namespace + value: mds + documentIndex: 1 + - equal: + path: spec.host + value: "*.nats.svc.cluster.local" + documentIndex: 1 + - equal: + path: spec.trafficPolicy.tls.mode + value: DISABLE + documentIndex: 1 \ No newline at end of file diff --git a/helm/mds/tests/egress-fluentbit_test.yaml b/helm/mds/tests/egress-other_test.yaml similarity index 55% rename from helm/mds/tests/egress-fluentbit_test.yaml rename to helm/mds/tests/egress-other_test.yaml index a84fc559f..ea957fb3f 100644 --- a/helm/mds/tests/egress-fluentbit_test.yaml +++ b/helm/mds/tests/egress-other_test.yaml @@ -1,4 +1,4 @@ -suite: test egress fluentbit +suite: test egress other templates: - egress.yaml tests: @@ -6,46 +6,48 @@ tests: release: namespace: mds set: - fluentbit: - enabled: true + egress: + hosts: + - host1 + - host2 asserts: - hasDocuments: - count: 1 + count: 3 - isAPIVersion: of: networking.istio.io/v1alpha3 - documentIndex: 0 + documentIndex: 2 - isKind: of: ServiceEntry - documentIndex: 0 + documentIndex: 2 - equal: path: metadata.name - value: fluentbit-output - documentIndex: 0 + value: egress-other + documentIndex: 2 - equal: path: metadata.namespace value: mds - documentIndex: 0 + documentIndex: 2 - equal: path: spec.hosts[0] - value: localhost - documentIndex: 0 + value: host1 + documentIndex: 2 + - equal: + path: spec.hosts[1] + value: host2 + documentIndex: 2 - equal: path: spec.ports[0].number - value: 65535 - documentIndex: 0 + value: 443 + documentIndex: 2 - equal: path: spec.ports[0].name - value: https - documentIndex: 0 + value: tcp + documentIndex: 2 - equal: path: spec.ports[0].protocol - value: HTTPS - documentIndex: 0 + value: TCP + documentIndex: 2 - equal: path: spec.location value: MESH_EXTERNAL - documentIndex: 0 - - equal: - path: spec.resolution - value: DNS - documentIndex: 0 + documentIndex: 2 \ No newline at end of file diff --git a/helm/mds/tests/egress-postgresql-host-reader_test.yaml b/helm/mds/tests/egress-postgresql-host-reader_test.yaml index bf4ad2628..7c5af87ec 100644 --- a/helm/mds/tests/egress-postgresql-host-reader_test.yaml +++ b/helm/mds/tests/egress-postgresql-host-reader_test.yaml @@ -12,72 +12,72 @@ tests: hostReader: postgresql-host-reader asserts: - hasDocuments: - count: 2 + count: 4 - isAPIVersion: of: networking.istio.io/v1alpha3 - documentIndex: 0 + documentIndex: 2 - isKind: of: ServiceEntry - documentIndex: 0 + documentIndex: 2 - equal: path: metadata.name value: pg-rw-external - documentIndex: 0 + documentIndex: 2 - equal: path: metadata.namespace value: mds - documentIndex: 0 + documentIndex: 2 - equal: path: spec.hosts[0] value: postgresql-host - documentIndex: 0 + documentIndex: 2 - equal: path: spec.ports[0].number value: 5432 - documentIndex: 0 + documentIndex: 2 - equal: path: spec.ports[0].name value: tcp - documentIndex: 0 + documentIndex: 2 - equal: path: spec.ports[0].protocol value: TCP - documentIndex: 0 + documentIndex: 2 - equal: path: spec.location value: MESH_EXTERNAL - documentIndex: 0 + documentIndex: 2 - isAPIVersion: of: networking.istio.io/v1alpha3 - documentIndex: 1 + documentIndex: 3 - isKind: of: ServiceEntry - documentIndex: 1 + documentIndex: 3 - equal: path: metadata.name value: pg-ro-external - documentIndex: 1 + documentIndex: 3 - equal: path: metadata.namespace value: mds - documentIndex: 1 + documentIndex: 3 - equal: path: spec.hosts[0] value: postgresql-host-reader - documentIndex: 1 + documentIndex: 3 - equal: path: spec.ports[0].number value: 5432 - documentIndex: 1 + documentIndex: 3 - equal: path: spec.ports[0].name value: tcp - documentIndex: 1 + documentIndex: 3 - equal: path: spec.ports[0].protocol value: TCP - documentIndex: 1 + documentIndex: 3 - equal: path: spec.location value: MESH_EXTERNAL - documentIndex: 1 + documentIndex: 3 diff --git a/helm/mds/tests/egress-postgresql_test.yaml b/helm/mds/tests/egress-postgresql_test.yaml index 401de4ae3..6da1705e4 100644 --- a/helm/mds/tests/egress-postgresql_test.yaml +++ b/helm/mds/tests/egress-postgresql_test.yaml @@ -5,46 +5,44 @@ tests: - it: all release: namespace: mds - # values: - # - ./egress-values-postgresql.yaml set: postgresql: internal: false host: postgresql-host asserts: - hasDocuments: - count: 1 + count: 3 - isAPIVersion: of: networking.istio.io/v1alpha3 - documentIndex: 0 + documentIndex: 2 - isKind: of: ServiceEntry - documentIndex: 0 + documentIndex: 2 - equal: path: metadata.name value: pg-rw-external - documentIndex: 0 + documentIndex: 2 - equal: path: metadata.namespace value: mds - documentIndex: 0 + documentIndex: 2 - equal: path: spec.hosts[0] value: postgresql-host - documentIndex: 0 + documentIndex: 2 - equal: path: spec.ports[0].number value: 5432 - documentIndex: 0 + documentIndex: 2 - equal: path: spec.ports[0].name value: tcp - documentIndex: 0 + documentIndex: 2 - equal: path: spec.ports[0].protocol value: TCP - documentIndex: 0 + documentIndex: 2 - equal: path: spec.location value: MESH_EXTERNAL - documentIndex: 0 + documentIndex: 2 diff --git a/helm/mds/tests/egress-redis_test.yaml b/helm/mds/tests/egress-redis_test.yaml index 2589a44a3..312ecffee 100644 --- a/helm/mds/tests/egress-redis_test.yaml +++ b/helm/mds/tests/egress-redis_test.yaml @@ -11,38 +11,38 @@ tests: host: redis-host asserts: - hasDocuments: - count: 1 + count: 3 - isAPIVersion: of: networking.istio.io/v1alpha3 - documentIndex: 0 + documentIndex: 2 - isKind: of: ServiceEntry - documentIndex: 0 + documentIndex: 2 - equal: path: metadata.name value: redis-external - documentIndex: 0 + documentIndex: 2 - equal: path: metadata.namespace value: mds - documentIndex: 0 + documentIndex: 2 - equal: path: spec.hosts[0] value: redis-host - documentIndex: 0 + documentIndex: 2 - equal: path: spec.ports[0].number value: 6379 - documentIndex: 0 + documentIndex: 2 - equal: path: spec.ports[0].name value: tcp - documentIndex: 0 + documentIndex: 2 - equal: path: spec.ports[0].protocol value: TCP - documentIndex: 0 + documentIndex: 2 - equal: path: spec.location value: MESH_EXTERNAL - documentIndex: 0 + documentIndex: 2 diff --git a/helm/mds/tests/ingress-agency-tls_test.yaml b/helm/mds/tests/ingress-agency-domain_test.yaml similarity index 92% rename from helm/mds/tests/ingress-agency-tls_test.yaml rename to helm/mds/tests/ingress-agency-domain_test.yaml index 50ea34bb7..67be5f436 100644 --- a/helm/mds/tests/ingress-agency-tls_test.yaml +++ b/helm/mds/tests/ingress-agency-domain_test.yaml @@ -1,4 +1,4 @@ -suite: test ingress agency +suite: test ingress agency domain templates: - ingress.yaml tests: @@ -6,8 +6,7 @@ tests: release: namespace: mds set: - tls: - enabled: true + domain: my-domain asserts: - isAPIVersion: of: networking.istio.io/v1alpha3 @@ -25,24 +24,24 @@ tests: documentIndex: 0 - equal: path: spec.hosts[0] - value: '*' + value: "my-domain" documentIndex: 0 - equal: path: spec.gateways[0] value: mds-gateway documentIndex: 0 - equal: - path: spec.http[0].match[0].uri.prefix - value: /agency - documentIndex: 0 - - equal: - path: spec.http[0].route[0].destination.port.number - value: 4001 + path: spec.http[0].match[0].uri.regex + value: "^/agency($|/.*$)" documentIndex: 0 - equal: path: spec.http[0].route[0].destination.host value: mds-agency.mds.svc.cluster.local documentIndex: 0 + - equal: + path: spec.http[0].route[0].destination.port.number + value: 4000 + documentIndex: 0 - equal: path: spec.http[0].corsPolicy.allowOrigin[0] value: "*" diff --git a/helm/mds/tests/ingress-agency_test.yaml b/helm/mds/tests/ingress-agency_test.yaml index c2e55a59b..2015bf48c 100644 --- a/helm/mds/tests/ingress-agency_test.yaml +++ b/helm/mds/tests/ingress-agency_test.yaml @@ -29,17 +29,17 @@ tests: value: mds-gateway documentIndex: 0 - equal: - path: spec.http[0].match[0].uri.prefix - value: /agency - documentIndex: 0 - - equal: - path: spec.http[0].route[0].destination.port.number - value: 4001 + path: spec.http[0].match[0].uri.regex + value: "^/agency($|/.*$)" documentIndex: 0 - equal: path: spec.http[0].route[0].destination.host value: mds-agency.mds.svc.cluster.local documentIndex: 0 + - equal: + path: spec.http[0].route[0].destination.port.number + value: 4000 + documentIndex: 0 - equal: path: spec.http[0].corsPolicy.allowOrigin[0] value: "*" diff --git a/helm/mds/tests/ingress-all-tls-domain-lets-encrypt-production_test.yaml b/helm/mds/tests/ingress-all-tls-domain-lets-encrypt-production_test.yaml deleted file mode 100644 index 0868b10c0..000000000 --- a/helm/mds/tests/ingress-all-tls-domain-lets-encrypt-production_test.yaml +++ /dev/null @@ -1,51 +0,0 @@ -suite: test ingress all -templates: - - ingress.yaml -tests: - - it: all - release: - namespace: mds - set: - tls: - enabled: true - domain: domain - letsencrypt: - enabled: true - production: true - asserts: - - hasDocuments: - count: 9 - - isAPIVersion: - of: certmanager.k8s.io/v1alpha1 - documentIndex: 8 - - isKind: - of: Certificate - documentIndex: 8 - - equal: - path: metadata.name - value: ingress-cert - documentIndex: 8 - - equal: - path: metadata.namespace - value: istio-system - documentIndex: 8 - - equal: - path: spec.secretName - value: ingress-cert - documentIndex: 8 - - equal: - path: spec.issuerRef.name - value: letsencrypt-prod - documentIndex: 8 - - equal: - path: spec.issuerRef.kind - value: ClusterIssuer - documentIndex: 8 - - equal: - path: spec.commonName - value: domain - documentIndex: 8 - - equal: - path: spec.dnsNames[0] - value: domain - documentIndex: 8 \ No newline at end of file diff --git a/helm/mds/tests/ingress-all-tls-domain-lets-encrypt_test.yaml b/helm/mds/tests/ingress-all-tls-domain-lets-encrypt_test.yaml deleted file mode 100644 index b7c9583ce..000000000 --- a/helm/mds/tests/ingress-all-tls-domain-lets-encrypt_test.yaml +++ /dev/null @@ -1,50 +0,0 @@ -suite: test ingress all -templates: - - ingress.yaml -tests: - - it: all - release: - namespace: mds - set: - tls: - enabled: true - domain: domain - letsencrypt: - enabled: true - asserts: - - hasDocuments: - count: 9 - - isAPIVersion: - of: certmanager.k8s.io/v1alpha1 - documentIndex: 8 - - isKind: - of: Certificate - documentIndex: 8 - - equal: - path: metadata.name - value: ingress-cert - documentIndex: 8 - - equal: - path: metadata.namespace - value: istio-system - documentIndex: 8 - - equal: - path: spec.secretName - value: ingress-cert - documentIndex: 8 - - equal: - path: spec.issuerRef.name - value: letsencrypt-staging - documentIndex: 8 - - equal: - path: spec.issuerRef.kind - value: ClusterIssuer - documentIndex: 8 - - equal: - path: spec.commonName - value: domain - documentIndex: 8 - - equal: - path: spec.dnsNames[0] - value: domain - documentIndex: 8 \ No newline at end of file diff --git a/helm/mds/tests/ingress-all-tls-domain_test.yaml b/helm/mds/tests/ingress-all-tls-domain_test.yaml deleted file mode 100644 index 1c037b311..000000000 --- a/helm/mds/tests/ingress-all-tls-domain_test.yaml +++ /dev/null @@ -1,60 +0,0 @@ -suite: test ingress all -templates: - - ingress.yaml -tests: - - it: all - release: - namespace: mds - set: - tls: - enabled: true - domain: domain - asserts: - - hasDocuments: - count: 9 - - isAPIVersion: - of: networking.istio.io/v1alpha3 - documentIndex: 8 - - isKind: - of: Gateway - documentIndex: 8 - - equal: - path: metadata.name - value: mds-gateway - documentIndex: 8 - - equal: - path: metadata.namespace - value: mds - documentIndex: 8 - - equal: - path: spec.selector.istio - value: ingressgateway - documentIndex: 8 - - equal: - path: spec.servers[0].port.number - value: 443 - documentIndex: 8 - - equal: - path: spec.servers[0].port.protocol - value: HTTPS - documentIndex: 8 - - equal: - path: spec.servers[0].port.name - value: https - documentIndex: 8 - - equal: - path: spec.servers[0].tls.mode - value: SIMPLE - documentIndex: 8 - - equal: - path: spec.servers[0].tls.privateKey - value: /etc/istio/ingressgateway-certs/tls.key - documentIndex: 8 - - equal: - path: spec.servers[0].tls.serverCertificate - value: /etc/istio/ingressgateway-certs/tls.crt - documentIndex: 8 - - equal: - path: spec.servers[0].hosts[0] - value: "*" - documentIndex: 8 \ No newline at end of file diff --git a/helm/mds/tests/ingress-all-tls-lets-encrypt-production_test.yaml b/helm/mds/tests/ingress-all-tls-lets-encrypt-production_test.yaml deleted file mode 100644 index 31fb0b402..000000000 --- a/helm/mds/tests/ingress-all-tls-lets-encrypt-production_test.yaml +++ /dev/null @@ -1,42 +0,0 @@ -suite: test ingress all -templates: - - ingress.yaml -tests: - - it: all - release: - namespace: mds - set: - tls: - enabled: true - letsencrypt: - enabled: true - production: true - asserts: - - hasDocuments: - count: 9 - - isAPIVersion: - of: certmanager.k8s.io/v1alpha1 - documentIndex: 8 - - isKind: - of: Certificate - documentIndex: 8 - - equal: - path: metadata.name - value: ingress-cert - documentIndex: 8 - - equal: - path: metadata.namespace - value: istio-system - documentIndex: 8 - - equal: - path: spec.secretName - value: ingress-cert - documentIndex: 8 - - equal: - path: spec.issuerRef.name - value: letsencrypt-prod - documentIndex: 8 - - equal: - path: spec.issuerRef.kind - value: ClusterIssuer - documentIndex: 8 \ No newline at end of file diff --git a/helm/mds/tests/ingress-all-tls-lets-encrypt_test.yaml b/helm/mds/tests/ingress-all-tls-lets-encrypt_test.yaml deleted file mode 100644 index 362770025..000000000 --- a/helm/mds/tests/ingress-all-tls-lets-encrypt_test.yaml +++ /dev/null @@ -1,41 +0,0 @@ -suite: test ingress all -templates: - - ingress.yaml -tests: - - it: all - release: - namespace: mds - set: - tls: - enabled: true - letsencrypt: - enabled: true - asserts: - - hasDocuments: - count: 9 - - isAPIVersion: - of: certmanager.k8s.io/v1alpha1 - documentIndex: 8 - - isKind: - of: Certificate - documentIndex: 8 - - equal: - path: metadata.name - value: ingress-cert - documentIndex: 8 - - equal: - path: metadata.namespace - value: istio-system - documentIndex: 8 - - equal: - path: spec.secretName - value: ingress-cert - documentIndex: 8 - - equal: - path: spec.issuerRef.name - value: letsencrypt-staging - documentIndex: 8 - - equal: - path: spec.issuerRef.kind - value: ClusterIssuer - documentIndex: 8 \ No newline at end of file diff --git a/helm/mds/tests/ingress-all-tls_test.yaml b/helm/mds/tests/ingress-all-tls_test.yaml deleted file mode 100644 index 0655c1fb6..000000000 --- a/helm/mds/tests/ingress-all-tls_test.yaml +++ /dev/null @@ -1,59 +0,0 @@ -suite: test ingress all -templates: - - ingress.yaml -tests: - - it: all - release: - namespace: mds - set: - tls: - enabled: true - asserts: - - hasDocuments: - count: 9 - - isAPIVersion: - of: networking.istio.io/v1alpha3 - documentIndex: 8 - - isKind: - of: Gateway - documentIndex: 8 - - equal: - path: metadata.name - value: mds-gateway - documentIndex: 8 - - equal: - path: metadata.namespace - value: mds - documentIndex: 8 - - equal: - path: spec.selector.istio - value: ingressgateway - documentIndex: 8 - - equal: - path: spec.servers[0].port.number - value: 443 - documentIndex: 8 - - equal: - path: spec.servers[0].port.protocol - value: HTTPS - documentIndex: 8 - - equal: - path: spec.servers[0].port.name - value: https - documentIndex: 8 - - equal: - path: spec.servers[0].tls.mode - value: SIMPLE - documentIndex: 8 - - equal: - path: spec.servers[0].tls.privateKey - value: /etc/istio/ingressgateway-certs/tls.key - documentIndex: 8 - - equal: - path: spec.servers[0].tls.serverCertificate - value: /etc/istio/ingressgateway-certs/tls.crt - documentIndex: 8 - - equal: - path: spec.servers[0].hosts[0] - value: "*" - documentIndex: 8 \ No newline at end of file diff --git a/helm/mds/tests/cronjob-default_test.yaml b/helm/mds/tests/ingress-all_test.yaml similarity index 61% rename from helm/mds/tests/cronjob-default_test.yaml rename to helm/mds/tests/ingress-all_test.yaml index 749a1c90d..00c6f7842 100644 --- a/helm/mds/tests/cronjob-default_test.yaml +++ b/helm/mds/tests/ingress-all_test.yaml @@ -1,10 +1,10 @@ -suite: test cronjob default +suite: test ingress all templates: - - cronjob.yaml + - ingress.yaml tests: - it: all release: namespace: mds asserts: - hasDocuments: - count: 0 \ No newline at end of file + count: 15 \ No newline at end of file diff --git a/helm/mds/tests/ingress-audit-tls_test.yaml b/helm/mds/tests/ingress-audit-domain_test.yaml similarity index 92% rename from helm/mds/tests/ingress-audit-tls_test.yaml rename to helm/mds/tests/ingress-audit-domain_test.yaml index 4a0151885..ba181fb24 100644 --- a/helm/mds/tests/ingress-audit-tls_test.yaml +++ b/helm/mds/tests/ingress-audit-domain_test.yaml @@ -1,4 +1,4 @@ -suite: test ingress audit +suite: test ingress audit domain templates: - ingress.yaml tests: @@ -6,8 +6,7 @@ tests: release: namespace: mds set: - tls: - enabled: true + domain: my-domain asserts: - isAPIVersion: of: networking.istio.io/v1alpha3 @@ -25,24 +24,24 @@ tests: documentIndex: 1 - equal: path: spec.hosts[0] - value: '*' + value: "my-domain" documentIndex: 1 - equal: path: spec.gateways[0] value: mds-gateway documentIndex: 1 - equal: - path: spec.http[0].match[0].uri.prefix - value: /audit - documentIndex: 1 - - equal: - path: spec.http[0].route[0].destination.port.number - value: 4002 + path: spec.http[0].match[0].uri.regex + value: "^/audit($|/.*$)" documentIndex: 1 - equal: path: spec.http[0].route[0].destination.host value: mds-audit.mds.svc.cluster.local documentIndex: 1 + - equal: + path: spec.http[0].route[0].destination.port.number + value: 4000 + documentIndex: 1 - equal: path: spec.http[0].corsPolicy.allowOrigin[0] value: "*" diff --git a/helm/mds/tests/ingress-audit_test.yaml b/helm/mds/tests/ingress-audit_test.yaml index cbe4c9a21..546102887 100644 --- a/helm/mds/tests/ingress-audit_test.yaml +++ b/helm/mds/tests/ingress-audit_test.yaml @@ -29,17 +29,17 @@ tests: value: mds-gateway documentIndex: 1 - equal: - path: spec.http[0].match[0].uri.prefix - value: /audit - documentIndex: 1 - - equal: - path: spec.http[0].route[0].destination.port.number - value: 4002 + path: spec.http[0].match[0].uri.regex + value: "^/audit($|/.*$)" documentIndex: 1 - equal: path: spec.http[0].route[0].destination.host value: mds-audit.mds.svc.cluster.local documentIndex: 1 + - equal: + path: spec.http[0].route[0].destination.port.number + value: 4000 + documentIndex: 1 - equal: path: spec.http[0].corsPolicy.allowOrigin[0] value: "*" diff --git a/helm/mds/tests/ingress-compliance-tls_test.yaml b/helm/mds/tests/ingress-compliance-domain_test.yaml similarity index 92% rename from helm/mds/tests/ingress-compliance-tls_test.yaml rename to helm/mds/tests/ingress-compliance-domain_test.yaml index 17a1944d1..5160bb239 100644 --- a/helm/mds/tests/ingress-compliance-tls_test.yaml +++ b/helm/mds/tests/ingress-compliance-domain_test.yaml @@ -1,4 +1,4 @@ -suite: test ingress compliance +suite: test ingress compliance domain templates: - ingress.yaml tests: @@ -6,8 +6,7 @@ tests: release: namespace: mds set: - tls: - enabled: true + domain: my-domain asserts: - isAPIVersion: of: networking.istio.io/v1alpha3 @@ -25,24 +24,24 @@ tests: documentIndex: 2 - equal: path: spec.hosts[0] - value: '*' + value: "my-domain" documentIndex: 2 - equal: path: spec.gateways[0] value: mds-gateway documentIndex: 2 - equal: - path: spec.http[0].match[0].uri.prefix - value: /compliance - documentIndex: 2 - - equal: - path: spec.http[0].route[0].destination.port.number - value: 4004 + path: spec.http[0].match[0].uri.regex + value: "^/compliance($|/.*$)" documentIndex: 2 - equal: path: spec.http[0].route[0].destination.host value: mds-compliance.mds.svc.cluster.local documentIndex: 2 + - equal: + path: spec.http[0].route[0].destination.port.number + value: 4000 + documentIndex: 2 - equal: path: spec.http[0].corsPolicy.allowOrigin[0] value: "*" diff --git a/helm/mds/tests/ingress-compliance-tls-domain_test.yaml b/helm/mds/tests/ingress-compliance-tls-domain_test.yaml deleted file mode 100644 index abadc8044..000000000 --- a/helm/mds/tests/ingress-compliance-tls-domain_test.yaml +++ /dev/null @@ -1,94 +0,0 @@ -suite: test ingress compliance -templates: - - ingress.yaml -tests: - - it: doc 2 - release: - namespace: mds - set: - tls: - enabled: true - domain: domain - asserts: - - isAPIVersion: - of: networking.istio.io/v1alpha3 - documentIndex: 2 - - isKind: - of: VirtualService - documentIndex: 2 - - equal: - path: metadata.name - value: mds-compliance-route - documentIndex: 2 - - equal: - path: metadata.namespace - value: mds - documentIndex: 2 - - equal: - path: spec.hosts[0] - value: '*' - documentIndex: 2 - - equal: - path: spec.gateways[0] - value: mds-gateway - documentIndex: 2 - - equal: - path: spec.http[0].match[0].uri.prefix - value: /compliance - documentIndex: 2 - - equal: - path: spec.http[0].route[0].destination.port.number - value: 4004 - documentIndex: 2 - - equal: - path: spec.http[0].route[0].destination.host - value: mds-compliance.mds.svc.cluster.local - documentIndex: 2 - - equal: - path: spec.http[0].corsPolicy.allowOrigin[0] - value: "*" - documentIndex: 2 - - equal: - path: spec.http[0].corsPolicy.allowMethods[0] - value: POST - documentIndex: 2 - - equal: - path: spec.http[0].corsPolicy.allowMethods[1] - value: GET - documentIndex: 2 - - equal: - path: spec.http[0].corsPolicy.allowMethods[2] - value: HEAD - documentIndex: 2 - - equal: - path: spec.http[0].corsPolicy.allowMethods[3] - value: OPTIONS - documentIndex: 2 - - equal: - path: spec.http[0].corsPolicy.allowMethods[4] - value: PATCH - documentIndex: 2 - - equal: - path: spec.http[0].corsPolicy.allowMethods[5] - value: PUT - documentIndex: 2 - - equal: - path: spec.http[0].corsPolicy.allowMethods[6] - value: DELETE - documentIndex: 2 - - equal: - path: spec.http[0].corsPolicy.allowCredentials - value: false - documentIndex: 2 - - equal: - path: spec.http[0].corsPolicy.allowHeaders[0] - value: Content-Type - documentIndex: 2 - - equal: - path: spec.http[0].corsPolicy.allowHeaders[1] - value: Authorization - documentIndex: 2 - - equal: - path: spec.http[0].corsPolicy.maxAge - value: "10m" - documentIndex: 2 diff --git a/helm/mds/tests/ingress-compliance_test.yaml b/helm/mds/tests/ingress-compliance_test.yaml index dfcbac755..d11eb3852 100644 --- a/helm/mds/tests/ingress-compliance_test.yaml +++ b/helm/mds/tests/ingress-compliance_test.yaml @@ -29,17 +29,17 @@ tests: value: mds-gateway documentIndex: 2 - equal: - path: spec.http[0].match[0].uri.prefix - value: /compliance - documentIndex: 2 - - equal: - path: spec.http[0].route[0].destination.port.number - value: 4004 + path: spec.http[0].match[0].uri.regex + value: "^/compliance($|/.*$)" documentIndex: 2 - equal: path: spec.http[0].route[0].destination.host value: mds-compliance.mds.svc.cluster.local documentIndex: 2 + - equal: + path: spec.http[0].route[0].destination.port.number + value: 4000 + documentIndex: 2 - equal: path: spec.http[0].corsPolicy.allowOrigin[0] value: "*" diff --git a/helm/mds/tests/ingress-daily-tls_test.yaml b/helm/mds/tests/ingress-config-domain_test.yaml similarity index 81% rename from helm/mds/tests/ingress-daily-tls_test.yaml rename to helm/mds/tests/ingress-config-domain_test.yaml index eceeef770..bb1760833 100644 --- a/helm/mds/tests/ingress-daily-tls_test.yaml +++ b/helm/mds/tests/ingress-config-domain_test.yaml @@ -1,4 +1,4 @@ -suite: test ingress daily +suite: test ingress config domain templates: - ingress.yaml tests: @@ -6,8 +6,7 @@ tests: release: namespace: mds set: - tls: - enabled: true + domain: my-domain asserts: - isAPIVersion: of: networking.istio.io/v1alpha3 @@ -15,33 +14,21 @@ tests: - isKind: of: VirtualService documentIndex: 3 - - equal: - path: metadata.name - value: mds-daily-route - documentIndex: 3 - equal: path: metadata.namespace value: mds documentIndex: 3 - equal: path: spec.hosts[0] - value: '*' + value: "my-domain" documentIndex: 3 - equal: path: spec.gateways[0] value: mds-gateway documentIndex: 3 - - equal: - path: spec.http[0].match[0].uri.prefix - value: /daily - documentIndex: 3 - equal: path: spec.http[0].route[0].destination.port.number - value: 4005 - documentIndex: 3 - - equal: - path: spec.http[0].route[0].destination.host - value: mds-daily.mds.svc.cluster.local + value: 4000 documentIndex: 3 - equal: path: spec.http[0].corsPolicy.allowOrigin[0] diff --git a/helm/mds/tests/ingress-daily-tls-domain_test.yaml b/helm/mds/tests/ingress-config_test.yaml similarity index 80% rename from helm/mds/tests/ingress-daily-tls-domain_test.yaml rename to helm/mds/tests/ingress-config_test.yaml index d6d54fe47..4b0b9df34 100644 --- a/helm/mds/tests/ingress-daily-tls-domain_test.yaml +++ b/helm/mds/tests/ingress-config_test.yaml @@ -1,14 +1,10 @@ -suite: test ingress daily +suite: test ingress config templates: - ingress.yaml tests: - it: doc 3 release: namespace: mds - set: - tls: - enabled: true - domain: domain asserts: - isAPIVersion: of: networking.istio.io/v1alpha3 @@ -16,33 +12,21 @@ tests: - isKind: of: VirtualService documentIndex: 3 - - equal: - path: metadata.name - value: mds-daily-route - documentIndex: 3 - equal: path: metadata.namespace value: mds documentIndex: 3 - equal: path: spec.hosts[0] - value: '*' + value: "*" documentIndex: 3 - equal: path: spec.gateways[0] value: mds-gateway documentIndex: 3 - - equal: - path: spec.http[0].match[0].uri.prefix - value: /daily - documentIndex: 3 - equal: path: spec.http[0].route[0].destination.port.number - value: 4005 - documentIndex: 3 - - equal: - path: spec.http[0].route[0].destination.host - value: mds-daily.mds.svc.cluster.local + value: 4000 documentIndex: 3 - equal: path: spec.http[0].corsPolicy.allowOrigin[0] diff --git a/helm/mds/tests/ingress-native_test.yaml b/helm/mds/tests/ingress-daily-domain_test.yaml similarity index 88% rename from helm/mds/tests/ingress-native_test.yaml rename to helm/mds/tests/ingress-daily-domain_test.yaml index e2c76b711..bbda18243 100644 --- a/helm/mds/tests/ingress-native_test.yaml +++ b/helm/mds/tests/ingress-daily-domain_test.yaml @@ -1,10 +1,12 @@ -suite: test ingress native +suite: test ingress daily domain templates: - ingress.yaml tests: - it: doc 4 release: namespace: mds + set: + domain: my-domain asserts: - isAPIVersion: of: networking.istio.io/v1alpha3 @@ -14,7 +16,7 @@ tests: documentIndex: 4 - equal: path: metadata.name - value: mds-native-route + value: mds-daily-route documentIndex: 4 - equal: path: metadata.namespace @@ -22,27 +24,27 @@ tests: documentIndex: 4 - equal: path: spec.hosts[0] - value: "*" + value: "my-domain" documentIndex: 4 - equal: path: spec.gateways[0] value: mds-gateway documentIndex: 4 - equal: - path: spec.http[0].match[0].uri.prefix - value: /native + path: spec.http[0].match[0].uri.regex + value: "^/daily($|/.*$)" documentIndex: 4 - equal: - path: spec.http[0].route[0].destination.port.number - value: 4006 + path: spec.http[0].route[0].destination.host + value: mds-daily.mds.svc.cluster.local documentIndex: 4 - equal: - path: spec.http[0].route[0].destination.host - value: mds-native.mds.svc.cluster.local + path: spec.http[0].route[0].destination.port.number + value: 4000 documentIndex: 4 - equal: path: spec.http[0].corsPolicy.allowOrigin[0] - value: '*' + value: "*" documentIndex: 4 - equal: path: spec.http[0].corsPolicy.allowMethods[0] @@ -86,5 +88,5 @@ tests: documentIndex: 4 - equal: path: spec.http[0].corsPolicy.maxAge - value: '10m' + value: "10m" documentIndex: 4 diff --git a/helm/mds/tests/ingress-daily_test.yaml b/helm/mds/tests/ingress-daily_test.yaml index 297c52af8..d1e850647 100644 --- a/helm/mds/tests/ingress-daily_test.yaml +++ b/helm/mds/tests/ingress-daily_test.yaml @@ -2,89 +2,89 @@ suite: test ingress daily templates: - ingress.yaml tests: - - it: doc 3 + - it: doc 4 release: namespace: mds asserts: - isAPIVersion: of: networking.istio.io/v1alpha3 - documentIndex: 3 + documentIndex: 4 - isKind: of: VirtualService - documentIndex: 3 + documentIndex: 4 - equal: path: metadata.name value: mds-daily-route - documentIndex: 3 + documentIndex: 4 - equal: path: metadata.namespace value: mds - documentIndex: 3 + documentIndex: 4 - equal: path: spec.hosts[0] value: "*" - documentIndex: 3 + documentIndex: 4 - equal: path: spec.gateways[0] value: mds-gateway - documentIndex: 3 + documentIndex: 4 - equal: - path: spec.http[0].match[0].uri.prefix - value: /daily - documentIndex: 3 - - equal: - path: spec.http[0].route[0].destination.port.number - value: 4005 - documentIndex: 3 + path: spec.http[0].match[0].uri.regex + value: "^/daily($|/.*$)" + documentIndex: 4 - equal: path: spec.http[0].route[0].destination.host value: mds-daily.mds.svc.cluster.local - documentIndex: 3 + documentIndex: 4 + - equal: + path: spec.http[0].route[0].destination.port.number + value: 4000 + documentIndex: 4 - equal: path: spec.http[0].corsPolicy.allowOrigin[0] value: "*" - documentIndex: 3 + documentIndex: 4 - equal: path: spec.http[0].corsPolicy.allowMethods[0] value: POST - documentIndex: 3 + documentIndex: 4 - equal: path: spec.http[0].corsPolicy.allowMethods[1] value: GET - documentIndex: 3 + documentIndex: 4 - equal: path: spec.http[0].corsPolicy.allowMethods[2] value: HEAD - documentIndex: 3 + documentIndex: 4 - equal: path: spec.http[0].corsPolicy.allowMethods[3] value: OPTIONS - documentIndex: 3 + documentIndex: 4 - equal: path: spec.http[0].corsPolicy.allowMethods[4] value: PATCH - documentIndex: 3 + documentIndex: 4 - equal: path: spec.http[0].corsPolicy.allowMethods[5] value: PUT - documentIndex: 3 + documentIndex: 4 - equal: path: spec.http[0].corsPolicy.allowMethods[6] value: DELETE - documentIndex: 3 + documentIndex: 4 - equal: path: spec.http[0].corsPolicy.allowCredentials value: false - documentIndex: 3 + documentIndex: 4 - equal: path: spec.http[0].corsPolicy.allowHeaders[0] value: Content-Type - documentIndex: 3 + documentIndex: 4 - equal: path: spec.http[0].corsPolicy.allowHeaders[1] value: Authorization - documentIndex: 3 + documentIndex: 4 - equal: path: spec.http[0].corsPolicy.maxAge value: "10m" - documentIndex: 3 + documentIndex: 4 diff --git a/helm/mds/tests/ingress-destination-rule_test.yaml b/helm/mds/tests/ingress-destination-rule_test.yaml new file mode 100644 index 000000000..93ac60923 --- /dev/null +++ b/helm/mds/tests/ingress-destination-rule_test.yaml @@ -0,0 +1,38 @@ +suite: test ingress destination-rule +templates: + - ingress.yaml +tests: + - it: doc 14 + release: + namespace: mds + asserts: + - isAPIVersion: + of: networking.istio.io/v1alpha3 + documentIndex: 14 + - isKind: + of: DestinationRule + documentIndex: 14 + - equal: + path: metadata.name + value: mds-resolve-timeout-race + documentIndex: 14 + - equal: + path: metadata.namespace + value: mds + documentIndex: 14 + - equal: + path: spec.host + value: "*.mds.svc.cluster.local" + documentIndex: 14 + - equal: + path: spec.trafficPolicy.tls.mode + value: ISTIO_MUTUAL + documentIndex: 14 + - equal: + path: spec.trafficPolicy.connectionPool.http.idleTimeout + value: 3s + documentIndex: 14 + - equal: + path: spec.trafficPolicy.connectionPool.http.maxRetries + value: 3 + documentIndex: 14 \ No newline at end of file diff --git a/helm/mds/tests/ingress-all-default_test.yaml b/helm/mds/tests/ingress-gateway_test.yaml similarity index 70% rename from helm/mds/tests/ingress-all-default_test.yaml rename to helm/mds/tests/ingress-gateway_test.yaml index 12adbf3dc..75ea5848d 100644 --- a/helm/mds/tests/ingress-all-default_test.yaml +++ b/helm/mds/tests/ingress-gateway_test.yaml @@ -1,44 +1,42 @@ -suite: test ingress all +suite: test ingress gateway templates: - ingress.yaml tests: - - it: all + - it: doc 13 release: namespace: mds asserts: - - hasDocuments: - count: 9 - isAPIVersion: of: networking.istio.io/v1alpha3 - documentIndex: 8 + documentIndex: 13 - isKind: of: Gateway - documentIndex: 8 + documentIndex: 13 - equal: path: metadata.name value: mds-gateway - documentIndex: 8 + documentIndex: 13 - equal: path: metadata.namespace value: mds - documentIndex: 8 + documentIndex: 13 - equal: path: spec.selector.istio value: ingressgateway - documentIndex: 8 + documentIndex: 13 - equal: path: spec.servers[0].port.number value: 80 - documentIndex: 8 - - equal: - path: spec.servers[0].port.protocol - value: HTTP - documentIndex: 8 + documentIndex: 13 - equal: path: spec.servers[0].port.name value: http - documentIndex: 8 + documentIndex: 13 + - equal: + path: spec.servers[0].port.protocol + value: HTTP + documentIndex: 13 - equal: path: spec.servers[0].hosts[0] value: "*" - documentIndex: 8 + documentIndex: 13 \ No newline at end of file diff --git a/helm/mds/tests/ingress-agency-tls-domain_test.yaml b/helm/mds/tests/ingress-geography-author-domain_test.yaml similarity index 66% rename from helm/mds/tests/ingress-agency-tls-domain_test.yaml rename to helm/mds/tests/ingress-geography-author-domain_test.yaml index 0c0898db1..825194f8c 100644 --- a/helm/mds/tests/ingress-agency-tls-domain_test.yaml +++ b/helm/mds/tests/ingress-geography-author-domain_test.yaml @@ -1,94 +1,92 @@ -suite: test ingress agency +suite: test ingress geography-author domain templates: - ingress.yaml tests: - - it: doc 0 + - it: doc 6 release: namespace: mds set: - tls: - enabled: true - domain: domain + domain: my-domain asserts: - isAPIVersion: of: networking.istio.io/v1alpha3 - documentIndex: 0 + documentIndex: 6 - isKind: of: VirtualService - documentIndex: 0 + documentIndex: 6 - equal: path: metadata.name - value: mds-agency-route - documentIndex: 0 + value: mds-geography-author-route + documentIndex: 6 - equal: path: metadata.namespace value: mds - documentIndex: 0 + documentIndex: 6 - equal: path: spec.hosts[0] - value: '*' - documentIndex: 0 + value: "my-domain" + documentIndex: 6 - equal: path: spec.gateways[0] value: mds-gateway - documentIndex: 0 + documentIndex: 6 - equal: - path: spec.http[0].match[0].uri.prefix - value: /agency - documentIndex: 0 - - equal: - path: spec.http[0].route[0].destination.port.number - value: 4001 - documentIndex: 0 + path: spec.http[0].match[0].uri.regex + value: "^/geography-author($|/.*$)" + documentIndex: 6 - equal: path: spec.http[0].route[0].destination.host - value: mds-agency.mds.svc.cluster.local - documentIndex: 0 + value: mds-geography-author.mds.svc.cluster.local + documentIndex: 6 + - equal: + path: spec.http[0].route[0].destination.port.number + value: 4000 + documentIndex: 6 - equal: path: spec.http[0].corsPolicy.allowOrigin[0] value: "*" - documentIndex: 0 + documentIndex: 6 - equal: path: spec.http[0].corsPolicy.allowMethods[0] value: POST - documentIndex: 0 + documentIndex: 6 - equal: path: spec.http[0].corsPolicy.allowMethods[1] value: GET - documentIndex: 0 + documentIndex: 6 - equal: path: spec.http[0].corsPolicy.allowMethods[2] value: HEAD - documentIndex: 0 + documentIndex: 6 - equal: path: spec.http[0].corsPolicy.allowMethods[3] value: OPTIONS - documentIndex: 0 + documentIndex: 6 - equal: path: spec.http[0].corsPolicy.allowMethods[4] value: PATCH - documentIndex: 0 + documentIndex: 6 - equal: path: spec.http[0].corsPolicy.allowMethods[5] value: PUT - documentIndex: 0 + documentIndex: 6 - equal: path: spec.http[0].corsPolicy.allowMethods[6] value: DELETE - documentIndex: 0 + documentIndex: 6 - equal: path: spec.http[0].corsPolicy.allowCredentials value: false - documentIndex: 0 + documentIndex: 6 - equal: path: spec.http[0].corsPolicy.allowHeaders[0] value: Content-Type - documentIndex: 0 + documentIndex: 6 - equal: path: spec.http[0].corsPolicy.allowHeaders[1] value: Authorization - documentIndex: 0 + documentIndex: 6 - equal: path: spec.http[0].corsPolicy.maxAge value: "10m" - documentIndex: 0 + documentIndex: 6 diff --git a/helm/mds/tests/ingress-geography-author_test.yaml b/helm/mds/tests/ingress-geography-author_test.yaml new file mode 100644 index 000000000..83b2d31e8 --- /dev/null +++ b/helm/mds/tests/ingress-geography-author_test.yaml @@ -0,0 +1,90 @@ +suite: test ingress geography-author +templates: + - ingress.yaml +tests: + - it: doc 6 + release: + namespace: mds + asserts: + - isAPIVersion: + of: networking.istio.io/v1alpha3 + documentIndex: 6 + - isKind: + of: VirtualService + documentIndex: 6 + - equal: + path: metadata.name + value: mds-geography-author-route + documentIndex: 6 + - equal: + path: metadata.namespace + value: mds + documentIndex: 6 + - equal: + path: spec.hosts[0] + value: "*" + documentIndex: 6 + - equal: + path: spec.gateways[0] + value: mds-gateway + documentIndex: 6 + - equal: + path: spec.http[0].match[0].uri.regex + value: "^/geography-author($|/.*$)" + documentIndex: 6 + - equal: + path: spec.http[0].route[0].destination.host + value: mds-geography-author.mds.svc.cluster.local + documentIndex: 6 + - equal: + path: spec.http[0].route[0].destination.port.number + value: 4000 + documentIndex: 6 + - equal: + path: spec.http[0].corsPolicy.allowOrigin[0] + value: "*" + documentIndex: 6 + - equal: + path: spec.http[0].corsPolicy.allowMethods[0] + value: POST + documentIndex: 6 + - equal: + path: spec.http[0].corsPolicy.allowMethods[1] + value: GET + documentIndex: 6 + - equal: + path: spec.http[0].corsPolicy.allowMethods[2] + value: HEAD + documentIndex: 6 + - equal: + path: spec.http[0].corsPolicy.allowMethods[3] + value: OPTIONS + documentIndex: 6 + - equal: + path: spec.http[0].corsPolicy.allowMethods[4] + value: PATCH + documentIndex: 6 + - equal: + path: spec.http[0].corsPolicy.allowMethods[5] + value: PUT + documentIndex: 6 + - equal: + path: spec.http[0].corsPolicy.allowMethods[6] + value: DELETE + documentIndex: 6 + - equal: + path: spec.http[0].corsPolicy.allowCredentials + value: false + documentIndex: 6 + - equal: + path: spec.http[0].corsPolicy.allowHeaders[0] + value: Content-Type + documentIndex: 6 + - equal: + path: spec.http[0].corsPolicy.allowHeaders[1] + value: Authorization + documentIndex: 6 + - equal: + path: spec.http[0].corsPolicy.maxAge + value: "10m" + documentIndex: 6 diff --git a/helm/mds/tests/ingress-audit-tls-domain_test.yaml b/helm/mds/tests/ingress-jurisdiction-domain_test.yaml similarity index 67% rename from helm/mds/tests/ingress-audit-tls-domain_test.yaml rename to helm/mds/tests/ingress-jurisdiction-domain_test.yaml index dd7d4d322..279eccd87 100644 --- a/helm/mds/tests/ingress-audit-tls-domain_test.yaml +++ b/helm/mds/tests/ingress-jurisdiction-domain_test.yaml @@ -1,94 +1,92 @@ -suite: test ingress audit +suite: test ingress jurisdiction domain templates: - ingress.yaml tests: - - it: doc 1 + - it: doc 7 release: namespace: mds set: - tls: - enabled: true - domain: domain + domain: my-domain asserts: - isAPIVersion: of: networking.istio.io/v1alpha3 - documentIndex: 1 + documentIndex: 7 - isKind: of: VirtualService - documentIndex: 1 + documentIndex: 7 - equal: path: metadata.name - value: mds-audit-route - documentIndex: 1 + value: mds-jurisdiction-route + documentIndex: 7 - equal: path: metadata.namespace value: mds - documentIndex: 1 + documentIndex: 7 - equal: path: spec.hosts[0] - value: '*' - documentIndex: 1 + value: "my-domain" + documentIndex: 7 - equal: path: spec.gateways[0] value: mds-gateway - documentIndex: 1 + documentIndex: 7 - equal: - path: spec.http[0].match[0].uri.prefix - value: /audit - documentIndex: 1 - - equal: - path: spec.http[0].route[0].destination.port.number - value: 4002 - documentIndex: 1 + path: spec.http[0].match[0].uri.regex + value: "^/jurisdiction($|/.*$)" + documentIndex: 7 - equal: path: spec.http[0].route[0].destination.host - value: mds-audit.mds.svc.cluster.local - documentIndex: 1 + value: mds-jurisdiction.mds.svc.cluster.local + documentIndex: 7 + - equal: + path: spec.http[0].route[0].destination.port.number + value: 4000 + documentIndex: 7 - equal: path: spec.http[0].corsPolicy.allowOrigin[0] value: "*" - documentIndex: 1 + documentIndex: 7 - equal: path: spec.http[0].corsPolicy.allowMethods[0] value: POST - documentIndex: 1 + documentIndex: 7 - equal: path: spec.http[0].corsPolicy.allowMethods[1] value: GET - documentIndex: 1 + documentIndex: 7 - equal: path: spec.http[0].corsPolicy.allowMethods[2] value: HEAD - documentIndex: 1 + documentIndex: 7 - equal: path: spec.http[0].corsPolicy.allowMethods[3] value: OPTIONS - documentIndex: 1 + documentIndex: 7 - equal: path: spec.http[0].corsPolicy.allowMethods[4] value: PATCH - documentIndex: 1 + documentIndex: 7 - equal: path: spec.http[0].corsPolicy.allowMethods[5] value: PUT - documentIndex: 1 + documentIndex: 7 - equal: path: spec.http[0].corsPolicy.allowMethods[6] value: DELETE - documentIndex: 1 + documentIndex: 7 - equal: path: spec.http[0].corsPolicy.allowCredentials value: false - documentIndex: 1 + documentIndex: 7 - equal: path: spec.http[0].corsPolicy.allowHeaders[0] value: Content-Type - documentIndex: 1 + documentIndex: 7 - equal: path: spec.http[0].corsPolicy.allowHeaders[1] value: Authorization - documentIndex: 1 + documentIndex: 7 - equal: path: spec.http[0].corsPolicy.maxAge value: "10m" - documentIndex: 1 + documentIndex: 7 diff --git a/helm/mds/tests/ingress-policy-tls_test.yaml b/helm/mds/tests/ingress-jurisdiction_test.yaml similarity index 68% rename from helm/mds/tests/ingress-policy-tls_test.yaml rename to helm/mds/tests/ingress-jurisdiction_test.yaml index 975f6f2c7..52ef6e5b3 100644 --- a/helm/mds/tests/ingress-policy-tls_test.yaml +++ b/helm/mds/tests/ingress-jurisdiction_test.yaml @@ -1,93 +1,90 @@ -suite: test ingress policy +suite: test ingress jurisdiction templates: - ingress.yaml tests: - - it: doc 5 + - it: doc 7 release: namespace: mds - set: - tls: - enabled: true asserts: - isAPIVersion: of: networking.istio.io/v1alpha3 - documentIndex: 5 + documentIndex: 7 - isKind: of: VirtualService - documentIndex: 5 + documentIndex: 7 - equal: path: metadata.name - value: mds-policy-route - documentIndex: 5 + value: mds-jurisdiction-route + documentIndex: 7 - equal: path: metadata.namespace value: mds - documentIndex: 5 + documentIndex: 7 - equal: path: spec.hosts[0] - value: '*' - documentIndex: 5 + value: "*" + documentIndex: 7 - equal: path: spec.gateways[0] value: mds-gateway - documentIndex: 5 - - equal: - path: spec.http[0].match[0].uri.prefix - value: /policy - documentIndex: 5 + documentIndex: 7 - equal: - path: spec.http[0].route[0].destination.port.number - value: 4003 - documentIndex: 5 + path: spec.http[0].match[0].uri.regex + value: "^/jurisdiction($|/.*$)" + documentIndex: 7 - equal: path: spec.http[0].route[0].destination.host - value: mds-policy.mds.svc.cluster.local - documentIndex: 5 + value: mds-jurisdiction.mds.svc.cluster.local + documentIndex: 7 + - equal: + path: spec.http[0].route[0].destination.port.number + value: 4000 + documentIndex: 7 - equal: path: spec.http[0].corsPolicy.allowOrigin[0] value: "*" - documentIndex: 5 + documentIndex: 7 - equal: path: spec.http[0].corsPolicy.allowMethods[0] value: POST - documentIndex: 5 + documentIndex: 7 - equal: path: spec.http[0].corsPolicy.allowMethods[1] value: GET - documentIndex: 5 + documentIndex: 7 - equal: path: spec.http[0].corsPolicy.allowMethods[2] value: HEAD - documentIndex: 5 + documentIndex: 7 - equal: path: spec.http[0].corsPolicy.allowMethods[3] value: OPTIONS - documentIndex: 5 + documentIndex: 7 - equal: path: spec.http[0].corsPolicy.allowMethods[4] value: PATCH - documentIndex: 5 + documentIndex: 7 - equal: path: spec.http[0].corsPolicy.allowMethods[5] value: PUT - documentIndex: 5 + documentIndex: 7 - equal: path: spec.http[0].corsPolicy.allowMethods[6] value: DELETE - documentIndex: 5 + documentIndex: 7 - equal: path: spec.http[0].corsPolicy.allowCredentials value: false - documentIndex: 5 + documentIndex: 7 - equal: path: spec.http[0].corsPolicy.allowHeaders[0] value: Content-Type - documentIndex: 5 + documentIndex: 7 - equal: path: spec.http[0].corsPolicy.allowHeaders[1] value: Authorization - documentIndex: 5 + documentIndex: 7 - equal: path: spec.http[0].corsPolicy.maxAge value: "10m" - documentIndex: 5 + documentIndex: 7 diff --git a/helm/mds/tests/ingress-native-tls-domain_test.yaml b/helm/mds/tests/ingress-native-tls-domain_test.yaml deleted file mode 100644 index d29a3b636..000000000 --- a/helm/mds/tests/ingress-native-tls-domain_test.yaml +++ /dev/null @@ -1,94 +0,0 @@ -suite: test ingress native -templates: - - ingress.yaml -tests: - - it: doc 4 - release: - namespace: mds - set: - tls: - enabled: true - domain: domain - asserts: - - isAPIVersion: - of: networking.istio.io/v1alpha3 - documentIndex: 4 - - isKind: - of: VirtualService - documentIndex: 4 - - equal: - path: metadata.name - value: mds-native-route - documentIndex: 4 - - equal: - path: metadata.namespace - value: mds - documentIndex: 4 - - equal: - path: spec.hosts[0] - value: '*' - documentIndex: 4 - - equal: - path: spec.gateways[0] - value: mds-gateway - documentIndex: 4 - - equal: - path: spec.http[0].match[0].uri.prefix - value: /native - documentIndex: 4 - - equal: - path: spec.http[0].route[0].destination.port.number - value: 4006 - documentIndex: 4 - - equal: - path: spec.http[0].route[0].destination.host - value: mds-native.mds.svc.cluster.local - documentIndex: 4 - - equal: - path: spec.http[0].corsPolicy.allowOrigin[0] - value: '*' - documentIndex: 4 - - equal: - path: spec.http[0].corsPolicy.allowMethods[0] - value: POST - documentIndex: 4 - - equal: - path: spec.http[0].corsPolicy.allowMethods[1] - value: GET - documentIndex: 4 - - equal: - path: spec.http[0].corsPolicy.allowMethods[2] - value: HEAD - documentIndex: 4 - - equal: - path: spec.http[0].corsPolicy.allowMethods[3] - value: OPTIONS - documentIndex: 4 - - equal: - path: spec.http[0].corsPolicy.allowMethods[4] - value: PATCH - documentIndex: 4 - - equal: - path: spec.http[0].corsPolicy.allowMethods[5] - value: PUT - documentIndex: 4 - - equal: - path: spec.http[0].corsPolicy.allowMethods[6] - value: DELETE - documentIndex: 4 - - equal: - path: spec.http[0].corsPolicy.allowCredentials - value: false - documentIndex: 4 - - equal: - path: spec.http[0].corsPolicy.allowHeaders[0] - value: Content-Type - documentIndex: 4 - - equal: - path: spec.http[0].corsPolicy.allowHeaders[1] - value: Authorization - documentIndex: 4 - - equal: - path: spec.http[0].corsPolicy.maxAge - value: '10m' - documentIndex: 4 diff --git a/helm/mds/tests/ingress-native-tls_test.yaml b/helm/mds/tests/ingress-native-tls_test.yaml deleted file mode 100644 index 97153b2de..000000000 --- a/helm/mds/tests/ingress-native-tls_test.yaml +++ /dev/null @@ -1,93 +0,0 @@ -suite: test ingress native -templates: - - ingress.yaml -tests: - - it: doc 4 - release: - namespace: mds - set: - tls: - enabled: true - asserts: - - isAPIVersion: - of: networking.istio.io/v1alpha3 - documentIndex: 4 - - isKind: - of: VirtualService - documentIndex: 4 - - equal: - path: metadata.name - value: mds-native-route - documentIndex: 4 - - equal: - path: metadata.namespace - value: mds - documentIndex: 4 - - equal: - path: spec.hosts[0] - value: '*' - documentIndex: 4 - - equal: - path: spec.gateways[0] - value: mds-gateway - documentIndex: 4 - - equal: - path: spec.http[0].match[0].uri.prefix - value: /native - documentIndex: 4 - - equal: - path: spec.http[0].route[0].destination.port.number - value: 4006 - documentIndex: 4 - - equal: - path: spec.http[0].route[0].destination.host - value: mds-native.mds.svc.cluster.local - documentIndex: 4 - - equal: - path: spec.http[0].corsPolicy.allowOrigin[0] - value: '*' - documentIndex: 4 - - equal: - path: spec.http[0].corsPolicy.allowMethods[0] - value: POST - documentIndex: 4 - - equal: - path: spec.http[0].corsPolicy.allowMethods[1] - value: GET - documentIndex: 4 - - equal: - path: spec.http[0].corsPolicy.allowMethods[2] - value: HEAD - documentIndex: 4 - - equal: - path: spec.http[0].corsPolicy.allowMethods[3] - value: OPTIONS - documentIndex: 4 - - equal: - path: spec.http[0].corsPolicy.allowMethods[4] - value: PATCH - documentIndex: 4 - - equal: - path: spec.http[0].corsPolicy.allowMethods[5] - value: PUT - documentIndex: 4 - - equal: - path: spec.http[0].corsPolicy.allowMethods[6] - value: DELETE - documentIndex: 4 - - equal: - path: spec.http[0].corsPolicy.allowCredentials - value: false - documentIndex: 4 - - equal: - path: spec.http[0].corsPolicy.allowHeaders[0] - value: Content-Type - documentIndex: 4 - - equal: - path: spec.http[0].corsPolicy.allowHeaders[1] - value: Authorization - documentIndex: 4 - - equal: - path: spec.http[0].corsPolicy.maxAge - value: '10m' - documentIndex: 4 diff --git a/helm/mds/tests/ingress-policy-author-domain_test.yaml b/helm/mds/tests/ingress-policy-author-domain_test.yaml new file mode 100644 index 000000000..46d2831b2 --- /dev/null +++ b/helm/mds/tests/ingress-policy-author-domain_test.yaml @@ -0,0 +1,92 @@ +suite: test ingress policy-author domain +templates: + - ingress.yaml +tests: + - it: doc 11 + release: + namespace: mds + set: + domain: my-domain + asserts: + - isAPIVersion: + of: networking.istio.io/v1alpha3 + documentIndex: 11 + - isKind: + of: VirtualService + documentIndex: 11 + - equal: + path: metadata.name + value: mds-policy-author-route + documentIndex: 11 + - equal: + path: metadata.namespace + value: mds + documentIndex: 11 + - equal: + path: spec.hosts[0] + value: "my-domain" + documentIndex: 11 + - equal: + path: spec.gateways[0] + value: mds-gateway + documentIndex: 11 + - equal: + path: spec.http[0].match[0].uri.regex + value: "^/policy-author($|/.*$)" + documentIndex: 11 + - equal: + path: spec.http[0].route[0].destination.host + value: mds-policy-author.mds.svc.cluster.local + documentIndex: 11 + - equal: + path: spec.http[0].route[0].destination.port.number + value: 4000 + documentIndex: 11 + - equal: + path: spec.http[0].corsPolicy.allowOrigin[0] + value: "*" + documentIndex: 11 + - equal: + path: spec.http[0].corsPolicy.allowMethods[0] + value: POST + documentIndex: 11 + - equal: + path: spec.http[0].corsPolicy.allowMethods[1] + value: GET + documentIndex: 11 + - equal: + path: spec.http[0].corsPolicy.allowMethods[2] + value: HEAD + documentIndex: 11 + - equal: + path: spec.http[0].corsPolicy.allowMethods[3] + value: OPTIONS + documentIndex: 11 + - equal: + path: spec.http[0].corsPolicy.allowMethods[4] + value: PATCH + documentIndex: 11 + - equal: + path: spec.http[0].corsPolicy.allowMethods[5] + value: PUT + documentIndex: 11 + - equal: + path: spec.http[0].corsPolicy.allowMethods[6] + value: DELETE + documentIndex: 11 + - equal: + path: spec.http[0].corsPolicy.allowCredentials + value: false + documentIndex: 11 + - equal: + path: spec.http[0].corsPolicy.allowHeaders[0] + value: Content-Type + documentIndex: 11 + - equal: + path: spec.http[0].corsPolicy.allowHeaders[1] + value: Authorization + documentIndex: 11 + - equal: + path: spec.http[0].corsPolicy.maxAge + value: "10m" + documentIndex: 11 diff --git a/helm/mds/tests/ingress-policy-author_test.yaml b/helm/mds/tests/ingress-policy-author_test.yaml new file mode 100644 index 000000000..d1c6195d8 --- /dev/null +++ b/helm/mds/tests/ingress-policy-author_test.yaml @@ -0,0 +1,90 @@ +suite: test ingress policy-author +templates: + - ingress.yaml +tests: + - it: doc 11 + release: + namespace: mds + asserts: + - isAPIVersion: + of: networking.istio.io/v1alpha3 + documentIndex: 11 + - isKind: + of: VirtualService + documentIndex: 11 + - equal: + path: metadata.name + value: mds-policy-author-route + documentIndex: 11 + - equal: + path: metadata.namespace + value: mds + documentIndex: 11 + - equal: + path: spec.hosts[0] + value: "*" + documentIndex: 11 + - equal: + path: spec.gateways[0] + value: mds-gateway + documentIndex: 11 + - equal: + path: spec.http[0].match[0].uri.regex + value: "^/policy-author($|/.*$)" + documentIndex: 11 + - equal: + path: spec.http[0].route[0].destination.host + value: mds-policy-author.mds.svc.cluster.local + documentIndex: 11 + - equal: + path: spec.http[0].route[0].destination.port.number + value: 4000 + documentIndex: 11 + - equal: + path: spec.http[0].corsPolicy.allowOrigin[0] + value: "*" + documentIndex: 11 + - equal: + path: spec.http[0].corsPolicy.allowMethods[0] + value: POST + documentIndex: 11 + - equal: + path: spec.http[0].corsPolicy.allowMethods[1] + value: GET + documentIndex: 11 + - equal: + path: spec.http[0].corsPolicy.allowMethods[2] + value: HEAD + documentIndex: 11 + - equal: + path: spec.http[0].corsPolicy.allowMethods[3] + value: OPTIONS + documentIndex: 11 + - equal: + path: spec.http[0].corsPolicy.allowMethods[4] + value: PATCH + documentIndex: 11 + - equal: + path: spec.http[0].corsPolicy.allowMethods[5] + value: PUT + documentIndex: 11 + - equal: + path: spec.http[0].corsPolicy.allowMethods[6] + value: DELETE + documentIndex: 11 + - equal: + path: spec.http[0].corsPolicy.allowCredentials + value: false + documentIndex: 11 + - equal: + path: spec.http[0].corsPolicy.allowHeaders[0] + value: Content-Type + documentIndex: 11 + - equal: + path: spec.http[0].corsPolicy.allowHeaders[1] + value: Authorization + documentIndex: 11 + - equal: + path: spec.http[0].corsPolicy.maxAge + value: "10m" + documentIndex: 11 diff --git a/helm/mds/tests/ingress-policy-tls-domain_test.yaml b/helm/mds/tests/ingress-policy-domain_test.yaml similarity index 70% rename from helm/mds/tests/ingress-policy-tls-domain_test.yaml rename to helm/mds/tests/ingress-policy-domain_test.yaml index 7d601e3f0..401558dbd 100644 --- a/helm/mds/tests/ingress-policy-tls-domain_test.yaml +++ b/helm/mds/tests/ingress-policy-domain_test.yaml @@ -1,94 +1,92 @@ -suite: test ingress policy +suite: test ingress policy domain templates: - ingress.yaml tests: - - it: doc 5 + - it: doc 10 release: namespace: mds set: - tls: - enabled: true - domain: domain + domain: my-domain asserts: - isAPIVersion: of: networking.istio.io/v1alpha3 - documentIndex: 5 + documentIndex: 10 - isKind: of: VirtualService - documentIndex: 5 + documentIndex: 10 - equal: path: metadata.name value: mds-policy-route - documentIndex: 5 + documentIndex: 10 - equal: path: metadata.namespace value: mds - documentIndex: 5 + documentIndex: 10 - equal: path: spec.hosts[0] - value: '*' - documentIndex: 5 + value: "my-domain" + documentIndex: 10 - equal: path: spec.gateways[0] value: mds-gateway - documentIndex: 5 + documentIndex: 10 - equal: - path: spec.http[0].match[0].uri.prefix - value: /policy - documentIndex: 5 - - equal: - path: spec.http[0].route[0].destination.port.number - value: 4003 - documentIndex: 5 + path: spec.http[0].match[0].uri.regex + value: "^/policy($|/.*$)" + documentIndex: 10 - equal: path: spec.http[0].route[0].destination.host value: mds-policy.mds.svc.cluster.local - documentIndex: 5 + documentIndex: 10 + - equal: + path: spec.http[0].route[0].destination.port.number + value: 4000 + documentIndex: 10 - equal: path: spec.http[0].corsPolicy.allowOrigin[0] value: "*" - documentIndex: 5 + documentIndex: 10 - equal: path: spec.http[0].corsPolicy.allowMethods[0] value: POST - documentIndex: 5 + documentIndex: 10 - equal: path: spec.http[0].corsPolicy.allowMethods[1] value: GET - documentIndex: 5 + documentIndex: 10 - equal: path: spec.http[0].corsPolicy.allowMethods[2] value: HEAD - documentIndex: 5 + documentIndex: 10 - equal: path: spec.http[0].corsPolicy.allowMethods[3] value: OPTIONS - documentIndex: 5 + documentIndex: 10 - equal: path: spec.http[0].corsPolicy.allowMethods[4] value: PATCH - documentIndex: 5 + documentIndex: 10 - equal: path: spec.http[0].corsPolicy.allowMethods[5] value: PUT - documentIndex: 5 + documentIndex: 10 - equal: path: spec.http[0].corsPolicy.allowMethods[6] value: DELETE - documentIndex: 5 + documentIndex: 10 - equal: path: spec.http[0].corsPolicy.allowCredentials value: false - documentIndex: 5 + documentIndex: 10 - equal: path: spec.http[0].corsPolicy.allowHeaders[0] value: Content-Type - documentIndex: 5 + documentIndex: 10 - equal: path: spec.http[0].corsPolicy.allowHeaders[1] value: Authorization - documentIndex: 5 + documentIndex: 10 - equal: path: spec.http[0].corsPolicy.maxAge value: "10m" - documentIndex: 5 + documentIndex: 10 diff --git a/helm/mds/tests/ingress-policy_test.yaml b/helm/mds/tests/ingress-policy_test.yaml index bc4c5b06b..51f33c3bc 100644 --- a/helm/mds/tests/ingress-policy_test.yaml +++ b/helm/mds/tests/ingress-policy_test.yaml @@ -2,89 +2,89 @@ suite: test ingress policy templates: - ingress.yaml tests: - - it: doc 5 + - it: doc 10 release: namespace: mds asserts: - isAPIVersion: of: networking.istio.io/v1alpha3 - documentIndex: 5 + documentIndex: 10 - isKind: of: VirtualService - documentIndex: 5 + documentIndex: 10 - equal: path: metadata.name value: mds-policy-route - documentIndex: 5 + documentIndex: 10 - equal: path: metadata.namespace value: mds - documentIndex: 5 + documentIndex: 10 - equal: path: spec.hosts[0] value: "*" - documentIndex: 5 + documentIndex: 10 - equal: path: spec.gateways[0] value: mds-gateway - documentIndex: 5 + documentIndex: 10 - equal: - path: spec.http[0].match[0].uri.prefix - value: /policy - documentIndex: 5 - - equal: - path: spec.http[0].route[0].destination.port.number - value: 4003 - documentIndex: 5 + path: spec.http[0].match[0].uri.regex + value: "^/policy($|/.*$)" + documentIndex: 10 - equal: path: spec.http[0].route[0].destination.host value: mds-policy.mds.svc.cluster.local - documentIndex: 5 + documentIndex: 10 + - equal: + path: spec.http[0].route[0].destination.port.number + value: 4000 + documentIndex: 10 - equal: path: spec.http[0].corsPolicy.allowOrigin[0] value: "*" - documentIndex: 5 + documentIndex: 10 - equal: path: spec.http[0].corsPolicy.allowMethods[0] value: POST - documentIndex: 5 + documentIndex: 10 - equal: path: spec.http[0].corsPolicy.allowMethods[1] value: GET - documentIndex: 5 + documentIndex: 10 - equal: path: spec.http[0].corsPolicy.allowMethods[2] value: HEAD - documentIndex: 5 + documentIndex: 10 - equal: path: spec.http[0].corsPolicy.allowMethods[3] value: OPTIONS - documentIndex: 5 + documentIndex: 10 - equal: path: spec.http[0].corsPolicy.allowMethods[4] value: PATCH - documentIndex: 5 + documentIndex: 10 - equal: path: spec.http[0].corsPolicy.allowMethods[5] value: PUT - documentIndex: 5 + documentIndex: 10 - equal: path: spec.http[0].corsPolicy.allowMethods[6] value: DELETE - documentIndex: 5 + documentIndex: 10 - equal: path: spec.http[0].corsPolicy.allowCredentials value: false - documentIndex: 5 + documentIndex: 10 - equal: path: spec.http[0].corsPolicy.allowHeaders[0] value: Content-Type - documentIndex: 5 + documentIndex: 10 - equal: path: spec.http[0].corsPolicy.allowHeaders[1] value: Authorization - documentIndex: 5 + documentIndex: 10 - equal: path: spec.http[0].corsPolicy.maxAge value: "10m" - documentIndex: 5 + documentIndex: 10 diff --git a/helm/mds/tests/ingress-web-sockets-domain_test.yaml b/helm/mds/tests/ingress-web-sockets-domain_test.yaml new file mode 100644 index 000000000..3e6a5a770 --- /dev/null +++ b/helm/mds/tests/ingress-web-sockets-domain_test.yaml @@ -0,0 +1,92 @@ +suite: test ingress web-sockets domain +templates: + - ingress.yaml +tests: + - it: doc 12 + release: + namespace: mds + set: + domain: my-domain + asserts: + - isAPIVersion: + of: networking.istio.io/v1alpha3 + documentIndex: 12 + - isKind: + of: VirtualService + documentIndex: 12 + - equal: + path: metadata.name + value: mds-web-sockets-route + documentIndex: 12 + - equal: + path: metadata.namespace + value: mds + documentIndex: 12 + - equal: + path: spec.hosts[0] + value: "my-domain" + documentIndex: 12 + - equal: + path: spec.gateways[0] + value: mds-gateway + documentIndex: 12 + - equal: + path: spec.http[0].match[0].uri.regex + value: "^/web-sockets($|/.*$)" + documentIndex: 12 + - equal: + path: spec.http[0].route[0].destination.host + value: mds-web-sockets.mds.svc.cluster.local + documentIndex: 12 + - equal: + path: spec.http[0].route[0].destination.port.number + value: 4000 + documentIndex: 12 + - equal: + path: spec.http[0].corsPolicy.allowOrigin[0] + value: "*" + documentIndex: 12 + - equal: + path: spec.http[0].corsPolicy.allowMethods[0] + value: POST + documentIndex: 12 + - equal: + path: spec.http[0].corsPolicy.allowMethods[1] + value: GET + documentIndex: 12 + - equal: + path: spec.http[0].corsPolicy.allowMethods[2] + value: HEAD + documentIndex: 12 + - equal: + path: spec.http[0].corsPolicy.allowMethods[3] + value: OPTIONS + documentIndex: 12 + - equal: + path: spec.http[0].corsPolicy.allowMethods[4] + value: PATCH + documentIndex: 12 + - equal: + path: spec.http[0].corsPolicy.allowMethods[5] + value: PUT + documentIndex: 12 + - equal: + path: spec.http[0].corsPolicy.allowMethods[6] + value: DELETE + documentIndex: 12 + - equal: + path: spec.http[0].corsPolicy.allowCredentials + value: false + documentIndex: 12 + - equal: + path: spec.http[0].corsPolicy.allowHeaders[0] + value: Content-Type + documentIndex: 12 + - equal: + path: spec.http[0].corsPolicy.allowHeaders[1] + value: Authorization + documentIndex: 12 + - equal: + path: spec.http[0].corsPolicy.maxAge + value: "10m" + documentIndex: 12 diff --git a/helm/mds/tests/ingress-web-sockets_test.yaml b/helm/mds/tests/ingress-web-sockets_test.yaml new file mode 100644 index 000000000..eee100d22 --- /dev/null +++ b/helm/mds/tests/ingress-web-sockets_test.yaml @@ -0,0 +1,90 @@ +suite: test ingress web-sockets +templates: + - ingress.yaml +tests: + - it: doc 12 + release: + namespace: mds + asserts: + - isAPIVersion: + of: networking.istio.io/v1alpha3 + documentIndex: 12 + - isKind: + of: VirtualService + documentIndex: 12 + - equal: + path: metadata.name + value: mds-web-sockets-route + documentIndex: 12 + - equal: + path: metadata.namespace + value: mds + documentIndex: 12 + - equal: + path: spec.hosts[0] + value: "*" + documentIndex: 12 + - equal: + path: spec.gateways[0] + value: mds-gateway + documentIndex: 12 + - equal: + path: spec.http[0].match[0].uri.regex + value: "^/web-sockets($|/.*$)" + documentIndex: 12 + - equal: + path: spec.http[0].route[0].destination.host + value: mds-web-sockets.mds.svc.cluster.local + documentIndex: 12 + - equal: + path: spec.http[0].route[0].destination.port.number + value: 4000 + documentIndex: 12 + - equal: + path: spec.http[0].corsPolicy.allowOrigin[0] + value: "*" + documentIndex: 12 + - equal: + path: spec.http[0].corsPolicy.allowMethods[0] + value: POST + documentIndex: 12 + - equal: + path: spec.http[0].corsPolicy.allowMethods[1] + value: GET + documentIndex: 12 + - equal: + path: spec.http[0].corsPolicy.allowMethods[2] + value: HEAD + documentIndex: 12 + - equal: + path: spec.http[0].corsPolicy.allowMethods[3] + value: OPTIONS + documentIndex: 12 + - equal: + path: spec.http[0].corsPolicy.allowMethods[4] + value: PATCH + documentIndex: 12 + - equal: + path: spec.http[0].corsPolicy.allowMethods[5] + value: PUT + documentIndex: 12 + - equal: + path: spec.http[0].corsPolicy.allowMethods[6] + value: DELETE + documentIndex: 12 + - equal: + path: spec.http[0].corsPolicy.allowCredentials + value: false + documentIndex: 12 + - equal: + path: spec.http[0].corsPolicy.allowHeaders[0] + value: Content-Type + documentIndex: 12 + - equal: + path: spec.http[0].corsPolicy.allowHeaders[1] + value: Authorization + documentIndex: 12 + - equal: + path: spec.http[0].corsPolicy.maxAge + value: "10m" + documentIndex: 12 diff --git a/helm/mds/tests/service-agency_test.yaml b/helm/mds/tests/service-agency_test.yaml index 38fae509d..27e396b13 100644 --- a/helm/mds/tests/service-agency_test.yaml +++ b/helm/mds/tests/service-agency_test.yaml @@ -30,5 +30,5 @@ tests: documentIndex: 0 - equal: path: spec.ports[0].port - value: 4001 + value: 4000 documentIndex: 0 \ No newline at end of file diff --git a/helm/mds/tests/service-all-disabled_test.yaml b/helm/mds/tests/service-all-disabled_test.yaml new file mode 100644 index 000000000..9630a36e1 --- /dev/null +++ b/helm/mds/tests/service-all-disabled_test.yaml @@ -0,0 +1,30 @@ +suite: test deployment al enabled false +templates: + - service.yaml +tests: + - it: all docs + release: + namespace: mds + set: + apis: + mds-agency: + enabled: false + mds-audit: + enabled: false + mds-policy: + enabled: false + mds-compliance: + enabled: false + mds-daily: + enabled: false + mds-jurisdiction: + enabled: false + mds-policy-author: + enabled: false + mds-web-sockets: + enabled: false + mds-geography-author: + enabled: false + asserts: + - hasDocuments: + count: 0 diff --git a/helm/mds/tests/service-all-default_test.yaml b/helm/mds/tests/service-all_test.yaml similarity index 89% rename from helm/mds/tests/service-all-default_test.yaml rename to helm/mds/tests/service-all_test.yaml index 31573b3b2..81a45eef7 100644 --- a/helm/mds/tests/service-all-default_test.yaml +++ b/helm/mds/tests/service-all_test.yaml @@ -7,4 +7,4 @@ tests: namespace: mds asserts: - hasDocuments: - count: 8 \ No newline at end of file + count: 13 \ No newline at end of file diff --git a/helm/mds/tests/service-audit_test.yaml b/helm/mds/tests/service-audit_test.yaml index 2c6afce25..d10a4fc9f 100644 --- a/helm/mds/tests/service-audit_test.yaml +++ b/helm/mds/tests/service-audit_test.yaml @@ -30,5 +30,5 @@ tests: documentIndex: 1 - equal: path: spec.ports[0].port - value: 4002 + value: 4000 documentIndex: 1 diff --git a/helm/mds/tests/service-compliance_test.yaml b/helm/mds/tests/service-compliance_test.yaml index cf7c4050d..3952e4d9f 100644 --- a/helm/mds/tests/service-compliance_test.yaml +++ b/helm/mds/tests/service-compliance_test.yaml @@ -30,5 +30,5 @@ tests: documentIndex: 2 - equal: path: spec.ports[0].port - value: 4004 + value: 4000 documentIndex: 2 diff --git a/helm/mds/tests/service-daily_test.yaml b/helm/mds/tests/service-daily_test.yaml index 09944f9f9..8b822b9fb 100644 --- a/helm/mds/tests/service-daily_test.yaml +++ b/helm/mds/tests/service-daily_test.yaml @@ -2,33 +2,33 @@ suite: test service daily templates: - service.yaml tests: - - it: doc 3 + - it: doc 4 release: namespace: mds asserts: - isAPIVersion: of: v1 - documentIndex: 3 + documentIndex: 4 - isKind: of: Service - documentIndex: 3 + documentIndex: 4 - equal: path: metadata.name value: mds-daily - documentIndex: 3 + documentIndex: 4 - equal: path: metadata.namespace value: mds - documentIndex: 3 + documentIndex: 4 - equal: path: spec.selector.app value: mds-daily - documentIndex: 3 + documentIndex: 4 - equal: path: spec.ports[0].name value: http-mds-daily - documentIndex: 3 + documentIndex: 4 - equal: path: spec.ports[0].port - value: 4005 - documentIndex: 3 + value: 4000 + documentIndex: 4 diff --git a/helm/mds/tests/service-geography-author_test.yaml b/helm/mds/tests/service-geography-author_test.yaml new file mode 100644 index 000000000..6438a1a00 --- /dev/null +++ b/helm/mds/tests/service-geography-author_test.yaml @@ -0,0 +1,34 @@ +suite: test service geography-author +templates: + - service.yaml +tests: + - it: doc 6 + release: + namespace: mds + asserts: + - isAPIVersion: + of: v1 + documentIndex: 6 + - isKind: + of: Service + documentIndex: 6 + - equal: + path: metadata.name + value: mds-geography-author + documentIndex: 6 + - equal: + path: metadata.namespace + value: mds + documentIndex: 6 + - equal: + path: spec.selector.app + value: mds-geography-author + documentIndex: 6 + - equal: + path: spec.ports[0].name + value: http-mds-geography-author + documentIndex: 6 + - equal: + path: spec.ports[0].port + value: 4000 + documentIndex: 6 diff --git a/helm/mds/tests/service-native_test.yaml b/helm/mds/tests/service-jurisdiction_test.yaml similarity index 54% rename from helm/mds/tests/service-native_test.yaml rename to helm/mds/tests/service-jurisdiction_test.yaml index 55af30e5f..cb4cbe3f2 100644 --- a/helm/mds/tests/service-native_test.yaml +++ b/helm/mds/tests/service-jurisdiction_test.yaml @@ -1,34 +1,34 @@ -suite: test service native +suite: test service jurisdiction templates: - service.yaml tests: - - it: doc 4 + - it: doc 7 release: namespace: mds asserts: - isAPIVersion: of: v1 - documentIndex: 4 + documentIndex: 7 - isKind: of: Service - documentIndex: 4 + documentIndex: 7 - equal: path: metadata.name - value: mds-native - documentIndex: 4 + value: mds-jurisdiction + documentIndex: 7 - equal: path: metadata.namespace value: mds - documentIndex: 4 + documentIndex: 7 - equal: path: spec.selector.app - value: mds-native - documentIndex: 4 + value: mds-jurisdiction + documentIndex: 7 - equal: path: spec.ports[0].name - value: http-mds-native - documentIndex: 4 + value: http-mds-jurisdiction + documentIndex: 7 - equal: path: spec.ports[0].port - value: 4006 - documentIndex: 4 + value: 4000 + documentIndex: 7 diff --git a/helm/mds/tests/service-policy-author_test.yaml b/helm/mds/tests/service-policy-author_test.yaml index aee940fb7..4d1f34b4f 100644 --- a/helm/mds/tests/service-policy-author_test.yaml +++ b/helm/mds/tests/service-policy-author_test.yaml @@ -2,33 +2,33 @@ suite: test service policy-author templates: - service.yaml tests: - - it: doc 6 + - it: doc 11 release: namespace: mds asserts: - isAPIVersion: of: v1 - documentIndex: 6 + documentIndex: 11 - isKind: of: Service - documentIndex: 6 + documentIndex: 11 - equal: path: metadata.name value: mds-policy-author - documentIndex: 6 + documentIndex: 11 - equal: path: metadata.namespace value: mds - documentIndex: 6 + documentIndex: 11 - equal: path: spec.selector.app value: mds-policy-author - documentIndex: 6 + documentIndex: 11 - equal: path: spec.ports[0].name value: http-mds-policy-author - documentIndex: 6 + documentIndex: 11 - equal: path: spec.ports[0].port - value: 4007 - documentIndex: 6 + value: 4000 + documentIndex: 11 diff --git a/helm/mds/tests/service-policy_test.yaml b/helm/mds/tests/service-policy_test.yaml index f8cf021c9..5f88cf523 100644 --- a/helm/mds/tests/service-policy_test.yaml +++ b/helm/mds/tests/service-policy_test.yaml @@ -2,33 +2,33 @@ suite: test service policy templates: - service.yaml tests: - - it: doc 5 + - it: doc 10 release: namespace: mds asserts: - isAPIVersion: of: v1 - documentIndex: 5 + documentIndex: 10 - isKind: of: Service - documentIndex: 5 + documentIndex: 10 - equal: path: metadata.name value: mds-policy - documentIndex: 5 + documentIndex: 10 - equal: path: metadata.namespace value: mds - documentIndex: 5 + documentIndex: 10 - equal: path: spec.selector.app value: mds-policy - documentIndex: 5 + documentIndex: 10 - equal: path: spec.ports[0].name value: http-mds-policy - documentIndex: 5 + documentIndex: 10 - equal: path: spec.ports[0].port - value: 4003 - documentIndex: 5 + value: 4000 + documentIndex: 10 diff --git a/helm/mds/tests/service-web-sockets_test.yaml b/helm/mds/tests/service-web-sockets_test.yaml new file mode 100644 index 000000000..e62be2d1f --- /dev/null +++ b/helm/mds/tests/service-web-sockets_test.yaml @@ -0,0 +1,34 @@ +suite: test service web-sockets +templates: + - service.yaml +tests: + - it: doc 12 + release: + namespace: mds + asserts: + - isAPIVersion: + of: v1 + documentIndex: 12 + - isKind: + of: Service + documentIndex: 12 + - equal: + path: metadata.name + value: mds-web-sockets + documentIndex: 12 + - equal: + path: metadata.namespace + value: mds + documentIndex: 12 + - equal: + path: spec.selector.app + value: mds-web-sockets + documentIndex: 12 + - equal: + path: spec.ports[0].name + value: http-mds-web-sockets + documentIndex: 12 + - equal: + path: spec.ports[0].port + value: 4000 + documentIndex: 12 diff --git a/helm/mds/values.yaml b/helm/mds/values.yaml index e478d91b0..99bfe2f85 100644 --- a/helm/mds/values.yaml +++ b/helm/mds/values.yaml @@ -24,26 +24,52 @@ apis: pathPrefix: /daily version: latest migration: false - mds-native: + mds-jurisdiction: enabled: true - pathPrefix: /native + pathPrefix: /jurisdiction version: latest + migration: false + mds-jurisdiction-service: + enabled: true + pathPrefix: /jurisdiction-service + version: latest + migration: false mds-policy-author: enabled: true pathPrefix: /policy-author version: latest migration: false -useEvents: false + mds-web-sockets: + enabled: true + pathPrefix: /web-sockets + version: latest + migration: false + mds-geography: + enabled: true + pathPrefix: /geography + version: latest + migration: false + mds-geography-author: + enabled: true + pathPrefix: /geography-author + version: latest + migration: false +nats: + enabled: true + credentials: false +tenantId: timezone: America/Los_Angeles registry: useLatestImage: true -domain: '*' +domain: "*" tls: enabled: false domain: jwt: issuer: enabled: false + bypassInternal: false + firstInstall: false audiences: [] postgresql: internal: true diff --git a/helm/mds/values.yaml.tpl b/helm/mds/values.yaml.tpl index 0d26538e2..b4c29c5f3 100644 --- a/helm/mds/values.yaml.tpl +++ b/helm/mds/values.yaml.tpl @@ -24,13 +24,33 @@ apis: pathPrefix: /daily version: ${DAILY_VERSION} migration: false - mds-native: + mds-jurisdiction: enabled: true - pathPrefix: /native - version: ${NATIVE_VERSION} + pathPrefix: /jurisdiction + version: ${JURISDICTION_VERSION} + migration: false + mds-jurisdiction-service: + enabled: true + pathPrefix: /jurisdiction-service + version: ${JURISDICTION_SERVICE_VERSION} migration: false mds-policy-author: enabled: true pathPrefix: /policy-author version: ${POLICY_AUTHOR_VERSION} migration: false + mds-web-sockets: + enabled: true + pathPrefix: /web-sockets + version: ${WEB_SOCKETS_VERSION} + migration: false + mds-geography: + enabled: true + pathPrefix: /geography + version: ${GEOGRAPHY_VERSION} + migration: false + mds-geography-author: + enabled: true + pathPrefix: /geography-author + version: ${GEOGRAPHY_AUTHOR_VERSION} + migration: false \ No newline at end of file diff --git a/helm/monitoring/Chart.yaml b/helm/monitoring/Chart.yaml new file mode 100644 index 000000000..f2fc806d2 --- /dev/null +++ b/helm/monitoring/Chart.yaml @@ -0,0 +1,5 @@ +apiVersion: v1 +appVersion: "1.0" +description: A Helm chart for Kubernetes +name: monitoring +version: 0.1.0 diff --git a/helm/monitoring/requirements.lock b/helm/monitoring/requirements.lock new file mode 100644 index 000000000..ca7b2d5c5 --- /dev/null +++ b/helm/monitoring/requirements.lock @@ -0,0 +1,9 @@ +dependencies: +- name: prometheus + repository: https://kubernetes-charts.storage.googleapis.com + version: 10.5.3 +- name: grafana + repository: https://kubernetes-charts.storage.googleapis.com + version: 5.0.5 +digest: sha256:76e37f93849f6550b8d83d2a73da68351201a4b10ec52d26161bcd30a3d16353 +generated: "2020-03-04T09:53:49.7248559-08:00" diff --git a/helm/monitoring/requirements.yaml b/helm/monitoring/requirements.yaml new file mode 100644 index 000000000..dd866aab1 --- /dev/null +++ b/helm/monitoring/requirements.yaml @@ -0,0 +1,7 @@ +dependencies: +- name: prometheus + version: 10.5.3 + repository: alias:stable +- name: grafana + version: 5.0.5 + repository: alias:stable diff --git a/helm/monitoring/values.yaml b/helm/monitoring/values.yaml new file mode 100644 index 000000000..51e9d38d2 --- /dev/null +++ b/helm/monitoring/values.yaml @@ -0,0 +1,221 @@ +prometheus: + + serverFiles: + alerting_rules.yml: + groups: + + - name: Nodes + rules: + + - alert: NodeMemoryWarn + expr: node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes * 100 < 20 + for: 5m + labels: + severity: warning + annotations: + identifier: "{{ $labels.kubernetes_node }}" + description: "Low memory on node ({{ $value }}% available)" + + - alert: NodeMemoryCritical + expr: node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes * 100 < 10 + for: 5m + labels: + severity: critical + annotations: + identifier: "{{ $labels.kubernetes_node }}" + description: "Low memory on node ({{ $value }}% available)" + + - alert: NodeDiskWarn + expr: predict_linear(node_filesystem_free_bytes[1h], 4 * 3600) < 0 + for: 5m + labels: + severity: warn + annotations: + identifier: "{{ $labels.kubernetes_node }}" + description: "Disk predicted to fill in under 4 hours" + + - alert: NodeInodesWarn + expr: predict_linear(node_filesystem_files_free{mountpoint ="/"}[1h], 4 * 3600) < 0 + for: 5m + labels: + severity: warn + annotations: + identifier: "{{ $labels.kubernetes_node }}" + description: "Disk predicted to run out of inodes in under 4 hours" + + - alert: NodeCpuLoadWarn + expr: 100 - (avg by(instance) (irate(node_cpu_seconds_total{mode="idle"}[5m])) * 100) > 80 + for: 5m + labels: + severity: warning + annotations: + identifier: "{{ $labels.kubernetes_node }}" + description: "High CPU usage on node ({{ $value }}%)" + + - alert: NodeCpuLoadCritical + expr: 100 - (avg by(instance) (irate(node_cpu_seconds_total{mode="idle"}[5m])) * 100) > 90 + for: 5m + labels: + severity: warning + annotations: + identifier: "{{ $labels.kubernetes_node }}" + description: "High CPU usage on node ({{ $value }}%)" + + - name: Pods + rules: + + - alert: PersistentVolumeDiskWarning + expr: predict_linear(kubelet_volume_stats_available_bytes[1h], 4 * 3600) < 0 + for: 5m + labels: + severity: warning + annotations: + identifier: "{{ $labels.namespace }}/{{ $labels.persistenvolumeclaim }}" + description: "Persistent volume predicted to run out of space in under 4 hours" + + - alert: StatefulsetDown + expr: (kube_statefulset_status_replicas_current - kube_statefulset_status_replicas_ready) != 0 + for: 5m + labels: + severity: error + annotations: + identifier: "{{ $labels.namespace }}/{{ $labels.statefulset }}" + description: "StatefulSet failure - {{ $value }} member(s) down" + + - alert: PodRestartsWarning + expr: round(rate(kube_pod_container_status_restarts_total[5m]) * 300) > 5 + for: 5m + labels: + severity: warning + annotations: + identifier: "{{ $labels.namespace }}/{{ $labels.pod }} " + description: "Pod container restarted {{ $value }} times in last 5 minutes" + + - alert: PodContainerStatusWaiting + expr: kube_pod_container_status_waiting_reason == 1 + for: 10m + labels: + severity: error + annotations: + identifier: "{{ $labels.namespace }}/{{ $labels.pod }} " + description: "Pod waiting in state {{ $labels.reason }}" + + - alert: PodContainerMemoryUsageAboveLimit + expr: |- + label_replace( + label_replace( + kube_pod_container_resource_limits_memory_bytes{}, + "pod_name", "$1", "pod", "(.+)" + ), + "container_name", "$1", "container", "(.+)" + ) + < + on(pod_name,namespace,container_name) + avg( + container_memory_usage_bytes{pod_name=~".+"} + ) + by (pod_name,namespace,container_name) + for: 5m + labels: + severity: error + annotations: + identifier: "{{ $labels.namespace }}/{{ $labels.pod }} " + description: "Pod exceeded memory limit: {{ $value }} bytes" + + - alert: PodContainerCPUCoresAboveLimit + expr: |- + label_replace( + label_replace( + kube_pod_container_resource_limits_cpu_cores{}, + "pod_name", "$1", "pod", "(.+)" + ), + "container_name", "$1", "container", "(.+)" + ) + < + on(pod_name,namespace,container_name) + avg( + rate(container_cpu_user_seconds_total{pod_name=~".+"}[5m]) + ) + by (pod_name,namespace,container_name) + for: 5m + labels: + severity: error + annotations: + identifier: "{{ $labels.namespace }}/{{ $labels.pod }} " + description: "Pod exceeded memory limit: {{ $value }} bytes" + + alertmanagerFiles: + notifications.tmpl: | + {{ define "__single_message_title" }}{{ range .Alerts.Firing }}{{ .Labels.alertname }} @ {{ .Annotations.identifier }}{{ end }}{{ range .Alerts.Resolved }}{{ .Labels.alertname }} @ {{ .Annotations.identifier }}{{ end }}{{ end }} + {{ define "custom_title" }}[{{ .Status | toUpper }}{{ if eq .Status "firing" }}:{{ .Alerts.Firing | len }}{{ end }}] {{ if or (and (eq (len .Alerts.Firing) 1) (eq (len .Alerts.Resolved) 0)) (and (eq (len .Alerts.Firing) 0) (eq (len .Alerts.Resolved) 1)) }}{{ template "__single_message_title" . }}{{ end }}{{ end }} + {{ define "custom_slack_message" }} + {{ if or (and (eq (len .Alerts.Firing) 1) (eq (len .Alerts.Resolved) 0)) (and (eq (len .Alerts.Firing) 0) (eq (len .Alerts.Resolved) 1)) }} + {{ range .Alerts.Firing }}{{ .Annotations.description }}{{ end }}{{ range .Alerts.Resolved }}{{ .Annotations.description }}{{ end }} + {{ else }} + {{ if gt (len .Alerts.Firing) 0 }} + *Alerts Firing:* + {{ range .Alerts.Firing }}- {{ .Annotations.identifier }}: {{ .Annotations.description }} + {{ end }}{{ end }} + {{ if gt (len .Alerts.Resolved) 0 }} + *Alerts Resolved:* + {{ range .Alerts.Resolved }}- {{ .Annotations.identifier }}: {{ .Annotations.description }} + {{ end }}{{ end }} + {{ end }} + {{ end }} + + + alertmanager.yml: + + global: + slack_api_url: "https://hooks.slack.com/services/REDACTED/REDACTED/REDACTED" + + templates: + - /etc/config/notifications.tmpl + + receivers: + - name: default-receiver + slack_configs: + - channel: '#k8s-alerts' + send_resolved: false + +grafana: + + env: + GF_AUTH_ANONYMOUS_ENABLED: "true" + GF_AUTH_ANONYMOUS_ORG_ROLE: "Admin" + GF_AUTH_DISABLE_LOGIN_FORM: "true" + + datasources: + datasources.yaml: + apiVersion: 1 + datasources: + - name: Prometheus + type: prometheus + # FIXME: hard-coded {{.Release.Name }} as the prefix here + url: http://monitoring-prometheus-server + access: proxy + isDefault: true + + dashboardProviders: + dashboardproviders.yaml: + apiVersion: 1 + providers: + - name: 'default' + orgId: 1 + folder: '' + type: file + disableDeletion: true + editable: false + options: + path: /var/lib/grafana/dashboards/default + + dashboards: + default: + node-exporter: + gnetId: 11074 + revision: 2 + datasource: Prometheus + kubernetes-metrics: + gnetId: 8588 + revision: 1 + datasource: Prometheus diff --git a/helm/nats-account-server/Chart.yaml b/helm/nats-account-server/Chart.yaml new file mode 100644 index 000000000..91f2adccb --- /dev/null +++ b/helm/nats-account-server/Chart.yaml @@ -0,0 +1,5 @@ +apiVersion: v1 +appVersion: '0.4.0' +description: NATS Account Server +name: nats-account-server +version: 0.8.4 diff --git a/helm/nats-account-server/templates/nats-account-server.yaml b/helm/nats-account-server/templates/nats-account-server.yaml new file mode 100644 index 000000000..6b25872e5 --- /dev/null +++ b/helm/nats-account-server/templates/nats-account-server.yaml @@ -0,0 +1,73 @@ +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: nats-account-server + namespace: {{ $.Release.Namespace | default "default" }} + labels: + app: nats-account-server + annotations: + sidecar.istio.io/inject: 'false' +spec: + replicas: 1 + selector: + matchLabels: + app: nats-account-server + template: + metadata: + labels: + app: nats-account-server + spec: + initContainers: + - name: nats-operator-data + image: busybox:1.28 + command: + - 'unzip' + - '/tmp/nats-operator-data.zip' + - '-d' + - '/nsc-operator' + volumeMounts: + - mountPath: /nsc-operator + name: nsc-operator + - mountPath: /tmp + name: nsc-operator-data + containers: + - name: nats-account-server + image: synadia/nats-account-server:0.8.4 + imagePullPolicy: Always + resources: + limits: + cpu: {{ $.Values.resourcesLimitsCpu | default "500m" }} + memory: {{ $.Values.resourcesLimitsMemory | default "512Mi" }} + requests: + cpu: {{ $.Values.resourcesRequestsCpu | default "50m" }} + memory: {{ $.Values.resourcesRequestsMemory | default "128Mi" }} + volumeMounts: + - name: nsc-operator + mountPath: /nsc-operator + command: + - 'nats-account-server' + - '-DV' + - '-hp' + - '0.0.0.0:9090' + - '-nsc' + - '/nsc-operator/KO' + volumes: + - name: nsc-operator + emptyDir: {} + - name: nsc-operator-data + configMap: + name: nats-operator-data +--- +apiVersion: v1 +kind: Service +metadata: + name: nats-account-server + namespace: {{ $.Release.Namespace | default "default" }} +spec: + selector: + app: nats-account-server + ports: + - name: http-nats-account-server + port: 80 + targetPort: 9090 \ No newline at end of file diff --git a/helm/nats-box/Chart.yaml b/helm/nats-box/Chart.yaml new file mode 100644 index 000000000..dd8f58f28 --- /dev/null +++ b/helm/nats-box/Chart.yaml @@ -0,0 +1,5 @@ +apiVersion: v1 +appVersion: '0.4.0' +description: NATS Box +name: nats-box +version: 0.0.1 diff --git a/helm/nats-box/templates/nats-box.yaml b/helm/nats-box/templates/nats-box.yaml new file mode 100644 index 000000000..0478b7123 --- /dev/null +++ b/helm/nats-box/templates/nats-box.yaml @@ -0,0 +1,41 @@ +apiVersion: v1 +kind: Pod +metadata: + name: nats-box + namespace: {{ $.Release.Namespace | default "default" }} + labels: + app: nats-box + annotations: + sidecar.istio.io/inject: 'false' +spec: + {{- if false }} + volumes: + - name: creds-volume + secret: + secretName: nats-creds + {{- end }} + containers: + - name: nats-box + image: synadia/nats-box:0.1.0 + imagePullPolicy: Always + resources: + limits: + cpu: {{ $.Values.resourcesLimitsCpu | default "500m" }} + memory: {{ $.Values.resourcesLimitsMemory | default "512Mi" }} + requests: + cpu: {{ $.Values.resourcesRequestsCpu | default "50m" }} + memory: {{ $.Values.resourcesRequestsMemory | default "128Mi" }} + env: + - name: NATS_URL + value: nats-cluster-mgmt.{{ default "nats" $.Values.natsNamespace }}.svc.cluster.local:4222 + - name: STAN_CLUSTER + value: nats-streaming + command: + - 'tail' + - '-f' + - '/dev/null' + {{- if false }} + volumeMounts: + - name: creds-volume + mountPath: /var/run/nats/secrets + {{- end }} diff --git a/helm/nats-init/Chart.yaml b/helm/nats-init/Chart.yaml new file mode 100644 index 000000000..f9080c968 --- /dev/null +++ b/helm/nats-init/Chart.yaml @@ -0,0 +1,18 @@ +name: nats-operator-init +version: 0.1.2 +appVersion: 0.4.4 +description: Nats operator creates/configures/manages nats clusters atop Kubernetes +keywords: +- addressing +- discovery +- messaging +- nats +- operator +- pubsub +home: https://github.com/nats-io/nats-operator +sources: +- https://github.com/nats-io/nats-operator +maintainers: +- name: richerlariviere + email: richerlariviere@gmail.com +icon: https://bitnami.com/assets/stacks/nats/img/nats-stack-110x117.png diff --git a/helm/nats-init/templates/customresourcedefinition.yaml b/helm/nats-init/templates/customresourcedefinition.yaml new file mode 100644 index 000000000..80b2ac749 --- /dev/null +++ b/helm/nats-init/templates/customresourcedefinition.yaml @@ -0,0 +1,35 @@ +apiVersion: apiextensions.k8s.io/v1beta1 +kind: CustomResourceDefinition +metadata: + name: natsclusters.nats.io + annotations: + "helm.sh/hook": "crd-install" + "helm.sh/hook-delete-policy": "before-hook-creation" +spec: + group: nats.io + names: + kind: NatsCluster + listKind: NatsClusterList + plural: natsclusters + shortNames: + - nats + singular: natscluster + scope: Namespaced + version: v1alpha2 +--- +apiVersion: apiextensions.k8s.io/v1beta1 +kind: CustomResourceDefinition +metadata: + name: natsserviceroles.nats.io + annotations: + "helm.sh/hook": "crd-install" + "helm.sh/hook-delete-policy": "before-hook-creation" +spec: + group: nats.io + names: + kind: NatsServiceRole + listKind: NatsServiceRoleList + plural: natsserviceroles + singular: natsservicerole + scope: Namespaced + version: v1alpha2 diff --git a/helm/nats-init/templates/rbac.yaml b/helm/nats-init/templates/rbac.yaml new file mode 100644 index 000000000..7dee4e3dc --- /dev/null +++ b/helm/nats-init/templates/rbac.yaml @@ -0,0 +1,108 @@ +{{- if .Values.rbacEnabled }} +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: nats-io:nats-operator-crd +rules: +# Allow creating CRDs +- apiGroups: + - apiextensions.k8s.io + resources: + - customresourcedefinitions + verbs: ["get", "list", "create", "update", "watch"] +# Allow all actions on NatsClusters +- apiGroups: + - nats.io + resources: + - natsclusters + - natsserviceroles + verbs: ["*"] +# Allowed actions on Pods +- apiGroups: [""] + resources: + - pods + verbs: ["create", "watch", "get", "patch", "update", "delete", "list"] +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: nats-io:nats-operator-crd-binding +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: nats-io:nats-operator-crd +subjects: +- kind: ServiceAccount + name: nats-operator + namespace: {{ .Release.Namespace }} +--- +apiVersion: rbac.authorization.k8s.io/v1 +{{- if .Values.clusterScoped }} +kind: ClusterRole +{{- else }} +kind: Role +{{- end }} +metadata: + name: nats-io:nats-operator +rules: +# Allowed actions on Pods +- apiGroups: [""] + resources: + - pods + verbs: ["create", "watch", "get", "patch", "update", "delete", "list"] + +# Allowed actions on Services +- apiGroups: [""] + resources: + - services + verbs: ["create", "watch", "get", "patch", "update", "delete", "list"] + +# Allowed actions on Secrets +- apiGroups: [""] + resources: + - secrets + verbs: ["create", "watch", "get", "update", "delete", "list"] + +# Allow all actions on some special subresources +- apiGroups: [""] + resources: + - pods/exec + - pods/log + - serviceaccounts/token + - events + verbs: ["*"] + +# Allow listing Namespaces and ServiceAccounts +- apiGroups: [""] + resources: + - namespaces + - serviceaccounts + verbs: ["list", "get", "watch"] + +# Allow actions on Endpoints +- apiGroups: [""] + resources: + - endpoints + verbs: ["create", "watch", "get", "update", "delete", "list"] +--- +apiVersion: rbac.authorization.k8s.io/v1 +{{- if .Values.clusterScoped }} +kind: ClusterRoleBinding +{{- else }} +kind: RoleBinding +{{- end }} +metadata: + name: nats-io:nats-operator-binding +roleRef: + apiGroup: rbac.authorization.k8s.io + {{- if .Values.clusterScoped }} + kind: ClusterRole + {{- else }} + kind: Role + {{- end }} + name: nats-io:nats-operator +subjects: +- kind: ServiceAccount + name: nats-operator + namespace: {{ .Release.Namespace }} +{{- end }} diff --git a/helm/nats-init/values.yaml b/helm/nats-init/values.yaml new file mode 100644 index 000000000..26d788749 --- /dev/null +++ b/helm/nats-init/values.yaml @@ -0,0 +1,184 @@ +## Specify if RBAC authorization is enabled. +## ref: https://kubernetes.io/docs/reference/access-authn-authz/rbac/ +## +rbacEnabled: true + +## Operator scope +## NOTE: If true +## * Make sure that no othe NATS operator is running in the cluster +## * The Release namespace must be "nats-io" +clusterScoped: false + +image: +# connecteverything/nats-operator:0.2.3-v1alpha2 + registry: docker.io + repository: connecteverything/nats-operator + tag: 0.6.0 + ## Specify a imagePullPolicy + ## Defaults to 'Always' if image tag is 'latest', else set to 'IfNotPresent' + ## ref: http://kubernetes.io/docs/user-guide/images/#pre-pulling-images + ## + pullPolicy: Always + ## Optionally specify an array of imagePullSecrets. + ## Secrets must be manually created in the namespace. + ## ref: https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry/ + ## + # pullSecrets: + # - myRegistrKeySecretName + +## NATS Pod Security Context +## ref: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/ +## +securityContext: + enabled: false #true + fsGroup: 1001 + runAsUser: 1001 + +## NATS Node selector and tolerations for pod assignment +## ref: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#nodeselector +## ref: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#taints-and-tolerations +## +# nodeSelector: {} +# tolerations: [] + +## Use an alternate scheduler, e.g. "stork". +## ref: https://kubernetes.io/docs/tasks/administer-cluster/configure-multiple-schedulers/ +## +# schedulerName: + +## Pods anti-affinity +## ref: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#affinity-and-anti-affinity +## +## Possible values: soft, hard +antiAffinity: soft + +## Pod annotations +## ref: https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations/ +## +podAnnotations: {} + # todo: specify + # sidecar.istio.io/inject: "false" + +## Additional pod labels +## ref: https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/ +## +podLabels: {} + +## Update strategy, can be set to RollingUpdate or OnDelete by default. +## https://kubernetes.io/docs/tutorials/stateful-application/basic-stateful-set/#updating-statefulsets + +updateStrategy: OnDelete +## Partition update strategy +## https://kubernetes.io/docs/concepts/workloads/controllers/statefulset/#partitions +# rollingUpdatePartition: + +## Configure resource requests and limits +## ref: http://kubernetes.io/docs/user-guide/compute-resources/ +## +# todo: specify +resources: {} + # limits: + # cpu: 500m + # memory: 512Mi + # requests: + # cpu: 100m + # memory: 256Mi + +## Configure extra options for liveness and readiness probes +## ref: https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-probes/#configure-probes) +livenessProbe: + enabled: true + initialDelaySeconds: 30 + periodSeconds: 10 + timeoutSeconds: 5 + failureThreshold: 6 + successThreshold: 1 +readinessProbe: + enabled: true + initialDelaySeconds: 5 + periodSeconds: 10 + timeoutSeconds: 5 + failureThreshold: 6 + successThreshold: 1 + +cluster: + ## Create a NATS Cluster when installing the operator + create: true + + name: nats-cluster + + ## Choose namespace for cluster deployment if clusterScoped is set to true + # namesapce: "some-namespace" + + ## Nats version + ## Image tags are listed here: https://hub.docker.com/_/nats?tab=tags + version: 2.1.2 #1.4.1 + + ## Cluster Size + size: 3 + + # pod: + # annotations: + # sidecar.istio.io/inject: "false" + + # operatorConfig: + # secret: operator-jwt + # systemAccount: "{{ $.Values.systemAccount | default "invalid" }}" + # resolver: URL(http://nats-account-server/jwt/v1/accounts/) + + ## Client Authentication + ## ref: https://github.com/nats-io/gnatsd#authentication + ## note: token not supported only user/password will work with this chart version + ## + auth: + enabled: false #true + + # NOTE: Only supported in Kubernetes v1.12+ clusters having the "TokenRequest" API enabled. + enableServiceAccounts: false + + ## This is where you enter a username/password for 1 user + username: "my-user" + password: "T0pS3cr3t" + + ## This is a where you can specify 2 or more users + users: [] + #- username: "another-user-1" + # password: "another-password-1" + #- username: "another-user-2" + # password: "another-password-2" + # permissions: + # publish: ["hello.*"] + # subscribe: ["hello.world"] + + defaultPermissions: {} + #publish: ["SANDBOX.*"] + #subscribe: ["PUBLIC.>"] + + tls: + enabled: false + #serverSecret: + #routesSecret: + + ## Configuration Reload + ## NOTE: Only supported in Kubernetes v1.12+. + configReload: + enabled: true + registry: "docker.io" + repository: "connecteverything/nats-server-config-reloader" + tag: "0.6.0" #"0.2.2-v1alpha2" + pullPolicy: "IfNotPresent" + + ## Prometheus Metrics Exporter + ## + metrics: + enabled: false + registry: "docker.io" + repository: "synadia/prometheus-nats-exporter" + tag: "0.6.0" #"0.2.0" + pullPolicy: "IfNotPresent" + + # Prometheus Operator ServiceMonitor config + ## + servicemonitor: + enabled: false + prometheusInstance: default diff --git a/helm/nats-prometheus/Chart.yaml b/helm/nats-prometheus/Chart.yaml new file mode 100644 index 000000000..1769d7f52 --- /dev/null +++ b/helm/nats-prometheus/Chart.yaml @@ -0,0 +1,5 @@ +apiVersion: v1 +appVersion: '0.4.0' +description: NATS Prometheus +name: nats-prometheus +version: 0.0.1 diff --git a/helm/nats-prometheus/templates/nats-prometheus.yml b/helm/nats-prometheus/templates/nats-prometheus.yml new file mode 100644 index 000000000..b4eb35e36 --- /dev/null +++ b/helm/nats-prometheus/templates/nats-prometheus.yml @@ -0,0 +1,46 @@ +--- +# Service for data from prometheus exporters per NATS Server +apiVersion: v1 +kind: Service +metadata: + name: nats-prometheus + namespace: {{ $.Release.Namespace }} +spec: + selector: + prometheus: nats-prometheus + clusterIP: None + ports: + - name: web + port: 9090 + protocol: TCP + targetPort: web +--- +apiVersion: monitoring.coreos.com/v1 +kind: Prometheus +metadata: + name: nats-prometheus + namespace: {{ $.Release.Namespace }} +spec: + scrapeInterval: "5s" + serviceAccountName: prometheus + serviceMonitorSelector: + matchLabels: + app: nats + resources: + requests: + memory: 400Mi + enableAdminAPI: true +--- +apiVersion: monitoring.coreos.com/v1 +kind: ServiceMonitor +metadata: + name: nats + namespace: {{ $.Release.Namespace }} + labels: + app: nats +spec: + selector: + matchLabels: + app: nats + endpoints: + - port: metrics diff --git a/helm/nats-server/Chart.yaml b/helm/nats-server/Chart.yaml new file mode 100644 index 000000000..1da4b216a --- /dev/null +++ b/helm/nats-server/Chart.yaml @@ -0,0 +1,15 @@ +apiVersion: v2 +appVersion: "2.1.4" +description: A Helm chart for the NATS.io High Speed Cloud Native Distributed Communications Technology. +name: nats +keywords: + - nats + - messaging + - cncf +version: 0.2.0 +home: http://github.com/nats-io/k8s +maintainers: + - name: Waldemar Quevedo + github: https://github.com/wallyqs + email: wally@nats.io +icon: https://nats.io/img/logo.png diff --git a/helm/nats-server/templates/NOTES.txt b/helm/nats-server/templates/NOTES.txt new file mode 100644 index 000000000..13a2000e0 --- /dev/null +++ b/helm/nats-server/templates/NOTES.txt @@ -0,0 +1,26 @@ + +{{- if or .Values.nats.debug .Values.nats.trace }} +*WARNING*: Keep in mind that running the server with +debug and/or trace enabled significantly affects the +performance of the server! +{{- end }} + +You can find more information about running NATS on Kubernetes +in the NATS documentation website: + + https://docs.nats.io/nats-on-kubernetes/nats-kubernetes + +{{- if .Values.natsbox.enabled }} + +NATS Box has been deployed into your cluster, you can +now use the NATS tools within the container as follows: + + kubectl exec -n {{ .Release.Namespace }} -it {{ template "nats.name" . }}-box -- /bin/sh -l + + nats-box:~# nats-sub test & + nats-box:~# nats-pub test hi + nats-box:~# nc {{ .Release.Name }} 4222 + +{{- end }} + +Thanks for using NATS! diff --git a/helm/nats-server/templates/_helpers.tpl b/helm/nats-server/templates/_helpers.tpl new file mode 100644 index 000000000..9a191dbd1 --- /dev/null +++ b/helm/nats-server/templates/_helpers.tpl @@ -0,0 +1,22 @@ +{{/* +Expand the name of the chart. +*/}} +{{- define "nats.name" -}} +{{- default .Release.Name -}} +{{- end -}} + +{{/* +Return the proper NATS image name +*/}} +{{- define "nats.clusterAdvertise" -}} +{{- printf "$(POD_NAME).%s.$(POD_NAMESPACE).svc" (include "nats.name" . ) }} +{{- end }} + +{{/* +Return the NATS cluster routes. +*/}} +{{- define "nats.clusterRoutes" -}} +{{- range $i, $e := until 3 -}} +{{- printf "nats://%s-%d.%s.%s.svc:6222," $.Release.Name $i $.Release.Name $.Release.Namespace -}} +{{- end -}} +{{- end }} diff --git a/helm/nats-server/templates/configmap.yaml b/helm/nats-server/templates/configmap.yaml new file mode 100644 index 000000000..5e0783f3a --- /dev/null +++ b/helm/nats-server/templates/configmap.yaml @@ -0,0 +1,109 @@ +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ template "nats.name" . }}-config + labels: + app: {{ template "nats.name" . }} + chart: {{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }} +data: + nats.conf: | + # PID file shared with configuration reloader. + pid_file: "/var/run/nats/nats.pid" + + ############### + # # + # Monitoring # + # # + ############### + http: 8222 + server_name: $POD_NAME + + {{ if .Values.cluster.enabled }} + ################################### + # # + # NATS Full Mesh Clustering Setup # + # # + ################################### + cluster { + port: 6222 + + routes = [ + {{ template "nats.clusterRoutes" . }} + ] + cluster_advertise: $CLUSTER_ADVERTISE + + connect_retries: {{ .Values.nats.connectRetries }} + } + {{ end }} + + {{- if and .Values.nats.advertise .Values.nats.externalAccess }} + include "advertise/client_advertise.conf" + {{- end }} + + {{- if .Values.leafnodes.enabled }} + leafnodes { + listen: "0.0.0.0:7422" + {{ if and .Values.nats.advertise .Values.nats.externalAccess }} + include "advertise/gateway_advertise.conf" + {{ end }} + } + {{ end }} + + #################### + # # + # Logging Options # + # # + #################### + {{- with .Values.nats.logging.debug }} + debug: {{ . }} + {{- end }} + + {{- with .Values.nats.logging.trace }} + trace: {{ . }} + {{- end }} + + {{- with .Values.nats.logging.logtime }} + logtime: {{ . }} + {{- end }} + + {{- with .Values.nats.logging.connectErrorReports }} + connect_error_reports: {{ . }} + {{- end }} + + {{- with .Values.nats.logging.reconnectErrorReports }} + reconnect_error_reports: {{ . }} + {{- end }} + + ################## + # # + # Server Limits # + # # + ################## + {{- with .Values.nats.limits.maxConnections }} + max_connections: {{ . }} + {{- end }} + {{- with .Values.nats.limits.maxSubscriptions }} + max_subscriptions: {{ . }} + {{- end }} + {{- with .Values.nats.limits.maxPending }} + max_pending: {{ . }} + {{- end }} + {{- with .Values.nats.limits.maxControlLine }} + max_control_line: {{ . }} + {{- end }} + {{- with .Values.nats.limits.maxPayload }} + max_payload: {{ . }} + {{- end }} + {{- with .Values.nats.pingInterval }} + ping_interval: {{ . }} + {{- end }} + {{- with .Values.nats.maxPings }} + ping_max: {{ . }} + {{- end }} + {{- with .Values.nats.writeDeadline }} + write_deadline: {{ . | quote }} + {{- end }} + {{- with .Values.nats.writeDeadline }} + lame_duck_duration: {{ . | quote }} + {{- end }} diff --git a/helm/nats-server/templates/nats-box.yaml b/helm/nats-server/templates/nats-box.yaml new file mode 100644 index 000000000..7a89e229e --- /dev/null +++ b/helm/nats-server/templates/nats-box.yaml @@ -0,0 +1,22 @@ +{{- if .Values.natsbox.enabled }} +--- +apiVersion: v1 +kind: Pod +metadata: + name: {{ .Release.Name }}-box + labels: + app: {{ .Release.Name }}-box + chart: {{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }} +spec: + containers: + - name: nats-box + image: {{ .Values.natsbox.image }} + imagePullPolicy: {{ .Values.natsbox.pullPolicy }} + env: + - name: NATS_URL + value: {{ .Release.Name }} + command: + - "tail" + - "-f" + - "/dev/null" +{{- end }} diff --git a/helm/nats-server/templates/rbac.yaml b/helm/nats-server/templates/rbac.yaml new file mode 100644 index 000000000..0b596f157 --- /dev/null +++ b/helm/nats-server/templates/rbac.yaml @@ -0,0 +1,31 @@ +{{ if and .Values.nats.externalAccess .Values.nats.advertise }} +--- +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ .Values.nats.serviceAccount }} + namespace: {{ .Release.Namespace }} +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: {{ .Values.nats.serviceAccount }} +rules: +- apiGroups: [""] + resources: + - nodes + verbs: ["get"] +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: {{ .Values.nats.serviceAccount }}-binding +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: {{ .Values.nats.serviceAccount }} +subjects: +- kind: ServiceAccount + name: {{ .Values.nats.serviceAccount }} + namespace: {{ .Release.Namespace }} +{{ end }} diff --git a/helm/nats-server/templates/service.yaml b/helm/nats-server/templates/service.yaml new file mode 100644 index 000000000..c8643969c --- /dev/null +++ b/helm/nats-server/templates/service.yaml @@ -0,0 +1,25 @@ +--- +apiVersion: v1 +kind: Service +metadata: + name: {{ template "nats.name" . }} + labels: + app: {{ template "nats.name" . }} + chart: {{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }} +spec: + selector: + app: {{ template "nats.name" . }} + clusterIP: None + ports: + - name: client + port: 4222 + - name: cluster + port: 6222 + - name: monitor + port: 8222 + - name: metrics + port: 7777 + - name: leafnodes + port: 7422 + - name: gateways + port: 7522 diff --git a/helm/nats-server/templates/statefulset.yaml b/helm/nats-server/templates/statefulset.yaml new file mode 100644 index 000000000..1129eed09 --- /dev/null +++ b/helm/nats-server/templates/statefulset.yaml @@ -0,0 +1,206 @@ +--- +apiVersion: apps/v1 +kind: StatefulSet +metadata: + name: {{ template "nats.name" . }} + labels: + app: {{ template "nats.name" . }} + chart: {{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }} +spec: + selector: + matchLabels: + app: {{ template "nats.name" . }} + {{- if .Values.cluster.enabled }} + replicas: 3 + {{- else }} + replicas: 1 + {{- end }} + serviceName: {{ template "nats.name" . }} + template: + metadata: + labels: + app: {{ template "nats.name" . }} + chart: {{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }} + spec: + # Common volumes for the containers. + volumes: + - name: config-volume + configMap: + name: {{ template "nats.name" . }}-config + + # Local volume shared with the reloader. + - name: pid + emptyDir: {} + {{ if and .Values.nats.externalAccess .Values.nats.advertise }} + # Local volume shared with the advertise config initializer. + - name: advertiseconfig + emptyDir: {} + {{ end }} + + {{ if and .Values.nats.externalAccess .Values.nats.advertise }} + # Assume that we only use the service account in case we want to + # figure out what is the current external public IP from the server + # in order to be able to advertise correctly. + serviceAccountName: {{ .Values.nats.serviceAccount }} + {{ end }} + + # Required to be able to HUP signal and apply config + # reload to the server without restarting the pod. + shareProcessNamespace: true + + {{ if and .Values.nats.externalAccess .Values.nats.advertise }} + # Initializer container required to be able to lookup + # the external ip on which this node is running. + initContainers: + - name: bootconfig + command: + - nats-pod-bootconfig + - -f + - /etc/nats-config/advertise/client_advertise.conf + - -gf + - /etc/nats-config/advertise/gateway_advertise.conf + env: + - name: KUBERNETES_NODE_NAME + valueFrom: + fieldRef: + apiVersion: v1 + fieldPath: spec.nodeName + image: {{ .Values.bootconfig.image }} + imagePullPolicy: {{ .Values.bootconfig.pullPolicy }} + volumeMounts: + - mountPath: /etc/nats-config/advertise + name: advertiseconfig + subPath: advertise + {{ end }} + + ################# + # # + # NATS Server # + # # + ################# + terminationGracePeriodSeconds: 60 + containers: + - name: nats + image: {{ .Values.nats.image }} + imagePullPolicy: {{ .Values.nats.pullPolicy }} + ports: + - containerPort: 4222 + name: client + {{- if .Values.nats.externalAccess }} + hostPort: 4222 + {{- end }} + - containerPort: 7422 + name: leafnodes + {{- if .Values.nats.externalAccess }} + hostPort: 7422 + {{- end }} + - containerPort: 7522 + name: gateways + {{- if .Values.nats.externalAccess }} + hostPort: 7522 + {{- end }} + - containerPort: 6222 + name: cluster + - containerPort: 8222 + name: monitor + - containerPort: 7777 + name: metrics + command: + - "nats-server" + - "--config" + - "/etc/nats-config/nats.conf" + + # Required to be able to define an environment variable + # that refers to other environment variables. This env var + # is later used as part of the configuration file. + env: + - name: POD_NAME + valueFrom: + fieldRef: + fieldPath: metadata.name + - name: POD_NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace + - name: CLUSTER_ADVERTISE + value: {{ template "nats.clusterAdvertise" . }} + volumeMounts: + - name: config-volume + mountPath: /etc/nats-config + - name: pid + mountPath: /var/run/nats + {{- if and .Values.nats.externalAccess .Values.nats.advertise }} + - mountPath: /etc/nats-config/advertise + name: advertiseconfig + subPath: advertise + {{- end }} + + # Liveness/Readiness probes against the monitoring. + # + livenessProbe: + httpGet: + path: / + port: 8222 + initialDelaySeconds: 10 + timeoutSeconds: 5 + readinessProbe: + httpGet: + path: / + port: 8222 + initialDelaySeconds: 10 + timeoutSeconds: 5 + + # Gracefully stop NATS Server on pod deletion or image upgrade. + # + lifecycle: + preStop: + exec: + # Using the alpine based NATS image, we add an extra sleep that is + # the same amount as the terminationGracePeriodSeconds to allow + # the NATS Server to gracefully terminate the client connections. + # + command: ["/bin/sh", "-c", "/nats-server -sl=ldm=/var/run/nats/nats.pid && /bin/sleep 60"] + + ################################# + # # + # NATS Configuration Reloader # + # # + ################################# + {{ if .Values.reloader.enabled }} + - name: reloader + image: {{ .Values.reloader.image }} + imagePullPolicy: {{ .Values.reloader.pullPolicy }} + command: + - "nats-server-config-reloader" + - "-pid" + - "/var/run/nats/nats.pid" + - "-config" + - "/etc/nats-config/nats.conf" + volumeMounts: + - name: config-volume + mountPath: /etc/nats-config + - name: pid + mountPath: /var/run/nats + {{ end }} + + ############################## + # # + # NATS Prometheus Exporter # + # # + ############################## + {{ if .Values.exporter.enabled }} + - name: metrics + image: {{ .Values.exporter.image }} + imagePullPolicy: {{ .Values.exporter.pullPolicy }} + args: + - -connz + - -routez + - -subz + - -varz + - -prefix=nats + - -use_internal_server_id + - http://localhost:8222/ + ports: + - containerPort: 7777 + name: metrics + {{ end }} diff --git a/helm/nats-server/values.yaml b/helm/nats-server/values.yaml new file mode 100644 index 000000000..4fa55f17d --- /dev/null +++ b/helm/nats-server/values.yaml @@ -0,0 +1,83 @@ +# NATS Server Configuration +nats: + image: nats:2.1.4-alpine3.11 + pullPolicy: IfNotPresent + + # Toggle whether to enable external access. + # This binds a host port for clients, gateways and leafnodes. + externalAccess: false + + # Toggle to disable client advertisements (connect_urls), + # in case of running behind a load balancer (which is not recommended) + # it might be required to disable advertisements. + advertise: true + + # In case both external access and advertise are enabled + # then a service account would be required to be able to + # gather the public ip from a node. + serviceAccount: "nats-server" + + # The number of connect attempts against discovered routes. + connectRetries: 30 + + # How many seconds should pass before sending a PING + # to a client that has no activity. + pingInterval: + + # Server settings. + limits: + maxConnections: + maxSubscriptions: + maxControlLine: + maxPayload: + + writeDeadline: + maxPending: + maxPings: + lameDuckDuration: + + logging: + debug: + trace: + logtime: + connectErrorReports: + reconnectErrorReports: + +# Toggle whether to use setup a NATS Cluster. +cluster: + enabled: false + +# Leafnode connections to extend a cluster: +# +# https://docs.nats.io/nats-server/configuration/leafnodes +# +leafnodes: + enabled: false + +# In case of both external access and advertisements being +# enabled, an initializer container will be used to gather +# the public ips. +bootconfig: + image: connecteverything/nats-boot-config:0.5.2 + pullPolicy: IfNotPresent + +# NATS Box +# +# https://github.com/nats-io/nats-box +# +natsbox: + enabled: true + image: synadia/nats-box:0.3.0 + pullPolicy: IfNotPresent + +# The NATS config reloader image to use. +reloader: + enabled: true + image: connecteverything/nats-server-config-reloader:0.6.0 + pullPolicy: IfNotPresent + +# Prometheus NATS Exporter configuration. +exporter: + enabled: true + image: synadia/prometheus-nats-exporter:0.5.0 + pullPolicy: IfNotPresent \ No newline at end of file diff --git a/helm/nats-surveyor-grafana/Chart.yaml b/helm/nats-surveyor-grafana/Chart.yaml new file mode 100644 index 000000000..daaff1653 --- /dev/null +++ b/helm/nats-surveyor-grafana/Chart.yaml @@ -0,0 +1,5 @@ +apiVersion: v1 +appVersion: '0.4.0' +description: NATS Surveyor Grafana +name: nats-surveyor-grafana +version: 0.0.1 diff --git a/helm/nats-surveyor-grafana/templates/nats-surveyor-grafana.yml b/helm/nats-surveyor-grafana/templates/nats-surveyor-grafana.yml new file mode 100644 index 000000000..7af30997c --- /dev/null +++ b/helm/nats-surveyor-grafana/templates/nats-surveyor-grafana.yml @@ -0,0 +1,55 @@ +--- +apiVersion: v1 +kind: Service +metadata: + name: grafana + namespace: {{ $.Release.Namespace }} +spec: + type: NodePort + selector: + app: nats-surveyor-grafana + ports: + - name: web + # nodePort: 30300 + port: 3000 + protocol: TCP + targetPort: web +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: nats-surveyor-grafana + namespace: {{ $.Release.Namespace }} + labels: + app: nats-surveyor-grafana +spec: + selector: + matchLabels: + app: nats-surveyor-grafana + replicas: 1 + template: + metadata: + labels: + app: nats-surveyor-grafana + spec: + containers: + - name: nats-surveyor-grafana + image: connecteverything/nats-surveyor-grafana:0.1.1 + imagePullPolicy: Always + env: + - name: GF_AUTH_ANONYMOUS_ENABLED + value: "true" + - name: GF_AUTH_ANONYMOUS_ORG_ROLE + value: "Admin" + - name: GF_AUTH_DISABLE_LOGIN_FORM + value: "true" + + ports: + - containerPort: 3000 + name: web + + # Disable all cpu limits for the server. + # + resources: + requests: + cpu: 0 diff --git a/helm/nats-surveyor/Chart.yaml b/helm/nats-surveyor/Chart.yaml new file mode 100644 index 000000000..a4aae1ac2 --- /dev/null +++ b/helm/nats-surveyor/Chart.yaml @@ -0,0 +1,5 @@ +apiVersion: v1 +appVersion: '0.4.0' +description: NATS Surveyor +name: nats-surveyor +version: 0.0.1 diff --git a/helm/nats-surveyor/templates/nats-surveyor.yml b/helm/nats-surveyor/templates/nats-surveyor.yml new file mode 100644 index 000000000..5b214a6dd --- /dev/null +++ b/helm/nats-surveyor/templates/nats-surveyor.yml @@ -0,0 +1,131 @@ +--- +# Service used by Grafana as a datasource +apiVersion: v1 +kind: Service +metadata: + name: nats-surveyor-prometheus + namespace: {{ $.Release.Namespace }} + labels: + app: nats-surveyor-prometheus +spec: + selector: + prometheus: nats-surveyor + clusterIP: None + ports: + - name: web + port: 9090 + protocol: TCP + targetPort: web +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: nats-surveyor-observations + namespace: {{ $.Release.Namespace }} +data: + test.json: {{ toJson $.Values.observations }} +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: nats-surveyor + namespace: {{ $.Release.Namespace }} + labels: + app: nats-surveyor +spec: + selector: + matchLabels: + app: nats-surveyor + replicas: 1 + template: + metadata: + labels: + app: nats-surveyor + spec: + volumes: + - name: config-volume + configMap: + name: nats-surveyor-observations + - name: creds-volume + secret: + secretName: nats-creds + containers: + - name: nats-surveyor + image: synadia/nats-surveyor:0.0.1-alpha4 + ports: + - containerPort: 7777 + name: metrics + command: + - "/nats-surveyor" + - "-s=nats://nats-cluster-mgmt.{{ default "nats" $.Values.natsNamespace }}.svc.cluster.local:4222" + - "-c={{ $.Values.clusterSize | default 3 }}" + - "-creds=/var/run/nats/secrets/sys.creds" + - "-observe=/etc/nats/observations/" + volumeMounts: + - name: config-volume + mountPath: /etc/nats/observations + - name: creds-volume + mountPath: /var/run/nats/secrets + + # Disable all cpu limits for the server. + # + resources: + requests: + cpu: 0 +--- +# Service used by Prometheus Operator to find the metrics endpoint. +apiVersion: v1 +kind: Service +metadata: + name: nats-surveyor + namespace: {{ $.Release.Namespace }} + labels: + app: nats-surveyor +spec: + clusterIP: None + ports: + - name: metrics + port: 7777 + protocol: TCP + targetPort: 7777 + selector: + app: nats-surveyor +--- +apiVersion: monitoring.coreos.com/v1 +kind: Prometheus +metadata: + name: nats-surveyor + namespace: {{ $.Release.Namespace }} +spec: + scrapeInterval: "5s" + serviceAccountName: prometheus + serviceMonitorSelector: + matchLabels: + app: nats-surveyor + resources: + requests: + memory: 400Mi + enableAdminAPI: true +--- +apiVersion: monitoring.coreos.com/v1 +kind: ServiceMonitor +metadata: + name: nats-surveyor + namespace: {{ $.Release.Namespace }} + labels: + app: nats-surveyor +spec: + selector: + matchLabels: + app: nats-surveyor + endpoints: + - port: metrics +--- +apiVersion: v1 +kind: Secret +metadata: + name: nats-creds + namespace: {{ $.Release.Namespace }} +data: + sys.creds: {{ $.Values.natsCreds | b64enc }} +type: Opaque diff --git a/helm/nats-surveyor/values.yaml b/helm/nats-surveyor/values.yaml new file mode 100644 index 000000000..9c2ab4342 --- /dev/null +++ b/helm/nats-surveyor/values.yaml @@ -0,0 +1,9 @@ + +natsNamespace: nats +clusterSize: 3 +observations: | + { + "name": "mds.telemetry", + "topic": "mds.events", + "credential": "/var/run/nats/secrets/sys.creds" + } diff --git a/helm/prometheus-operator/Chart.yaml b/helm/prometheus-operator/Chart.yaml new file mode 100644 index 000000000..a95097960 --- /dev/null +++ b/helm/prometheus-operator/Chart.yaml @@ -0,0 +1,5 @@ +apiVersion: v1 +appVersion: '0.4.0' +description: Prometheus Operator +name: prometheus-operator +version: 0.0.1 diff --git a/helm/prometheus-operator/templates/prometheus-operator.yml b/helm/prometheus-operator/templates/prometheus-operator.yml new file mode 100644 index 000000000..c28abbc33 --- /dev/null +++ b/helm/prometheus-operator/templates/prometheus-operator.yml @@ -0,0 +1,208 @@ +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + labels: + app.kubernetes.io/component: controller + app.kubernetes.io/name: prometheus-operator + app.kubernetes.io/version: v0.30.0 + name: prometheus-operator +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: prometheus-operator +subjects: +- kind: ServiceAccount + name: prometheus-operator + namespace: {{ $.Release.Namespace }} +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + labels: + app.kubernetes.io/component: controller + app.kubernetes.io/name: prometheus-operator + app.kubernetes.io/version: v0.30.0 + name: prometheus-operator +rules: +- apiGroups: + - apiextensions.k8s.io + resources: + - customresourcedefinitions + verbs: + - '*' +- apiGroups: + - monitoring.coreos.com + resources: + - alertmanagers + - prometheuses + - prometheuses/finalizers + - alertmanagers/finalizers + - servicemonitors + - podmonitors + - prometheusrules + verbs: + - '*' +- apiGroups: + - apps + resources: + - statefulsets + verbs: + - '*' +- apiGroups: + - "" + resources: + - configmaps + - secrets + verbs: + - '*' +- apiGroups: + - "" + resources: + - pods + verbs: + - list + - delete +- apiGroups: + - "" + resources: + - services + - services/finalizers + - endpoints + verbs: + - get + - create + - update + - delete +- apiGroups: + - "" + resources: + - nodes + verbs: + - list + - watch +- apiGroups: + - "" + resources: + - namespaces + verbs: + - get + - list + - watch +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + labels: + app.kubernetes.io/component: controller + app.kubernetes.io/name: prometheus-operator + app.kubernetes.io/version: v0.30.0 + name: prometheus-operator + namespace: {{ $.Release.Namespace }} +spec: + replicas: 1 + selector: + matchLabels: + app.kubernetes.io/component: controller + app.kubernetes.io/name: prometheus-operator + template: + metadata: + labels: + app.kubernetes.io/component: controller + app.kubernetes.io/name: prometheus-operator + app.kubernetes.io/version: v0.30.0 + spec: + containers: + - args: + - --kubelet-service=kube-system/kubelet + - --logtostderr=true + - --config-reloader-image=quay.io/coreos/configmap-reload:v0.0.1 + - --prometheus-config-reloader=quay.io/coreos/prometheus-config-reloader:v0.30.0 + image: quay.io/coreos/prometheus-operator:v0.30.0 + name: prometheus-operator + ports: + - containerPort: 8080 + name: http + resources: + limits: + cpu: 200m + memory: 200Mi + requests: + cpu: 100m + memory: 100Mi + securityContext: + allowPrivilegeEscalation: false + readOnlyRootFilesystem: true + nodeSelector: + beta.kubernetes.io/os: linux + securityContext: + runAsNonRoot: true + runAsUser: 65534 + serviceAccountName: prometheus-operator +--- +apiVersion: v1 +kind: ServiceAccount +metadata: + labels: + app.kubernetes.io/component: controller + app.kubernetes.io/name: prometheus-operator + app.kubernetes.io/version: v0.30.0 + name: prometheus-operator + namespace: {{ $.Release.Namespace }} +--- +apiVersion: v1 +kind: Service +metadata: + labels: + app.kubernetes.io/component: controller + app.kubernetes.io/name: prometheus-operator + app.kubernetes.io/version: v0.30.0 + name: prometheus-operator + namespace: {{ $.Release.Namespace }} +spec: + clusterIP: None + ports: + - name: http + port: 8080 + targetPort: http + selector: + app.kubernetes.io/component: controller + app.kubernetes.io/name: prometheus-operator +--- +apiVersion: v1 +kind: ServiceAccount +metadata: + name: prometheus + namespace: {{ $.Release.Namespace }} +--- +apiVersion: rbac.authorization.k8s.io/v1beta1 +kind: ClusterRole +metadata: + name: prometheus +rules: +- apiGroups: [""] + resources: + - nodes + - services + - endpoints + - pods + verbs: ["get", "list", "watch"] +- apiGroups: [""] + resources: + - configmaps + verbs: ["get"] +- nonResourceURLs: ["/metrics"] + verbs: ["get"] +--- +apiVersion: rbac.authorization.k8s.io/v1beta1 +kind: ClusterRoleBinding +metadata: + name: prometheus +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: prometheus +subjects: +- kind: ServiceAccount + name: prometheus + namespace: {{ $.Release.Namespace }} diff --git a/lerna.json b/lerna.json index 1b8f8c79b..f487b511a 100644 --- a/lerna.json +++ b/lerna.json @@ -1,5 +1,4 @@ { - "packages": ["container-images/*", "packages/*"], "version": "independent", "npmClient": "yarn", "useWorkspaces": true, diff --git a/nginx/nginx.conf b/nginx/nginx.conf index 6164c2146..aed30bfec 100644 --- a/nginx/nginx.conf +++ b/nginx/nginx.conf @@ -29,9 +29,6 @@ http { location /daily { proxy_pass http://daily:4005; } - location /native { - proxy_pass http://native:4006; - } location /policy-author { proxy_pass http://policy-author:4007; } diff --git a/okteto.yml b/okteto.yml new file mode 100644 index 000000000..1f5033aee --- /dev/null +++ b/okteto.yml @@ -0,0 +1,10 @@ +name: mds-agency +namespace: mds +image: node:14.2.0 +command: ["bash"] +forward: + - 9229:9229 +resources: + limits: + cpu: 2000m + memory: 4Gi diff --git a/package.json b/package.json index 7c663c8c5..1a29fd38b 100644 --- a/package.json +++ b/package.json @@ -1,83 +1,62 @@ { "name": "root", "private": true, - "version": "0.0.12", + "version": "0.0.20", "license": "Apache-2.0", "workspaces": [ "container-images/*", "packages/*" ], "engines": { - "node": "10.16.3" + "node": "14.2.0" }, "devDependencies": { - "@types/bluebird": "3.5.28", - "@types/body-parser": "1.17.1", - "@types/cors": "2.8.6", - "@types/d3": "5.7.2", - "@types/express": "4.17.1", - "@types/flat": "0.0.28", - "@types/geojson": "7946.0.7", - "@types/hapi__joi": "15.0.4", - "@types/jwt-decode": "2.2.1", - "@types/mocha": "5.2.7", + "@istanbuljs/nyc-config-typescript": "1.0.1", + "@types/mocha": "7.0.2", "@types/mockdate": "2.0.0", - "@types/moment-timezone": "0.5.12", - "@types/morgan": "1.7.37", - "@types/node": "10.14.21", - "@types/pg": "7.11.2", - "@types/point-in-polygon": "1.0.0", - "@types/redis": "2.8.14", - "@types/request-promise": "4.1.44", - "@types/should": "13.0.0", - "@types/sinon": "7.5.0", - "@types/supertest": "2.0.8", - "@types/uuid": "3.4.5", - "@types/yargs": "13.0.3", - "@typescript-eslint/eslint-plugin": "2.5.0", - "@typescript-eslint/parser": "2.5.0", - "chai": "4.2.0", - "check-node-version": "4.0.1", - "cypress": "3.5.0", + "@types/node": "14.0.1", + "@types/sinon": "9.0.0", + "@types/sinon-express-mock": "1.3.8", + "@types/supertest": "2.0.9", + "@typescript-eslint/eslint-plugin": "2.33.0", + "@typescript-eslint/parser": "2.33.0", + "check-node-version": "4.0.3", "dotenv": "8.2.0", - "eslint": "6.5.1", - "eslint-config-airbnb-base": "14.0.0", - "eslint-config-prettier": "6.4.0", + "eslint": "7.0.0", + "eslint-config-airbnb-base": "14.1.0", + "eslint-config-prettier": "6.11.0", "eslint-config-recommended": "4.0.0", - "eslint-plugin-import": "2.18.2", - "eslint-plugin-prettier": "3.1.1", + "eslint-plugin-import": "2.20.2", + "eslint-plugin-prettier": "3.1.3", "eslint-plugin-promise": "4.2.1", - "git-revision-webpack-plugin": "3.0.4", - "husky": "3.0.9", - "lerna": "3.18.3", - "mocha": "6.2.2", - "mochawesome": "4.1.0", + "husky": "4.2.5", + "lerna": "3.21.0", + "mocha": "7.1.2", + "mochawesome": "6.1.1", "mockdate": "2.0.5", - "nodemon": "1.19.4", - "nyc": "14.1.1", - "prettier": "1.18.2", + "node-rsa": "1.0.8", + "nodemon": "2.0.4", + "nyc": "15.0.1", + "prettier": "2.0.5", "should": "13.2.3", - "source-map-support": "0.5.13", + "sinon": "9.0.2", + "sinon-express-mock": "2.2.1", + "source-map-support": "0.5.19", "supertest": "4.0.2", - "ts-loader": "6.2.1", - "ts-mocha": "6.0.0", - "ts-node": "8.4.1", + "ts-loader": "7.0.4", + "ts-mocha": "7.0.0", + "ts-node": "8.10.1", "tsconfig-paths": "3.9.0", - "typescript": "3.6.4", + "typescript": "3.8.3", "unit.js": "2.1.1", - "uuid": "3.3.3", - "webpack": "4.41.2", - "webpack-bundle-analyzer": "3.6.0", - "webpack-cli": "3.3.9", - "zip-webpack-plugin": "3.0.0" + "webpack": "4.43.0", + "webpack-cli": "3.3.11" }, - "types": "types/mds.d.ts", - "dependencies": {}, "scripts": { "build": "yarn run:concurrent build", "bundle": "yarn run:concurrent bundle", + "clean": "git clean -fdX -e '!.env' -e '!helm/mds/charts/*' -e '!helm/mds/files/*' -e '!helm/util/charts/*' && yarn", "image": "yarn run:concurrent image", - "clean": "git clean -fdX -e '!.env' && yarn", "run:concurrent": "lerna run --stream", "run:sequential": "lerna run --stream --concurrency 1", "test": "check-node-version --package --print && yarn test:eslint && yarn test:unit", diff --git a/packages/ladot-service-areas/la-city-boundary.js b/packages/ladot-service-areas/la-city-boundary.js deleted file mode 100644 index 6ce2f9341..000000000 --- a/packages/ladot-service-areas/la-city-boundary.js +++ /dev/null @@ -1,8773 +0,0 @@ -module.exports = { - type: 'FeatureCollection', - crs: { - type: 'name', - properties: { - name: 'EPSG:4326' - } - }, - features: [ - { - type: 'Feature', - id: 1, - properties: { - name: 'City Boundaries', - OBJECTID_1: 1, - OBJECTID: 1, - CITY: 'IN' - }, - geometry: { - type: 'Polygon', - coordinates: [ - [ - [-118.26738937343, 33.7036555618044], - [-118.267453518531, 33.7036576632831], - [-118.267585037278, 33.7036674719269], - [-118.267683711822, 33.7036791488823], - [-118.267780702025, 33.7036943292957], - [-118.267922186683, 33.7037279589125], - [-118.268050476885, 33.7037510788928], - [-118.268685333364, 33.7038409916948], - [-118.26886289077, 33.7038694834057], - [-118.269084943528, 33.7039145559652], - [-118.269323418389, 33.7039689707811], - [-118.269464903046, 33.7040054027489], - [-118.269621265601, 33.7040497749965], - [-118.269836861269, 33.7041179680903], - [-118.269958555142, 33.7041600049019], - [-118.270169237924, 33.704238473562], - [-118.270330513365, 33.7043047982068], - [-118.270444206843, 33.7043592120282], - [-118.270694472091, 33.7044906937694], - [-118.271218021018, 33.7047503863944], - [-118.277109986525, 33.7079098388937], - [-118.277249786841, 33.7079068034086], - [-118.277266209841, 33.7079049351878], - [-118.277297369704, 33.7078969952489], - [-118.277343408362, 33.7078778459815], - [-118.277366287554, 33.7078598639749], - [-118.277381025314, 33.707819230141], - [-118.278518658078, 33.7063531298721], - [-118.278559643713, 33.7063155316141], - [-118.278584206348, 33.7062996514418], - [-118.27861705145, 33.7062952139606], - [-118.278635157893, 33.7062970822164], - [-118.278656633018, 33.706315998678], - [-118.278663230245, 33.7063435554405], - [-118.278638666712, 33.7063795193377], - [-118.277771371153, 33.7075576785662], - [-118.277766599302, 33.7075906064587], - [-118.277747088793, 33.7076606649675], - [-118.277740632601, 33.7077019990872], - [-118.277744001283, 33.7077407647522], - [-118.277753966295, 33.7077650516659], - [-118.277777126659, 33.7078005479121], - [-118.277806743216, 33.7078341759212], - [-118.277899100807, 33.7079173117762], - [-118.277946963943, 33.7079542091243], - [-118.278118205294, 33.7080627997024], - [-118.278345591351, 33.7081961432416], - [-118.278508551133, 33.7082888537093], - [-118.278837980416, 33.7084705371803], - [-118.278938479438, 33.7085177094325], - [-118.279056944766, 33.7085669830174], - [-118.279134284322, 33.7085924376998], - [-118.279211623879, 33.708614389116], - [-118.279385952739, 33.7086547886745], - [-118.27961137418, 33.7086968232658], - [-118.279963401482, 33.7087673479227], - [-118.280090007343, 33.7087946707472], - [-118.280468422657, 33.7088820091216], - [-118.280726688301, 33.7089336177456], - [-118.280980041059, 33.7089889634805], - [-118.281075487058, 33.709006010405], - [-118.281238306703, 33.7090309979082], - [-118.2818697945, 33.7091185691929], - [-118.282065599384, 33.7091358499896], - [-118.282297336879, 33.7091477600995], - [-118.282428855627, 33.7091498614438], - [-118.28288924221, 33.7091491612448], - [-118.283332925519, 33.709135616839], - [-118.283446337823, 33.7091265090188], - [-118.283658424672, 33.7091019893394], - [-118.283898162564, 33.7090587869504], - [-118.28405915773, 33.709025393319], - [-118.284231662113, 33.7089817238422], - [-118.284386060052, 33.708933851644], - [-118.284537089309, 33.7088789729236], - [-118.28477514286, 33.7087818264875], - [-118.284829322051, 33.7087519352543], - [-118.284853885584, 33.7087344212286], - [-118.284899784105, 33.7086949550621], - [-118.284921119093, 33.7086734707176], - [-118.28496856092, 33.70861579027], - [-118.28499803734, 33.708567450069], - [-118.285010950622, 33.7085282177254], - [-118.285022179563, 33.7084450824617], - [-118.285032004437, 33.7084042157521], - [-118.285043374414, 33.7083787610139], - [-118.285059656378, 33.7083547081738], - [-118.28512029266, 33.7082858174903], - [-118.285136714762, 33.7082624648304], - [-118.28514808384, 33.7082379449043], - [-118.285152996726, 33.7082124901168], - [-118.285147943703, 33.7081723235035], - [-118.285124643201, 33.7080896549496], - [-118.285117906734, 33.7080480871156], - [-118.285117765699, 33.7080205308999], - [-118.285122538448, 33.7079789630324], - [-118.285135591867, 33.7079240844491], - [-118.285145276604, 33.7078976954581], - [-118.285171384342, 33.7078484214892], - [-118.285199315659, 33.7078150266398], - [-118.285310763348, 33.7077111070584], - [-118.285459686954, 33.7075462360403], - [-118.285503901134, 33.7074887879907], - [-118.285575906494, 33.7073804305895], - [-118.28560032989, 33.707329521246], - [-118.285632893819, 33.7072365765869], - [-118.285660544862, 33.7071438649833], - [-118.285720619696, 33.7069012276845], - [-118.285736761524, 33.7068479827522], - [-118.285779150327, 33.7067438280963], - [-118.285798800974, 33.7067048280768], - [-118.285829820699, 33.7066564875475], - [-118.285859297118, 33.7066237932017], - [-118.285905195639, 33.7065850270325], - [-118.285931443514, 33.7065672786601], - [-118.285987307046, 33.7065362186259], - [-118.286016782567, 33.706522673808], - [-118.286046399124, 33.7065112311441], - [-118.286092297645, 33.7064976863223], - [-118.286172864848, 33.7064820393414], - [-118.286320666458, 33.7064607883485], - [-118.286499768068, 33.706439536603], - [-118.286675641133, 33.7064087104279], - [-118.286739646097, 33.7063935312416], - [-118.286785684755, 33.7063797524958], - [-118.286905553251, 33.7063339802606], - [-118.287020369623, 33.7062795676902], - [-118.287141782323, 33.706209274149], - [-118.287327059851, 33.7060740592645], - [-118.287776498666, 33.7057728017063], - [-118.287894542684, 33.7056997053472], - [-118.287978198295, 33.7056562680868], - [-118.288093154803, 33.7056023221548], - [-118.288316329557, 33.705515680674], - [-118.288493746826, 33.705443051912], - [-118.288569261904, 33.7054154948605], - [-118.288629898185, 33.7053940096956], - [-118.288707096706, 33.7053704223413], - [-118.288754819705, 33.7053592126848], - [-118.288835246771, 33.7053454345204], - [-118.289014348381, 33.7053265178451], - [-118.289227979434, 33.7053181105986], - [-118.289505755587, 33.7052877510905], - [-118.289704507844, 33.7052319366481], - [-118.289924595089, 33.7051740199806], - [-118.290036182017, 33.7051497323342], - [-118.290180755082, 33.7051231093293], - [-118.29032701159, 33.705099288739], - [-118.290359856692, 33.7050922826817], - [-118.290404211009, 33.7050782705656], - [-118.29043368653, 33.705065659659], - [-118.2904748132, 33.7050430063561], - [-118.290525623709, 33.7050070418993], - [-118.290573206572, 33.7049675747674], - [-118.290594401422, 33.7049467897237], - [-118.290614052069, 33.7049213347082], - [-118.290661493896, 33.7048395971689], - [-118.290690970315, 33.7048113389421], - [-118.29071553295, 33.7048029316452], - [-118.290743605303, 33.7048057340776], - [-118.29078964486, 33.7048244169579], - [-118.290871896404, 33.7048643519747], - [-118.290903197301, 33.704873693409], - [-118.290931128618, 33.7048739265712], - [-118.290957376493, 33.7048615495442], - [-118.290981940026, 33.7048386630251], - [-118.291037663421, 33.7047674341602], - [-118.291058998409, 33.7047457156706], - [-118.291078649056, 33.7047345059326], - [-118.291096755499, 33.7047375415298], - [-118.291121460068, 33.7047618292928], - [-118.291171147683, 33.7048629507595], - [-118.29119599149, 33.7049047532971], - [-118.291205817263, 33.7049161969223], - [-118.291273471183, 33.7049787844749], - [-118.291298174854, 33.7049984014597], - [-118.291350950877, 33.7050322637277], - [-118.29139376099, 33.7050514136295], - [-118.291423377546, 33.7050605218818], - [-118.291487522648, 33.7050721986478], - [-118.291587741396, 33.7050787376361], - [-118.291719260143, 33.7050824742007], - [-118.291786633789, 33.7050813061506], - [-118.291900046094, 33.7050735998597], - [-118.291982158399, 33.7050623901645], - [-118.292110308464, 33.7050378685774], - [-118.292609855306, 33.7049540297021], - [-118.292833171095, 33.7049136276555], - [-118.292964689843, 33.7048961128472], - [-118.293290048859, 33.7048610824724], - [-118.293454271673, 33.7048400642407], - [-118.293518417672, 33.7048290876774], - [-118.293597300534, 33.704810404798], - [-118.293643199055, 33.7047959255638], - [-118.293823844869, 33.7047305354431], - [-118.293956766785, 33.7046782233107], - [-118.294035650545, 33.70465510358], - [-118.294130816269, 33.7046352526067], - [-118.294344446424, 33.7046020904183], - [-118.294410135729, 33.704598820906], - [-118.294456174387, 33.7046055938406], - [-118.294520459626, 33.7046249770004], - [-118.294551760523, 33.7046382889547], - [-118.294594571535, 33.7046621096729], - [-118.294619274307, 33.7046805586744], - [-118.294639065989, 33.7046999425646], - [-118.294660541114, 33.7047347390952], - [-118.294673875706, 33.7047742070814], - [-118.294683841616, 33.7048307228028], - [-118.294702228333, 33.7048998494197], - [-118.294720335675, 33.704938149278], - [-118.294778304858, 33.7050383356482], - [-118.294798235779, 33.7050901804908], - [-118.294801604462, 33.7051165703444], - [-118.294801744599, 33.7051581395972], - [-118.294793744203, 33.7052417451121], - [-118.294797253022, 33.7052688351498], - [-118.29480385025, 33.7052954581095], - [-118.29483543232, 33.7053762610771], - [-118.29486673232, 33.7054264706016], - [-118.294904770582, 33.7054687406808], - [-118.294929474253, 33.7054841539384], - [-118.294977197252, 33.7054995671933], - [-118.295046255239, 33.7055154475135], - [-118.295089065353, 33.7055329621956], - [-118.295112084682, 33.7055502437137], - [-118.29513033216, 33.7055721959074], - [-118.295151807285, 33.7056107293735], - [-118.295166685183, 33.7056525315472], - [-118.295173422548, 33.7056810226573], - [-118.295176932266, 33.705735202447], - [-118.295165563187, 33.7057735019328], - [-118.29515250887, 33.7057989574432], - [-118.295119804803, 33.7058498676939], - [-118.295096925611, 33.7059017120466], - [-118.295082327988, 33.7059701368293], - [-118.295079240478, 33.7059979276093], - [-118.295079380616, 33.7060532744886], - [-118.29508611798, 33.7060934420929], - [-118.295099452572, 33.70613244239], - [-118.295164018983, 33.7062604180622], - [-118.295183809767, 33.7063129623893], - [-118.295197144359, 33.7063664415587], - [-118.295220725136, 33.7065028240136], - [-118.295211320673, 33.7066408405956], - [-118.295211600947, 33.7067097325985], - [-118.295215110665, 33.7067779235805], - [-118.29522704119, 33.7068874490201], - [-118.295258622363, 33.7070231304314], - [-118.295267044068, 33.7070773093745], - [-118.295267325241, 33.7071457332207], - [-118.295240375782, 33.7074068189921], - [-118.29524051592, 33.7074476869236], - [-118.295245568943, 33.7075023326563], - [-118.295250621967, 33.7075294219809], - [-118.295272237229, 33.7076104560017], - [-118.295292169048, 33.7076630002501], - [-118.295343541005, 33.7077638843996], - [-118.295432811086, 33.7079119410156], - [-118.295528678395, 33.7080560270405], - [-118.295589735986, 33.7081375283656], - [-118.295640967805, 33.7081914727052], - [-118.295673812907, 33.708221831187], - [-118.295789329964, 33.7083210803684], - [-118.296077914648, 33.7085835637215], - [-118.296112583329, 33.7086127540626], - [-118.29616199067, 33.7086487170096], - [-118.296201573136, 33.7086730036666], - [-118.296270631124, 33.708710134589], - [-118.296611569622, 33.7088773386225], - [-118.296746597189, 33.7089546349756], - [-118.297008512688, 33.7090982529497], - [-118.29718143838, 33.7091790523397], - [-118.297420193515, 33.7092990836632], - [-118.297640842207, 33.7093936603815], - [-118.297711584535, 33.7094272877668], - [-118.298008028579, 33.7095734733307], - [-118.298184182816, 33.7096500688623], - [-118.298258293827, 33.7096769243604], - [-118.298320894724, 33.7096935041029], - [-118.298401462826, 33.7097082164079], - [-118.298516559471, 33.7097247961443], - [-118.298662957015, 33.7097395084439], - [-118.298745208559, 33.7097502504836], - [-118.298824092319, 33.7097675311531], - [-118.298932731875, 33.7097974220329], - [-118.299090638634, 33.7098345517219], - [-118.299219069871, 33.7098600060286], - [-118.299330797835, 33.7098789209582], - [-118.299559446922, 33.7099078777105], - [-118.30029452215, 33.7099833057077], - [-118.300440918795, 33.7100043226808], - [-118.300600510793, 33.7100370157398], - [-118.300740451247, 33.7100746123687], - [-118.300863829461, 33.7101138440059], - [-118.301028473585, 33.7101757275804], - [-118.301257403845, 33.710285248687], - [-118.301581779206, 33.7104463781161], - [-118.30174979291, 33.7105337148107], - [-118.301845379046, 33.7105874244986], - [-118.302245690795, 33.7108267828555], - [-118.302517431168, 33.7109818397686], - [-118.302616245849, 33.7110297108251], - [-118.302705234758, 33.7110654396146], - [-118.302751274315, 33.7110815520511], - [-118.302891214768, 33.7111217173037], - [-118.302988204072, 33.7111392315898], - [-118.303052350072, 33.7111429678906], - [-118.303216713023, 33.7111280226862], - [-118.303282402328, 33.7111291899068], - [-118.30334991701, 33.7111338602834], - [-118.303415606315, 33.7111415667778], - [-118.303463329314, 33.7111502065994], - [-118.303525930212, 33.7111658527298], - [-118.303614779881, 33.7112008805331], - [-118.303771282574, 33.711277707758], - [-118.303935927596, 33.7113683128618], - [-118.304067726617, 33.7114502780795], - [-118.304150119197, 33.7114962807322], - [-118.304304938446, 33.7115738086193], - [-118.304406980774, 33.7116193449181], - [-118.304481091785, 33.7116490012623], - [-118.304556887137, 33.7116753891006], - [-118.30472630401, 33.7117269960459], - [-118.30511959812, 33.711829743557], - [-118.305213359777, 33.7118514610024], - [-118.305325227878, 33.7118743456528], - [-118.305502926319, 33.7119023676654], - [-118.305601599965, 33.7119138096109], - [-118.305700274509, 33.7119219830688], - [-118.305994612902, 33.7119406644011], - [-118.306091603105, 33.7119486039662], - [-118.306175539888, 33.7119586448061], - [-118.306580202178, 33.7120163237557], - [-118.306690385937, 33.7120417766684], - [-118.306767725493, 33.7120641942355], - [-118.306876365947, 33.7120975874326], - [-118.306996515616, 33.7121400873709], - [-118.307075538615, 33.7121620378803], - [-118.307138000274, 33.7121753479192], - [-118.307299275715, 33.7122033698336], - [-118.307430794462, 33.7122229851682], - [-118.307493396258, 33.7122383972137], - [-118.307523011916, 33.7122489057985], - [-118.307565822928, 33.7122687542648], - [-118.307661409064, 33.7123224628676], - [-118.307718956937, 33.7123500177029], - [-118.307954484425, 33.7124483280525], - [-118.308119128549, 33.712512778237], - [-118.308206293877, 33.7125517748718], - [-118.308260754242, 33.7125818986709], - [-118.308379359707, 33.7126554554547], - [-118.308621343386, 33.7127500292239], - [-118.308772793953, 33.7128028034684], - [-118.308835394851, 33.7128198496359], - [-118.308868239952, 33.7128261548937], - [-118.308915962054, 33.7128333934607], - [-118.308981651359, 33.7128394648297], - [-118.309113311142, 33.7128478713399], - [-118.309228407787, 33.7128511405381], - [-118.309294097093, 33.7128553437928], - [-118.309407649534, 33.7128695885279], - [-118.309521201976, 33.7128901377665], - [-118.309598541532, 33.7129116210562], - [-118.309776239974, 33.7129833094472], - [-118.309978781324, 33.7130437898722], - [-118.310115353993, 33.7130874565362], - [-118.310191009208, 33.7131140770722], - [-118.310235503663, 33.7131325250055], - [-118.310278313776, 33.7131537743476], - [-118.310317895344, 33.7131773595643], - [-118.310457976833, 33.7132738001155], - [-118.31058991689, 33.7133765457757], - [-118.310799196503, 33.7135329991587], - [-118.310934505243, 33.7136525573725], - [-118.311074726869, 33.7137679122102], - [-118.311124133311, 33.7138041068835], - [-118.31122954522, 33.713871124302], - [-118.311419034049, 33.7139799405359], - [-118.311694143105, 33.714130088405], - [-118.311778219128, 33.7141728204743], - [-118.311895000115, 33.7142232587403], - [-118.312222745056, 33.7143542583607], - [-118.312310050522, 33.7143925540029], - [-118.31249448543, 33.7144812877424], - [-118.312667270985, 33.7145354619804], - [-118.312761032642, 33.7145597469726], - [-118.312858162982, 33.7145770266743], - [-118.312988137526, 33.7145964075837], - [-118.313100006525, 33.7146174234278], - [-118.313259597625, 33.7146508156319], - [-118.31343238318, 33.7146935474421], - [-118.313577095484, 33.7147218024174], - [-118.313659347926, 33.7147332439872], - [-118.313725177369, 33.7147358129602], - [-118.313772760231, 33.7147323099511], - [-118.313847853999, 33.7147173653714], - [-118.3139501775, 33.7146970504528], - [-118.314047167703, 33.7146865421675], - [-118.314311749402, 33.7146734656535], - [-118.314394000946, 33.7146718314627], - [-118.314443268149, 33.7146736995363], - [-118.314490991148, 33.7146788367385], - [-118.314699989589, 33.7147164313351], - [-118.314831509235, 33.7147313759149], - [-118.31491544512, 33.7147360460956], - [-118.314977906779, 33.7147311427795], - [-118.315025488743, 33.7147206344984], - [-118.315086265161, 33.7147005527161], - [-118.315412887209, 33.7145653512005], - [-118.3154834894, 33.7145305579056], - [-118.315631149975, 33.7144446263349], - [-118.315785266742, 33.7143638318992], - [-118.315857553274, 33.7143313743712], - [-118.315947806113, 33.7142977489062], - [-118.316025005532, 33.7142736969766], - [-118.316103888393, 33.7142540821104], - [-118.316135048256, 33.7142484778621], - [-118.316200738459, 33.7142435745182], - [-118.316315835105, 33.714241939572], - [-118.316366785751, 33.7142356351654], - [-118.316414508751, 33.7142251268236], - [-118.316476829373, 33.7142073800301], - [-118.316737902253, 33.7141237832429], - [-118.316934970168, 33.714048592767], - [-118.316964446588, 33.7140350491339], - [-118.316990693564, 33.7140196374109], - [-118.317013712893, 33.7140021244596], - [-118.31703336354, 33.7139813416024], - [-118.317052874049, 33.7139446807286], - [-118.31708712232, 33.7138494081145], - [-118.317100175739, 33.7138241888755], - [-118.317127967817, 33.7137907963498], - [-118.317186919758, 33.7137405917152], - [-118.317213167632, 33.7137230779597], - [-118.317255837608, 33.713700193802], - [-118.317333036129, 33.7136712383284], - [-118.317387216218, 33.7136548924922], - [-118.317443079751, 33.7136415826827], - [-118.317494031296, 33.7136350443463], - [-118.317523507715, 33.7136352774847], - [-118.317546667181, 33.713640414749], - [-118.317563089283, 33.7136516233246], - [-118.317571370852, 33.7136712383284], - [-118.317571510989, 33.7137123364171], - [-118.317553825856, 33.7138253560594], - [-118.31755410613, 33.7138676220048], - [-118.317547790075, 33.7139367413482], - [-118.317546245871, 33.7139916160893], - [-118.317551439032, 33.7140322470027], - [-118.31755803626, 33.7140591011304], - [-118.317609408216, 33.7141770236643], - [-118.317617829921, 33.7142034107257], - [-118.317622882945, 33.7142433406342], - [-118.317613198208, 33.7142972818857], - [-118.317584002961, 33.714392086983], - [-118.31757923111, 33.7144464944134], - [-118.317584284134, 33.7144868919755], - [-118.317597758863, 33.71454106621], - [-118.317619374125, 33.7146080830533], - [-118.317672431321, 33.7147528587439], - [-118.317728856301, 33.7148684459857], - [-118.317763525881, 33.7149312594206], - [-118.317806476131, 33.7149882354748], - [-118.317827952155, 33.7150087842064], - [-118.317887325405, 33.7150566537653], - [-118.317976314314, 33.7151185330305], - [-118.318042283893, 33.715160564458], - [-118.318109798575, 33.7151993267561], - [-118.318239913256, 33.7152630747266], - [-118.31830083071, 33.715288993692], - [-118.318332131608, 33.7152992680224], - [-118.318361747266, 33.7153060401132], - [-118.318393048164, 33.7153088422028], - [-118.318424208924, 33.7153065071281], - [-118.318453825481, 33.7152999689185], - [-118.318484985343, 33.7152899277221], - [-118.318591660283, 33.7152446272511], - [-118.318622961181, 33.7152336527647], - [-118.318706475756, 33.7151556611661], - [-118.318753918481, 33.7151178328802], - [-118.318793359912, 33.7150951822287], - [-118.318867190649, 33.7150664611072], - [-118.318978918612, 33.7150379731108], - [-118.319108612882, 33.7150111192893], - [-118.319158020222, 33.7150038809059], - [-118.319223709527, 33.7150008449244], - [-118.319271432527, 33.7150055150904], - [-118.31930427673, 33.7150113531712], - [-118.319353684071, 33.7150241957519], - [-118.319399862866, 33.7150412422256], - [-118.319440989537, 33.7150627249771], - [-118.319465693207, 33.7150795375615], - [-118.31949376556, 33.7151115277907], - [-118.31954668172, 33.7152002607872], - [-118.319563244857, 33.7152243124572], - [-118.319583035641, 33.7152457951629], - [-118.319652373903, 33.7153044051872], - [-118.319776032392, 33.7153961739521], - [-118.319878216653, 33.7154678602688], - [-118.319921026767, 33.7155084904836], - [-118.31996903004, 33.7155671010758], - [-118.320002015279, 33.7156177716558], - [-118.320018718554, 33.7156560667345], - [-118.320025315781, 33.7156943617961], - [-118.320020543032, 33.7157193473531], - [-118.32001071726, 33.7157445660356], - [-118.319943764923, 33.7158452068117], - [-118.319922570072, 33.7158968119982], - [-118.319890006143, 33.7159892802731], - [-118.319860670759, 33.7160537277998], - [-118.319837791567, 33.7160901546414], - [-118.319782208309, 33.7161604393477], - [-118.319757645674, 33.7161961660024], - [-118.319736450823, 33.7162466030798], - [-118.319731678074, 33.7162872329261], - [-118.31973181911, 33.7163287967721], - [-118.319737152408, 33.7164126245433], - [-118.31973743358, 33.7165088283301], - [-118.3197409424, 33.7165637021746], - [-118.319744451219, 33.7166047988788], - [-118.319752872925, 33.7166594387846], - [-118.319772944882, 33.7167404641901], - [-118.319822632497, 33.7168572158988], - [-118.319857442214, 33.7169197947493], - [-118.31990699059, 33.7169900795238], - [-118.319936747284, 33.7170223029176], - [-118.320105040364, 33.7171745466145], - [-118.320220418183, 33.7172726175646], - [-118.320363867456, 33.7173853986452], - [-118.320426609388, 33.7174297639799], - [-118.320545215752, 33.7175068195068], - [-118.32059953508, 33.717538342576], - [-118.320670417546, 33.7175703318891], - [-118.320714912899, 33.7175857429748], - [-118.320810358898, 33.7176083929693], - [-118.321039007985, 33.7176487882965], - [-118.321121259529, 33.7176536921932], - [-118.321168842392, 33.7176492552987], - [-118.32120014329, 33.7176427172675], - [-118.321262463912, 33.7176233370458], - [-118.32138401675, 33.717580138944], - [-118.321553153348, 33.7175271345078], - [-118.321631896073, 33.7175070533819], - [-118.321807769138, 33.7174701601384], - [-118.32187008976, 33.7174524140156], - [-118.321960482736, 33.7174187897729], - [-118.322047366892, 33.717378160462], - [-118.322088492664, 33.7173543432708], - [-118.322162182365, 33.7173004039398], - [-118.322252295066, 33.7172191451868], - [-118.322311247007, 33.7171537645251], - [-118.32236360172, 33.7170839476372], - [-118.322402903013, 33.7170395821237], - [-118.322496243361, 33.7169510841575], - [-118.322535544655, 33.7169193277431], - [-118.322574986984, 33.7168943432825], - [-118.322601234858, 33.716884069143], - [-118.322625797493, 33.7168800992151], - [-118.322670291948, 33.7168903733552], - [-118.322719699288, 33.7169160586998], - [-118.322762650437, 33.7169499170161], - [-118.322774160551, 33.716961825295], - [-118.322789038448, 33.7169849424599], - [-118.322792407131, 33.7170061908486], - [-118.322782722394, 33.7170414501458], - [-118.322746649645, 33.7170921198557], - [-118.322686153501, 33.7171509624957], - [-118.322650079854, 33.7171785157815], - [-118.322595900662, 33.7172072369436], - [-118.322474346926, 33.717256272414], - [-118.32233496792, 33.7173298251935], - [-118.322254540854, 33.7173772264546], - [-118.322202045106, 33.7174110838418], - [-118.322080772542, 33.7175026164799], - [-118.322081193852, 33.717606991962], - [-118.322086246876, 33.71766163123], - [-118.322091299899, 33.7176884834753], - [-118.322117969083, 33.7177676406507], - [-118.322147865914, 33.7178325538437], - [-118.322175938267, 33.7178822890847], - [-118.322218888517, 33.7179408980565], - [-118.322258470984, 33.7179854962308], - [-118.322321212916, 33.7180487751325], - [-118.322423536417, 33.7181382052192], - [-118.322507613338, 33.7182035851312], - [-118.322595058042, 33.7182659298746], - [-118.322774721099, 33.7183868824927], - [-118.322908205361, 33.7184674392702], - [-118.323031723712, 33.7185356209159], - [-118.323132222735, 33.7185825545141], - [-118.323237634643, 33.7186220153891], - [-118.323405507312, 33.7186796898627], - [-118.323528885526, 33.7187161155901], - [-118.323640893764, 33.7187378305466], - [-118.323739568308, 33.718749505453], - [-118.323838242852, 33.7187553432792], - [-118.32398449936, 33.7187558102754], - [-118.32419827055, 33.7187492723285], - [-118.324413585944, 33.7187455363586], - [-118.324643779236, 33.7187562772716], - [-118.324839443084, 33.7187560433999], - [-118.325086058478, 33.7187639823347], - [-118.325184733022, 33.7187716881444], - [-118.325265301123, 33.7187824290541], - [-118.325492406006, 33.7188202557254], - [-118.325604274107, 33.7188419706556], - [-118.325666875903, 33.7188583155042], - [-118.325729336663, 33.7188776961944], - [-118.325877558685, 33.7189337356393], - [-118.325951669696, 33.7189647904424], - [-118.326081785274, 33.7190238656699], - [-118.326279414637, 33.7191207666484], - [-118.326398020102, 33.7191686339146], - [-118.326549470669, 33.719222104331], - [-118.326735449781, 33.7192795445311], - [-118.326872161689, 33.7193269447157], - [-118.327021927915, 33.7193832169902], - [-118.327194853607, 33.719460971626], - [-118.32728538672, 33.7194945950689], - [-118.32750435107, 33.7195567050051], - [-118.327657485978, 33.7196041042895], - [-118.327751247636, 33.7196302558138], - [-118.328177526985, 33.7197367297945], - [-118.3283963503, 33.7197958044908], - [-118.328844104773, 33.7199335665873], - [-118.32907626268, 33.7200017470686], - [-118.329207922463, 33.720047979008], - [-118.329282033474, 33.7200788002871], - [-118.329349547258, 33.7201163925192], - [-118.32949299653, 33.7202090901173], - [-118.329614971576, 33.7202814732708], - [-118.32965778169, 33.7203020207356], - [-118.329702276144, 33.7203169643432], - [-118.329781159904, 33.7203340090188], - [-118.329830567245, 33.7203407807124], - [-118.329897940891, 33.7203461506957], - [-118.329963770333, 33.7203473185381], - [-118.330027915434, 33.7203419478078], - [-118.3301560655, 33.7203150963924], - [-118.330205332703, 33.720307857709], - [-118.330285900804, 33.7203055227702], - [-118.330336852349, 33.720307857709], - [-118.330419103893, 33.7203178983186], - [-118.330483248994, 33.7203321410684], - [-118.330542482107, 33.7203531555085], - [-118.330601855358, 33.7203790736845], - [-118.330713863595, 33.7204379140716], - [-118.330768183822, 33.7204689683306], - [-118.330820959845, 33.7205025913786], - [-118.330870507323, 33.7205387832129], - [-118.330905036766, 33.7205682033568], - [-118.330936476903, 33.7205997245548], - [-118.331050451553, 33.7207346834487], - [-118.331113192587, 33.7208150049043], - [-118.331164424406, 33.7208684750424], - [-118.3312106041, 33.7209070008977], - [-118.331311103122, 33.7209782163761], - [-118.3315584201, 33.7211383917731], - [-118.332061056247, 33.7214491681759], - [-118.332209418406, 33.721534859619], - [-118.33231988244, 33.7215936992139], - [-118.332420241325, 33.7216401634126], - [-118.33253716245, 33.721689897195], - [-118.332762865063, 33.7217779225494], - [-118.332952212857, 33.7218605779253], - [-118.333050746365, 33.7218979362612], - [-118.329719540865, 33.7273772940915], - [-118.32848393604, 33.7294271769786], - [-118.321022585884, 33.7294012622818], - [-118.321015287072, 33.7308466571637], - [-118.321015146037, 33.7308552957494], - [-118.321014865762, 33.7308611320052], - [-118.321014444452, 33.7308669690077], - [-118.321014023142, 33.7308725721711], - [-118.321013321558, 33.7308784084257], - [-118.321012619974, 33.7308840115884], - [-118.321011778253, 33.7308898485893], - [-118.321010795496, 33.7308954517512], - [-118.321009672602, 33.7309012880043], - [-118.321008408672, 33.7309068911654], - [-118.321007145641, 33.7309124943262], - [-118.321005602335, 33.7309180974867], - [-118.321004058131, 33.7309237006467], - [-118.32100237379, 33.7309293038064], - [-118.321000548413, 33.7309349069658], - [-118.320998583798, 33.7309402770333], - [-118.320996619182, 33.7309458801919], - [-118.320994372496, 33.7309512495117], - [-118.320992127606, 33.7309568526696], - [-118.320989740782, 33.7309622227358], - [-118.320987354857, 33.7309675920546], - [-118.320984687759, 33.7309729621201], - [-118.32098202066, 33.7309780983469], - [-118.320979073288, 33.7309834676647], - [-118.320976125916, 33.7309886038908], - [-118.320973178543, 33.7309937401167], - [-118.320969949998, 33.7309988763422], - [-118.320966721453, 33.7310040125675], - [-118.320963352771, 33.7310089157012], - [-118.320959843951, 33.7310140519258], - [-118.320956335132, 33.7310189543119], - [-118.320952685277, 33.7310236236065], - [-118.320948895284, 33.7310285267391], - [-118.320944966053, 33.7310331960331], - [-118.320941035026, 33.7310380984181], - [-118.320936964759, 33.7310425346205], - [-118.320932754355, 33.7310472039138], - [-118.320928543053, 33.7310516393687], - [-118.320924191614, 33.7310563086615], - [-118.320919700038, 33.7310605110248], - [-118.320915208461, 33.7310649472261], - [-118.320910576748, 33.731069149589], - [-118.320905804897, 33.7310733519517], - [-118.320901032148, 33.7310775543142], - [-118.320896119261, 33.7310815228383], - [-118.320891207273, 33.7310854921094], - [-118.32088615425, 33.7310894606332], - [-118.320880960191, 33.7310931960658], - [-118.32087576703, 33.7310969314983], - [-118.320870433732, 33.7311006669306], - [-118.320865099536, 33.7311044023627], - [-118.320859625203, 33.7311079047037], - [-118.320854151768, 33.7311111732066], - [-118.320848537297, 33.7311146748003], - [-118.320842922827, 33.7311179433028], - [-118.320837167321, 33.7311212118053], - [-118.320831412713, 33.7311242472168], - [-118.320825517968, 33.731127281881], - [-118.320819622325, 33.7311303172922], - [-118.320813586545, 33.7311331188654], - [-118.320807551663, 33.7311359204385], - [-118.320801515882, 33.7311384881735], - [-118.320795339965, 33.7311410566555], - [-118.320789164047, 33.7311436243903], - [-118.320782847992, 33.7311459590343], - [-118.320378044667, 33.7312984115194], - [-118.320363447044, 33.7313040146546], - [-118.320348989558, 33.7313098508799], - [-118.320334672209, 33.7313159209422], - [-118.320320495895, 33.7313222248416], - [-118.320306319582, 33.7313287618308], - [-118.320292423543, 33.7313352988196], - [-118.320278528402, 33.7313423027356], - [-118.320264912637, 33.731349306651], - [-118.320251437908, 33.7313565436561], - [-118.320237963179, 33.7313640144979], - [-118.320224769622, 33.7313717191764], - [-118.320211716203, 33.7313794231071], - [-118.320198943058, 33.7313875947117], - [-118.320186169913, 33.7313957655685], - [-118.32017367794, 33.7314041702616], - [-118.320161326105, 33.7314128087912], - [-118.320149114407, 33.7314214465728], - [-118.320137183882, 33.7314305520279], - [-118.320125393494, 33.7314396567349], - [-118.320113743243, 33.731448995278], - [-118.320102374165, 33.7314583338201], - [-118.320091145224, 33.7314679061983], - [-118.32008005642, 33.7314777116654], - [-118.320069248788, 33.7314877502212], - [-118.320058721432, 33.731497789523], - [-118.320048334212, 33.7315080619134], - [-118.320038228165, 33.7315183343026], - [-118.320028263154, 33.7315290736173], - [-118.320018437381, 33.7315395790938], - [-118.320009032918, 33.7315505522427], - [-118.319999769491, 33.731561524643], - [-118.319990646201, 33.7315724977891], - [-118.319981803185, 33.7315837040234], - [-118.319973241342, 33.7315951433459], - [-118.319964959774, 33.7316065834139], - [-118.319956818342, 33.73161825657], - [-118.319948958084, 33.7316299297244], - [-118.319941378998, 33.7316418359668], - [-118.319934080186, 33.7316537429546], - [-118.319926921511, 33.7316658830303], - [-118.31992004401, 33.7316780231041], - [-118.319913446782, 33.7316901631763], - [-118.319907130727, 33.7317025363361], - [-118.319901094947, 33.7317149102412], - [-118.319895200202, 33.7317275172337], - [-118.319889725869, 33.7317401242245], - [-118.319884392571, 33.7317527312133], - [-118.319879338649, 33.7317655712895], - [-118.319874566798, 33.7317784121108], - [-118.319870075222, 33.7317912521832], - [-118.31986586392, 33.7318040930007], - [-118.319861933791, 33.7318171669053], - [-118.319858284834, 33.7318302408079], - [-118.319854776014, 33.7318433147085], - [-118.319851687606, 33.731856621696], - [-118.319848880371, 33.7318696955926], - [-118.319846213273, 33.7318830033231], - [-118.319843967485, 33.7318963103045], - [-118.319841862732, 33.7319096180309], - [-118.319840178391, 33.7319229250082], - [-118.319838774324, 33.7319364658191], - [-118.319837511293, 33.7319497735393], - [-118.319836668673, 33.7319630805103], - [-118.319835967089, 33.731976621315], - [-118.319835686815, 33.7319899290289], - [-118.319835545779, 33.7319966990558], - [-118.318542954527, 33.7319922636498], - [-118.318541130049, 33.7323501599106], - [-118.318524426774, 33.7347911945971], - [-118.318522322021, 33.7352555346597], - [-118.318749426905, 33.7351857317483], - [-118.319925378206, 33.7355109320902], - [-118.320313338119, 33.7354507013204], - [-118.320385343479, 33.735775199929], - [-118.31999724253, 33.7358351966458], - [-118.320061668804, 33.7361263107101], - [-118.318935827327, 33.736603249076], - [-118.318578606864, 33.7364225589404], - [-118.318517409135, 33.7363434197161], - [-118.318512215076, 33.7374817155775], - [-118.318518672166, 33.7374870844883], - [-118.318677561682, 33.737617581712], - [-118.318682193396, 33.7376215499337], - [-118.318694405094, 33.7376320554112], - [-118.318706335619, 33.7376430270339], - [-118.31871798587, 33.737653765582], - [-118.318729495086, 33.737664971022], - [-118.318740865062, 33.7376761764606], - [-118.31875181283, 33.7376876157179], - [-118.318762620462, 33.7376992880466], - [-118.318773147818, 33.7377109603738], - [-118.318783535038, 33.7377228657724], - [-118.318793641085, 33.7377350049892], - [-118.318803325822, 33.7377471442044], - [-118.318812870422, 33.7377595172378], - [-118.318822274885, 33.7377718895223], - [-118.318831258038, 33.7377844956249], - [-118.318839960018, 33.7377971017257], - [-118.318848521861, 33.7378099416444], - [-118.318856663292, 33.7378230146338], - [-118.318864663688, 33.7378360876213], - [-118.31887238381, 33.7378493936794], - [-118.318879682621, 33.7378627004825], - [-118.318886841296, 33.7378760065365], - [-118.318893718798, 33.7378895464079], - [-118.318900316025, 33.7379033200968], - [-118.318906491943, 33.737916859964], - [-118.318912526825, 33.7379306329014], - [-118.318918141295, 33.7379446396561], - [-118.318923615629, 33.7379586464084], - [-118.318928668652, 33.7379726531585], - [-118.318933581538, 33.7379866599062], - [-118.318938073115, 33.738000900471], - [-118.318942283518, 33.7380151402864], - [-118.31894396786, 33.7380212098743], - [-118.318947757852, 33.7380354504333], - [-118.318951266671, 33.7380499240621], - [-118.318954495216, 33.7380641638693], - [-118.318957302452, 33.7380786374932], - [-118.31895996955, 33.7380933449336], - [-118.318962215338, 33.7381078185526], - [-118.318964179954, 33.738122525241], - [-118.318965864295, 33.7381369988551], - [-118.318967268361, 33.7381517062855], - [-118.318967689671, 33.7381589427169], - [-118.318968251118, 33.7381736501436], - [-118.318968531393, 33.7381883568207], - [-118.318968531393, 33.7382030642424], - [-118.318968251118, 33.7382175378428], - [-118.318967549534, 33.7382322445124], - [-118.318966706914, 33.7382469519266], - [-118.318965443883, 33.7382616585911], - [-118.318963899679, 33.7382763660002], - [-118.318962075201, 33.7382908395883], - [-118.31894691613, 33.7384007917155], - [-118.318864102241, 33.7391716224878], - [-118.318857926323, 33.7392442234645], - [-118.318853154473, 33.7393170574484], - [-118.318850627512, 33.7393691146601], - [-118.31884936448, 33.7393901244389], - [-118.318848381724, 33.7394629582989], - [-118.318849083308, 33.7395357920971], - [-118.318851188959, 33.7396086258334], - [-118.318854838814, 33.7396812264402], - [-118.318860031974, 33.7397540600531], - [-118.318864242378, 33.7398016819973], - [-118.318898070237, 33.7402743980185], - [-118.318899052095, 33.7402811681387], - [-118.31890017499, 33.7402879375114], - [-118.318901438919, 33.7402947076306], - [-118.31890270195, 33.7403014770022], - [-118.318904246154, 33.7403082471203], - [-118.318905930496, 33.7403147834249], - [-118.318907754974, 33.740321552795], - [-118.318909860625, 33.7403285559774], - [-118.318911544966, 33.7403357929721], - [-118.318913509582, 33.7403425623405], - [-118.318915475095, 33.7403490986425], - [-118.318917720884, 33.7403558687569], - [-118.318919966672, 33.7403624050579], - [-118.318922352597, 33.7403691744241], - [-118.318925019695, 33.7403757107241], - [-118.318927685895, 33.7403822470236], - [-118.31893049313, 33.7403887833226], - [-118.318933441401, 33.7403950865553], - [-118.318936669048, 33.7404016228534], - [-118.318939897593, 33.7404079253382], - [-118.318943266275, 33.7404142285695], - [-118.318946775095, 33.7404205310534], - [-118.31895042495, 33.7404268342838], - [-118.318954214942, 33.7404329037012], - [-118.318958004036, 33.7404389731181], - [-118.318962075201, 33.7404450425346], - [-118.318966285605, 33.7404511119506], - [-118.318970496907, 33.7404569475537], - [-118.318974847448, 33.7404627839034], - [-118.318979480059, 33.7404686195057], - [-118.318984111773, 33.7404744558545], - [-118.318988884522, 33.7404800583905], - [-118.318993656373, 33.7404856609262], - [-118.318998709396, 33.740491029649], - [-118.31900376242, 33.7404966321839], - [-118.319009095718, 33.740502001653], - [-118.319014429914, 33.7405071373094], - [-118.319019763212, 33.7405125060308], - [-118.319025378581, 33.7405176416865], - [-118.319030992153, 33.7405225442766], - [-118.319036747659, 33.7405276799317], - [-118.319042642404, 33.7405323487088], - [-118.319048678184, 33.740537250551], - [-118.319054713964, 33.7405419193276], - [-118.319060889882, 33.740546588104], - [-118.3190670658, 33.7405510238148], - [-118.319073521992, 33.7405554587783], - [-118.319079838945, 33.7405598944887], - [-118.319086435274, 33.7405640963865], - [-118.319093032501, 33.7405682982841], - [-118.319099769866, 33.7405722663693], - [-118.319106507231, 33.7405762352013], - [-118.319113384732, 33.7405802032862], - [-118.319123069469, 33.7405853389378], - [-118.319657286789, 33.7408642981109], - [-118.31967160324, 33.7408720011892], - [-118.319683534663, 33.7408787712623], - [-118.319695325051, 33.7408855405878], - [-118.319706975302, 33.7408925437242], - [-118.319718484518, 33.7408997806714], - [-118.319729853596, 33.7409070168709], - [-118.319741082537, 33.7409144868811], - [-118.319752171341, 33.740922190702], - [-118.319762978972, 33.7409298937752], - [-118.319773646466, 33.7409380644702], - [-118.31978431396, 33.7409462344174], - [-118.31979470118, 33.7409544051108], - [-118.319804807227, 33.7409628088677], - [-118.319814913274, 33.7409714456878], - [-118.319824738148, 33.7409800832541], - [-118.319834282748, 33.7409889538837], - [-118.319843827348, 33.7409980575763], - [-118.319853091673, 33.7410071620151], - [-118.319862214963, 33.7410164995169], - [-118.319871057979, 33.7410258370177], - [-118.319879759959, 33.7410354075814], - [-118.319888321802, 33.7410449788911], - [-118.319896603371, 33.7410547832636], - [-118.319904603767, 33.7410648206989], - [-118.319912464025, 33.7410748588801], - [-118.319920184147, 33.741084896313], - [-118.319927623096, 33.7410951675556], - [-118.319934921907, 33.741105438797], - [-118.319941940445, 33.7411159438479], - [-118.319948677809, 33.7411264481506], - [-118.319955275037, 33.7411371862626], - [-118.319961731229, 33.7411479243733], - [-118.319967767009, 33.7411586624827], - [-118.319973662652, 33.7411696344013], - [-118.31997941726, 33.7411806055715], - [-118.319984890695, 33.7411918105509], - [-118.319990084754, 33.7412030155288], - [-118.31999499764, 33.7412142205052], - [-118.319999769491, 33.7412254254802], - [-118.320004261068, 33.7412368642642], - [-118.320008471471, 33.7412483022996], - [-118.320012542636, 33.7412599741439], - [-118.320016332628, 33.7412714129232], - [-118.320019841448, 33.7412830847644], - [-118.32002321013, 33.7412947566039], - [-118.32002629764, 33.7413064284419], - [-118.320029104875, 33.7413181002782], - [-118.320031631836, 33.7413300051762], - [-118.320033877624, 33.7413419108195], - [-118.320035983275, 33.741353582651], - [-118.320037807754, 33.741365487544], - [-118.320039351059, 33.7413773931824], - [-118.320040755126, 33.7413895318822], - [-118.320041737883, 33.7414014367702], - [-118.320042579604, 33.7414133424036], - [-118.320043281189, 33.7414252472883], - [-118.320043562361, 33.7414373859813], - [-118.320043562361, 33.7414492916097], - [-118.320043422224, 33.7414611964894], - [-118.320043000914, 33.7414733351773], - [-118.32004229933, 33.7414852408007], - [-118.32004145671, 33.7414971456755], - [-118.320040193679, 33.7415092843583], - [-118.32003879051, 33.7415211899767], - [-118.320037105271, 33.7415330948465], - [-118.320035140655, 33.7415450004616], - [-118.320033035004, 33.7415566722655], - [-118.31995429228, 33.7419630831134], - [-118.319951625182, 33.7419761554699], - [-118.319946010711, 33.7420020671157], - [-118.319939834794, 33.7420279780066], - [-118.319933237566, 33.7420538896368], - [-118.319925938755, 33.7420795674509], - [-118.319918219531, 33.7421050114493], - [-118.319909937963, 33.7421304561871], - [-118.319901094947, 33.7421559001704], - [-118.31989183152, 33.7421811110853], - [-118.319882005747, 33.742206088932], - [-118.319871619426, 33.7422310660244], - [-118.319860670759, 33.7422558100489], - [-118.319849161544, 33.7422803210056], - [-118.31983723012, 33.7423048312083], - [-118.319824878285, 33.7423291083436], - [-118.319811965003, 33.7423531524116], - [-118.319798490274, 33.7423769626656], - [-118.319784454996, 33.7424005391056], - [-118.319770137647, 33.7424241162862], - [-118.319755118713, 33.742447225846], - [-118.319739679369, 33.7424703361466], - [-118.319723818714, 33.742493212634], - [-118.319707535851, 33.7425156222483], - [-118.31961910839, 33.7426351400927], - [-118.319238447288, 33.742627203366], - [-118.318489617057, 33.7426218340299], - [-118.3184695451, 33.7456755523101], - [-118.318468422206, 33.7458450185854], - [-118.31846168574, 33.746847104015], - [-118.313502704199, 33.7468172260368], - [-118.313258896041, 33.7468162923498], - [-118.312188075476, 33.7468102233842], - [-118.312183865072, 33.747305076096], - [-118.311114869884, 33.747298773371], - [-118.311118940151, 33.7468039206228], - [-118.309462531208, 33.7467943507028], - [-118.309457197012, 33.7468662445892], - [-118.30945074082, 33.7469488757999], - [-118.30944161753, 33.7470422443037], - [-118.309419299785, 33.7471944347469], - [-118.309206511352, 33.7488365334038], - [-118.309134927302, 33.749389261299], - [-118.309133242063, 33.7519588860869], - [-118.309110925216, 33.7545632102], - [-118.30909955524, 33.7570803989857], - [-118.309099274965, 33.7577324988012], - [-118.301011910447, 33.7577261975895], - [-118.30109977736, 33.7580013659995], - [-118.301151150215, 33.7581656733167], - [-118.301890154673, 33.7605158869766], - [-118.309049867625, 33.7605203216545], - [-118.309046358805, 33.7625715125881], - [-118.310736594708, 33.7627764189785], - [-118.310764105613, 33.7627799200263], - [-118.310823057554, 33.7627878548877], - [-118.31088172922, 33.7627967232614], - [-118.310940399988, 33.762806525147], - [-118.310998650344, 33.7628170267927], - [-118.311056900701, 33.7628284626965], - [-118.311409769724, 33.7629003431384], - [-118.311413981026, 33.7629010428989], - [-118.31141819143, 33.7629017434062], - [-118.311421138802, 33.7629019764107], - [-118.311425350104, 33.7629022101621], - [-118.311428297477, 33.7629022101621], - [-118.311432508779, 33.7629022101621], - [-118.311435456151, 33.7629022101621], - [-118.311439667453, 33.7629017434062], - [-118.311444017994, 33.7629012766502], - [-118.311448229296, 33.762900576143], - [-118.311451036531, 33.7628998763825], - [-118.311455106798, 33.7628989428706], - [-118.311459177065, 33.7628975426028], - [-118.311463107194, 33.762896142335], - [-118.311467037323, 33.7628947420672], - [-118.311469564284, 33.7628935748039], - [-118.311473213241, 33.76289170778], - [-118.311475599166, 33.7628903075121], - [-118.31147798599, 33.7628889072442], - [-118.311481354672, 33.7628868072156], - [-118.311484582319, 33.7628844734356], - [-118.311487810864, 33.7628819059041], - [-118.311490758237, 33.7628793391194], - [-118.311492583613, 33.7628774720952], - [-118.311494408092, 33.762875605071], - [-118.311496934154, 33.7628728045347], - [-118.311498478358, 33.7628707037589], - [-118.311500724146, 33.7628676702177], - [-118.311502829797, 33.762864402925], - [-118.311503952692, 33.762862302149], - [-118.31150577717, 33.762859034856], - [-118.311506759927, 33.7628567010753], - [-118.311508022958, 33.7628534337821], - [-118.311509145852, 33.7628499334841], - [-118.311669018125, 33.7628826064116], - [-118.310870640416, 33.7656095846847], - [-118.308941788618, 33.7721971500265], - [-118.308922279007, 33.7722750897736], - [-118.308900802983, 33.7723688973991], - [-118.308880591788, 33.7724678386641], - [-118.308862905756, 33.7725583791554], - [-118.308846483655, 33.7726552196656], - [-118.308832026168, 33.7727497273018], - [-118.308817568682, 33.7728447007887], - [-118.30880507671, 33.7729399078943], - [-118.308794549353, 33.7730353486179], - [-118.308786127647, 33.7731310222118], - [-118.30877953042, 33.773226695699], - [-118.308775038843, 33.7733223690792], - [-118.308772652918, 33.7734180423527], - [-118.308772091471, 33.773513948495], - [-118.308773635675, 33.7736098552766], - [-118.308777144494, 33.773705528229], - [-118.308781636071, 33.7737827661857], - [-118.308787811988, 33.7738663048448], - [-118.308796935278, 33.7739619775108], - [-118.308808024082, 33.7740574170958], - [-118.308816445788, 33.7741260212556], - [-118.30893603401, 33.7749684011876], - [-118.306438998693, 33.7749632675953], - [-118.307894549728, 33.779572409146], - [-118.307910270245, 33.779568909531], - [-118.307942272727, 33.7795612091827], - [-118.308207135599, 33.7794968093654], - [-118.308267631743, 33.7794814094019], - [-118.30833065395, 33.7794653098105], - [-118.308379218671, 33.7794524761297], - [-118.309368208104, 33.7791932432813], - [-118.309752659197, 33.7819040699848], - [-118.309754764848, 33.7819187691608], - [-118.309758273668, 33.7819467682956], - [-118.309051411829, 33.7819458349913], - [-118.309050569209, 33.7822206926811], - [-118.308721561236, 33.7822202260304], - [-118.308712157672, 33.7851187780485], - [-118.308711876499, 33.7852562015388], - [-118.308710472432, 33.7857034695643], - [-118.308706683338, 33.7867960803452], - [-118.30869755915, 33.7896128207035], - [-118.308702612174, 33.7900117716625], - [-118.308702472036, 33.7900255362306], - [-118.308765073832, 33.7976945978999], - [-118.308763108318, 33.7979528416106], - [-118.308772793953, 33.7986372896412], - [-118.308797777898, 33.8017270358523], - [-118.308798760655, 33.8018753968322], - [-118.308803813679, 33.8025136260392], - [-118.308805216847, 33.8026619856555], - [-118.308807182361, 33.8029610368047], - [-118.308809288012, 33.8032605534449], - [-118.30881111249, 33.8034089117663], - [-118.308836939055, 33.8066319091094], - [-118.308838202086, 33.8067158822409], - [-118.308841289596, 33.8067996224049], - [-118.308846202482, 33.8068835953718], - [-118.308852799709, 33.8069673346251], - [-118.308861081278, 33.8070508409121], - [-118.308871187325, 33.8071343471175], - [-118.30888311785, 33.8072178532415], - [-118.308896592579, 33.80730089277], - [-118.308912031924, 33.8073839322179], - [-118.308924945206, 33.8074476116637], - [-118.308932385054, 33.807478868041], - [-118.308940104277, 33.8075094242643], - [-118.308948526881, 33.8075397475937], - [-118.308957650171, 33.8075700709122], - [-118.308967334908, 33.8076003942201], - [-118.308977581092, 33.807630251005], - [-118.308988529759, 33.8076601077795], - [-118.309000038974, 33.8076897316609], - [-118.309045516186, 33.8078005277823], - [-118.309070781303, 33.8078642069181], - [-118.309094643252, 33.8079283517706], - [-118.309117241271, 33.8079927302033], - [-118.309138436122, 33.8080575750974], - [-118.309158226906, 33.8081226535704], - [-118.309176754659, 33.8081879648752], - [-118.309193878345, 33.8082535090113], - [-118.309209598862, 33.8083195203521], - [-118.309240619485, 33.808455274196], - [-118.309227706203, 33.8084573731074], - [-118.309308133269, 33.8088093527959], - [-118.30893645532, 33.8088683653803], - [-118.308360410644, 33.8089957212685], - [-118.308605201559, 33.809767550249], - [-118.308616290363, 33.8098027706585], - [-118.308673136652, 33.8099910037108], - [-118.308726052813, 33.810180168599], - [-118.308774898706, 33.8103700331882], - [-118.308819955506, 33.8105605974734], - [-118.308860800105, 33.8107518607037], - [-118.308867397333, 33.8107861477181], - [-118.308895329548, 33.8147878816402], - [-118.30891694481, 33.8170360190882], - [-118.308974773857, 33.8230319308389], - [-118.309044674464, 33.8312756517139], - [-118.309043410535, 33.8315799658513], - [-118.309173807286, 33.847763506457], - [-118.30919668558, 33.8506178725329], - [-118.309210301345, 33.8522015703349], - [-118.309253111458, 33.8575154731258], - [-118.309260691442, 33.8584353597268], - [-118.309260270132, 33.8600680990735], - [-118.309258164481, 33.865695495181], - [-118.299331219145, 33.8657029543063], - [-118.299155486217, 33.8705360977897], - [-118.298929644365, 33.8705384286343], - [-118.296906890772, 33.870558473895], - [-118.29604970126, 33.8705612709077], - [-118.294367746027, 33.8705507817365], - [-118.294384027992, 33.8701170109005], - [-118.29445954307, 33.8681024278709], - [-118.29451849501, 33.8664740378746], - [-118.297693478092, 33.8664752037253], - [-118.297715935974, 33.8664749702568], - [-118.297738253719, 33.8664745040657], - [-118.297760711601, 33.8664735716835], - [-118.297783028448, 33.8664726393013], - [-118.29780548633, 33.8664710080053], - [-118.297827804075, 33.8664693759635], - [-118.297849981683, 33.8664672784763], - [-118.297872298529, 33.866464714052], - [-118.297894476137, 33.866461916905], - [-118.297916512709, 33.8664588870354], - [-118.29793855018, 33.866455623697], - [-118.297960586752, 33.8664518941673], - [-118.297982483187, 33.8664476984461], - [-118.298004238587, 33.8664435027248], - [-118.298025994885, 33.8664386073434], - [-118.298047611045, 33.8664337127076], - [-118.298069086171, 33.8664283511342], - [-118.298075121951, 33.8664269525601], - [-118.298080315112, 33.8655453801446], - [-118.291694416336, 33.865534891103], - [-118.290668652571, 33.8662947875799], - [-118.290635387057, 33.8663194957567], - [-118.290903197301, 33.8725846521596], - [-118.290972535563, 33.8739257774225], - [-118.291288630254, 33.8815973360305], - [-118.291336072979, 33.8825871120728], - [-118.29136681243, 33.8833286788789], - [-118.291373689931, 33.88349344436], - [-118.291591250215, 33.8887683474298], - [-118.291800108519, 33.8941256461043], - [-118.2917011537, 33.8945933156437], - [-118.291668309497, 33.9055025556137], - [-118.291645149132, 33.9133369149512], - [-118.291635184121, 33.916576732341], - [-118.291611602446, 33.9219539479365], - [-118.29160402336, 33.9236891294872], - [-118.291597145858, 33.9251454530444], - [-118.291692731994, 33.9258677825279], - [-118.291672379763, 33.9301979018193], - [-118.291661992544, 33.9343198120891], - [-118.291652869254, 33.9382102543248], - [-118.29164220176, 33.9430402205559], - [-118.291641920587, 33.9437572740618], - [-118.291638833077, 33.9445902976143], - [-118.291637008599, 33.9454969022463], - [-118.291636867564, 33.9466855112863], - [-118.291629990062, 33.9508073223541], - [-118.291620866772, 33.9549293995042], - [-118.291611322172, 33.9594992647289], - [-118.292432438037, 33.9594971695302], - [-118.292485214958, 33.959519056606], - [-118.300300838205, 33.9594978676814], - [-118.300269116895, 33.9527384506152], - [-118.300273748609, 33.9500037016698], - [-118.302413703601, 33.9499976471213], - [-118.302461988047, 33.9486649369949], - [-118.302464093698, 33.9470576468333], - [-118.302464233835, 33.9467416348337], - [-118.302464795282, 33.9463981414214], - [-118.302464795282, 33.9463569225361], - [-118.302465356729, 33.9460150580911], - [-118.302465918176, 33.9454792033969], - [-118.305192445201, 33.9454715187417], - [-118.305203112695, 33.9418436423687], - [-118.309027409743, 33.9418378197567], - [-118.309030918562, 33.9400245221506], - [-118.30903709448, 33.938215145587], - [-118.313399397941, 33.9382184061798], - [-118.31339420478, 33.9400303448866], - [-118.313388730447, 33.9418427108104], - [-118.313387467416, 33.9431999804035], - [-118.313384238871, 33.9454649981115], - [-118.31345708685, 33.9454649981115], - [-118.31774920943, 33.9454682584266], - [-118.317754121418, 33.9549352212206], - [-118.317756086932, 33.9590570984067], - [-118.317753279697, 33.9627189326928], - [-118.317743313787, 33.9708134334645], - [-118.317759735889, 33.9708132002817], - [-118.321572522823, 33.9708036554319], - [-118.321580242944, 33.9708036554319], - [-118.321584032936, 33.9708034222491], - [-118.322629166176, 33.9708008617081], - [-118.322654291156, 33.9708008617081], - [-118.32658245501, 33.9707908504913], - [-118.326591438163, 33.967271150821], - [-118.333079240028, 33.967254155333], - [-118.333070958459, 33.9699774072463], - [-118.333069835565, 33.9714236266617], - [-118.333076011483, 33.9726961490309], - [-118.333075871346, 33.972708720507], - [-118.33313510356, 33.9727080224642], - [-118.335223967769, 33.9726959165982], - [-118.335223546459, 33.9744209851267], - [-118.335222283428, 33.9745033964315], - [-118.335318571148, 33.9745029308311], - [-118.335314220607, 33.9755391177656], - [-118.335300324568, 33.97966744569], - [-118.335292464309, 33.9817070925304], - [-118.335358434787, 33.9817066269695], - [-118.338487098038, 33.9816894012138], - [-118.338652023335, 33.9816882376837], - [-118.339570972022, 33.9816831165123], - [-118.339571392434, 33.9817385182589], - [-118.343966821271, 33.9817138435359], - [-118.35303166421, 33.9816891688057], - [-118.357402950824, 33.9816768310655], - [-118.357527732207, 33.9816765986574], - [-118.357728590114, 33.9816758999434], - [-118.35855223294, 33.9816735721379], - [-118.358666347728, 33.9816724086076], - [-118.360003713709, 33.9816686841187], - [-118.360183236628, 33.9816682185575], - [-118.361492812327, 33.9816647264766], - [-118.36601596992, 33.981650992794], - [-118.366187632581, 33.9816505272328], - [-118.366264269655, 33.9816502940798], - [-118.36673841214, 33.9818996021234], - [-118.366939269149, 33.9816379570788], - [-118.366958358349, 33.9816481994267], - [-118.37016281785, 33.9816384226401], - [-118.370169133905, 33.9796616264112], - [-118.370170397835, 33.9780833222796], - [-118.37017236245, 33.9772266493615], - [-118.37014808009, 33.9714178068186], - [-118.370135306945, 33.9683970608065], - [-118.370135166808, 33.968243634703], - [-118.370134043914, 33.9679977792567], - [-118.372378568194, 33.9679933553422], - [-118.372834744374, 33.9676529747362], - [-118.372756843371, 33.96755984711], - [-118.372703365763, 33.9674927951561], - [-118.372651291325, 33.9674250450631], - [-118.372600620953, 33.9673565960844], - [-118.372551353749, 33.9672874482182], - [-118.37250363075, 33.9672173697604], - [-118.372477382876, 33.9671773246885], - [-118.371930253172, 33.9673915190396], - [-118.371628615968, 33.966856963435], - [-118.371251463685, 33.9633715599022], - [-118.375203769763, 33.9617880634887], - [-118.375304128648, 33.96174661885], - [-118.375422032529, 33.9616958607819], - [-118.375520425901, 33.9616511564033], - [-118.375617837413, 33.9616052881963], - [-118.375714406306, 33.9615577897426], - [-118.375809852305, 33.9615091267111], - [-118.375904315547, 33.9614588341731], - [-118.375997655895, 33.9614073777977], - [-118.376090014385, 33.9613545236291], - [-118.376181249081, 33.9613002731548], - [-118.376271221645, 33.9612448580909], - [-118.376360070417, 33.961187812762], - [-118.376447656157, 33.9611298367927], - [-118.376534119003, 33.9610704630171], - [-118.376619318818, 33.9610096929225], - [-118.376703114566, 33.9609479906901], - [-118.376785647283, 33.9608848921334], - [-118.376866916968, 33.9608206289691], - [-118.376946782587, 33.9607552011946], - [-118.377025245037, 33.9606886095523], - [-118.37710230342, 33.9606208540396], - [-118.377177958635, 33.9605519339084], - [-118.377252209783, 33.9604820823672], - [-118.377324916727, 33.9604110662023], - [-118.377396220503, 33.9603391193673], - [-118.377465839938, 33.9602660079032], - [-118.377534056204, 33.9601919650188], - [-118.37760058723, 33.9601169907117], - [-118.377670487838, 33.9600352636124], - [-118.377733791217, 33.9599581939798], - [-118.377886223643, 33.9597600465363], - [-118.377943772415, 33.959679949352], - [-118.37799949581, 33.9595989207283], - [-118.378053675002, 33.9595171938763], - [-118.378106030613, 33.9594347680491], - [-118.378156700985, 33.9593514107753], - [-118.378205546879, 33.959267587736], - [-118.378252708431, 33.9591828332457], - [-118.378298045505, 33.9590976129863], - [-118.37834169734, 33.9590114612711], - [-118.378383385457, 33.958924843783], - [-118.378423388335, 33.9588377605206], - [-118.378461426597, 33.9587499782666], - [-118.37849777962, 33.95866173098], - [-118.378532168926, 33.958572784698], - [-118.37856459182, 33.9584836051064], - [-118.37859533127, 33.958393727261], - [-118.378624106105, 33.9583036168487], - [-118.378651054666, 33.9582130399063], - [-118.378676039509, 33.9581219979225], - [-118.378690355959, 33.9580658818802], - [-118.378907777004, 33.9580109304002], - [-118.378898652816, 33.9553517951817], - [-118.378897811094, 33.9550961226351], - [-118.378888968079, 33.9525619427352], - [-118.370939299124, 33.9525833658747], - [-118.370177695748, 33.9525859269641], - [-118.370176152443, 33.9520526754906], - [-118.370161694956, 33.9491418552718], - [-118.370151588909, 33.949142088514], - [-118.370139938659, 33.94074672544], - [-118.370045194244, 33.94074672544], - [-118.370043229628, 33.9384426877151], - [-118.370040142119, 33.9345298967835], - [-118.37013249971, 33.9345291984274], - [-118.370129552337, 33.9308372683484], - [-118.368388925439, 33.9308391317058], - [-118.368386960823, 33.9290255998649], - [-118.378736535653, 33.9290125560848], - [-118.378738219994, 33.9301958051538], - [-118.378738921579, 33.9309094734163], - [-118.396168343741, 33.9308831538842], - [-118.399265145546, 33.930878029654], - [-118.399265425821, 33.9309788833917], - [-118.400739364471, 33.9311528742929], - [-118.400964224465, 33.9311777962292], - [-118.401189645007, 33.9311992247507], - [-118.401415346722, 33.9312176256974], - [-118.401641469747, 33.9312325324885], - [-118.401867873047, 33.9312444109632], - [-118.402094557519, 33.9312527960309], - [-118.402321382128, 33.9312579202386], - [-118.402548206737, 33.9312597835868], - [-118.402586103964, 33.9312597835868], - [-118.414394178099, 33.9312388209176], - [-118.414988189081, 33.9312048147989], - [-118.415971844317, 33.9312029514496], - [-118.415971984454, 33.9312360258947], - [-118.416269972702, 33.9312353275116], - [-118.42496187466, 33.9312225166158], - [-118.42532920117, 33.9312157623483], - [-118.42549875818, 33.9312101723008], - [-118.425668175052, 33.9312020197749], - [-118.425837311651, 33.9311915388065], - [-118.426006306315, 33.9311787279042], - [-118.426175021604, 33.9311631227199], - [-118.426343315582, 33.9311454208907], - [-118.426511188251, 33.9311249240312], - [-118.426574490732, 33.9311167714971], - [-118.428278623572, 33.9308882781141], - [-118.428266552012, 33.9308256223639], - [-118.428776205798, 33.9308244581381], - [-118.428776205798, 33.9308225947804], - [-118.428776205798, 33.9307424703615], - [-118.428766521061, 33.9272062065413], - [-118.427679278395, 33.927208302535], - [-118.42767254103, 33.9235871020476], - [-118.426586000846, 33.9235880338056], - [-118.426583193611, 33.9219243636751], - [-118.426582912438, 33.9217734157997], - [-118.424408428902, 33.9217748134664], - [-118.424406603525, 33.9202089464523], - [-118.424402954568, 33.9199631855541], - [-118.424242380711, 33.9199631855541], - [-118.423947621009, 33.9199617878576], - [-118.422234505016, 33.9199617878576], - [-118.422235909083, 33.9189859568107], - [-118.422235909083, 33.9188485149727], - [-118.422234926326, 33.9178794272465], - [-118.422234926326, 33.917824449824], - [-118.422234786189, 33.9177007508663], - [-118.422233241985, 33.9163342223968], - [-118.427186327882, 33.9163263017792], - [-118.427348305806, 33.9163260684471], - [-118.427401502241, 33.9163258358605], - [-118.429458925414, 33.9163225744295], - [-118.429489103418, 33.9163591486686], - [-118.429568547726, 33.916447673142], - [-118.429704417913, 33.916585352184], - [-118.429775861826, 33.9166799333405], - [-118.429805619418, 33.9167127800937], - [-118.429827094543, 33.9167318826717], - [-118.429866816249, 33.9167561103254], - [-118.429909767397, 33.9167752128937], - [-118.429974193671, 33.9167945487886], - [-118.430038478909, 33.916808758857], - [-118.430092939274, 33.9168143498496], - [-118.430249442864, 33.9168124861854], - [-118.43027260233, 33.9168164468444], - [-118.43028256824, 33.9168322879877], - [-118.430274426809, 33.9168488276291], - [-118.430256320366, 33.9168688620081], - [-118.430233441174, 33.9168907600449], - [-118.430157926995, 33.9169478350615], - [-118.430100378222, 33.9169858067775], - [-118.430085640462, 33.916993028834], - [-118.430052656121, 33.9170051426246], - [-118.42994738435, 33.9170340293491], - [-118.429920996338, 33.9170461431339], - [-118.429903030033, 33.9170617509044], - [-118.429894888601, 33.9170820185633], - [-118.429906678989, 33.9171325699065], - [-118.429951594754, 33.9172175995954], - [-118.42998163262, 33.9172825941771], - [-118.430051392192, 33.9174074588965], - [-118.430089710728, 33.9174831700641], - [-118.430133083187, 33.9175726251935], - [-118.430168173179, 33.9176646423689], - [-118.430186700931, 33.9177457109113], - [-118.430207194198, 33.9178957341023], - [-118.430216177351, 33.9180329448952], - [-118.430218563276, 33.9181843653061], - [-118.430234002621, 33.9183078309805], - [-118.430247618386, 33.9183756201756], - [-118.430264320762, 33.9184285010328], - [-118.43028607706, 33.918479517484], - [-118.430319343471, 33.9185426482223], - [-118.430370855565, 33.9186297727089], - [-118.430472197207, 33.9188026236993], - [-118.430497181151, 33.918852941442], - [-118.430517253108, 33.9189053553529], - [-118.430542378088, 33.9189850250023], - [-118.430597820311, 33.9191723182707], - [-118.430607786221, 33.9191984088426], - [-118.430641051734, 33.9192610731455], - [-118.430680914475, 33.9193211745668], - [-118.430748990604, 33.9194159852648], - [-118.430855384371, 33.9195860392551], - [-118.430933425511, 33.9197074067202], - [-118.430974972593, 33.919767274504], - [-118.431116035941, 33.9199529357795], - [-118.431213868763, 33.9200868812272], - [-118.431315209507, 33.9202438887556], - [-118.431421463137, 33.9203994983054], - [-118.431451360866, 33.9204484174165], - [-118.431491363744, 33.9205238925614], - [-118.431546385556, 33.9206389687339], - [-118.431634813915, 33.9208455933421], - [-118.431683098362, 33.9209480899584], - [-118.43174134782, 33.9210615345604], - [-118.43188269234, 33.9213093891747], - [-118.432063899601, 33.9216038320651], - [-118.432180259278, 33.9218004373526], - [-118.432409469813, 33.9221507849867], - [-118.432512635933, 33.9223226970621], - [-118.432587448528, 33.9224596674105], - [-118.432652296112, 33.922571013892], - [-118.432750408311, 33.9227296478081], - [-118.43279855262, 33.9228023256324], - [-118.432901578603, 33.9229742363931], - [-118.432948178709, 33.9230620547981], - [-118.432966566324, 33.9231179606042], - [-118.432980040155, 33.9231757298985], - [-118.432990146202, 33.9232020525577], - [-118.433003480794, 33.9232251132965], - [-118.433033237488, 33.9232509700596], - [-118.433076188637, 33.9232698378587], - [-118.433110857318, 33.9232782237115], - [-118.433145526898, 33.923283581712], - [-118.433214725023, 33.9232854452346], - [-118.433262448023, 33.9232786895922], - [-118.433308486681, 33.9232644806028], - [-118.433477903554, 33.9231862125987], - [-118.433581069674, 33.9233734966316], - [-118.433694060669, 33.9235575190987], - [-118.433732379206, 33.923633224056], - [-118.433760732731, 33.9236977486106], - [-118.433959484987, 33.924207184738], - [-118.43400973405, 33.9243665140468], - [-118.434050159136, 33.9245279398626], - [-118.434070230194, 33.9245947923616], - [-118.43415711435, 33.9248163152897], - [-118.434210591957, 33.924960735573], - [-118.43429242309, 33.9251559355021], - [-118.4343856233, 33.9253471747217], - [-118.434547320052, 33.9257065925575], - [-118.434580725702, 33.9257848582459], - [-118.434612588047, 33.9258770998579], - [-118.434657644847, 33.9259947306889], - [-118.43480586597, 33.9263005714309], - [-118.434852607111, 33.9263883864069], - [-118.434918998, 33.9264994951945], - [-118.435003916642, 33.926663711396], - [-118.435095573548, 33.9268542488115], - [-118.435202247589, 33.9270857814788], - [-118.435220635205, 33.9271386569297], - [-118.435230741252, 33.9271787208437], - [-118.435237618754, 33.9272197164572], - [-118.435249690314, 33.9273291932828], - [-118.435256989126, 33.9274666214392], - [-118.435268919651, 33.9275481465119], - [-118.435279165835, 33.9276024185016], - [-118.435299377929, 33.9276837108887], - [-118.435329555933, 33.9277906245373], - [-118.43538303354, 33.9279348072228], - [-118.435559749225, 33.9283056271714], - [-118.435634702855, 33.9284726350045], - [-118.435681443098, 33.9285895638463], - [-118.435724954795, 33.9287076575278], - [-118.435791626857, 33.92886348417], - [-118.435816751837, 33.9289291690146], - [-118.435841876818, 33.9290083634408], - [-118.435920899816, 33.9293034788648], - [-118.436006099631, 33.9295198644901], - [-118.436064349988, 33.9296186239017], - [-118.436119231662, 33.9297187807352], - [-118.436270962503, 33.930053024572], - [-118.436354336941, 33.9302468153038], - [-118.436414552811, 33.9304040366551], - [-118.43646971476, 33.9305617243024], - [-118.436509857775, 33.9306667709772], - [-118.436578214178, 33.9308209639697], - [-118.436671555424, 33.9310119579238], - [-118.436706505279, 33.9310795044636], - [-118.436718154631, 33.9311004671719], - [-118.4367264362, 33.9311165389509], - [-118.436809670501, 33.9312618802261], - [-118.43681963641, 33.9312784170661], - [-118.436916205304, 33.9314631215822], - [-118.436924486872, 33.9314798916738], - [-118.436985964875, 33.9315774838517], - [-118.436999299467, 33.9315975147671], - [-118.437015862605, 33.931626629834], - [-118.437044215232, 33.9316906817536], - [-118.437105974407, 33.9318427770127], - [-118.437122677682, 33.9318863320934], - [-118.437194542904, 33.9320498395326], - [-118.437212789484, 33.9320933952528], - [-118.437247880374, 33.9321798067604], - [-118.437286198911, 33.9322603956586], - [-118.437296164821, 33.9322815913647], - [-118.437311183754, 33.9323111712836], - [-118.437374346098, 33.9324180798688], - [-118.437391049373, 33.9324469613672], - [-118.437462493286, 33.9325561981863], - [-118.437490705776, 33.9326004520194], - [-118.437500671685, 33.9326167560573], - [-118.43767387855, 33.9329386437109], - [-118.437682160119, 33.9329556467984], - [-118.437733813248, 33.9330711720041], - [-118.437743918396, 33.933091900936], - [-118.437772131784, 33.9331466359651], - [-118.437783782035, 33.9331673648787], - [-118.437793747047, 33.9331834362675], - [-118.437851997403, 33.9332726416252], - [-118.43788680712, 33.9333250474095], - [-118.437993200888, 33.9335008960712], - [-118.438044854016, 33.9335973217451], - [-118.438053135585, 33.9336140914164], - [-118.438154898537, 33.933826274428], - [-118.438277995579, 33.934056390677], - [-118.438302979523, 33.934102273733], - [-118.438356176856, 33.9341931088743], - [-118.438404461303, 33.9342746275084], - [-118.438422707883, 33.9343035083772], - [-118.438484186784, 33.9343959740461], - [-118.438519136639, 33.9344586264242], - [-118.438527418207, 33.9344746975693], - [-118.438537384117, 33.9344961252613], - [-118.43856419254, 33.9345611066606], - [-118.438597739226, 33.9346673133209], - [-118.43864616381, 33.9348037980012], - [-118.438657955096, 33.9348392001352], - [-118.438703151135, 33.9349852341547], - [-118.438708204158, 33.9350031677214], - [-118.438719994546, 33.9350383372369], - [-118.438746662832, 33.9351028524064], - [-118.43880659753, 33.9352216354067], - [-118.438816563439, 33.9352428303758], - [-118.438896429058, 33.935384438093], - [-118.438908079309, 33.935404468113], - [-118.438992997951, 33.9355684346107], - [-118.439008016884, 33.9355975475747], - [-118.439063038695, 33.9356983964684], - [-118.439116236028, 33.9357985461518], - [-118.439191188761, 33.9359510991402], - [-118.439247894913, 33.9360694151465], - [-118.439284529108, 33.9361416157783], - [-118.439394432594, 33.9363421472619], - [-118.439419417437, 33.9364078263384], - [-118.439459701487, 33.9365408142953], - [-118.43949647582, 33.936632112643], - [-118.439531425674, 33.9367092041463], - [-118.439586447486, 33.9368093526408], - [-118.439644557705, 33.9368931978013], - [-118.439682736104, 33.9369539851185], - [-118.439727792006, 33.9370420223854], - [-118.439786042362, 33.9371710503846], - [-118.439939878854, 33.9375765327817], - [-118.440006691054, 33.9377321106679], - [-118.440086556672, 33.9378830302619], - [-118.44013301664, 33.9379559276838], - [-118.44016122913, 33.9379899311065], - [-118.440232532906, 33.9380665555806], - [-118.44024923618, 33.9380898455674], - [-118.440265798419, 33.9381275753325], - [-118.441684153811, 33.9401414351313], - [-118.444231298952, 33.9432831202411], - [-118.444420927918, 33.9436105568061], - [-118.444805097839, 33.9442547135417], - [-118.445006517194, 33.9446017084615], - [-118.445409355005, 33.9453418043096], - [-118.445880690255, 33.9462523602277], - [-118.446032280959, 33.9465292515043], - [-118.446230331631, 33.9468778669099], - [-118.447008777521, 33.9481970984339], - [-118.447236725024, 33.9485780764757], - [-118.447635913878, 33.9492370992575], - [-118.447996784196, 33.9498218317871], - [-118.448015311949, 33.9498809801723], - [-118.448040295893, 33.949938731312], - [-118.448068649419, 33.9499943869796], - [-118.448106827818, 33.950046781726], - [-118.448159884116, 33.950093355138], - [-118.448204519605, 33.9501219981464], - [-118.448252382742, 33.9501431886607], - [-118.44830193022, 33.9501590236057], - [-118.448349794255, 33.9501646124091], - [-118.44839597305, 33.9501587911115], - [-118.448478224594, 33.9501406274978], - [-118.448521035606, 33.9501352711871], - [-118.448565670197, 33.9501366683883], - [-118.448603568323, 33.9501438876338], - [-118.44866308171, 33.9501688040114], - [-118.448691154063, 33.9501846389517], - [-118.448714313529, 33.9502046654895], - [-118.448729332462, 33.9502281853947], - [-118.448734385486, 33.9502514720543], - [-118.448731297078, 33.9502766216395], - [-118.448721472204, 33.9503017712172], - [-118.448706733545, 33.9503255235893], - [-118.448654238695, 33.950370932145], - [-118.44860019964, 33.9504268200236], - [-118.448560757311, 33.9504719960318], - [-118.448465732622, 33.9505609507822], - [-118.448683995388, 33.9510092164793], - [-118.448897345268, 33.9514393157335], - [-118.449097360556, 33.9518312241601], - [-118.449278848091, 33.9521681747832], - [-118.449435211545, 33.9524308419457], - [-118.449634665386, 33.9527382173829], - [-118.449890685241, 33.9531249969363], - [-118.451036178264, 33.9548874858165], - [-118.451416979503, 33.9554919723356], - [-118.45200734063, 33.9564401413061], - [-118.452218584859, 33.9567691575676], - [-118.45242140828, 33.9570765173336], - [-118.452605703051, 33.9573307868462], - [-118.452755047967, 33.9575047231476], - [-118.452882776722, 33.9576360485935], - [-118.453050228979, 33.957820928399], - [-118.453241120977, 33.9580456247198], - [-118.453445346669, 33.958298959958], - [-118.453644660372, 33.9585685935096], - [-118.453789233437, 33.9587867675011], - [-118.45390404891, 33.9589914368059], - [-118.45400244318, 33.9591858598207], - [-118.454085957756, 33.9593716676248], - [-118.454162735865, 33.9595546809299], - [-118.454411316772, 33.9597761129048], - [-118.454621579142, 33.959951441247], - [-118.454704392133, 33.9600110482709], - [-118.454856684422, 33.960111402562], - [-118.45506834996, 33.9602438878078], - [-118.455187516873, 33.9603116443659], - [-118.455303175864, 33.960372880637], - [-118.455516384708, 33.9604678788409], - [-118.456122888561, 33.9607049082108], - [-118.45619882405, 33.9607316849442], - [-118.456636612614, 33.9608709218844], - [-118.456770237012, 33.9609030534536], - [-118.456844628297, 33.9609135307641], - [-118.456874244854, 33.9609149277884], - [-118.456925336536, 33.9609130650893], - [-118.456950040206, 33.9609088740162], - [-118.457015869649, 33.9608932742816], - [-118.457085067773, 33.9608709218844], - [-118.457152581557, 33.9608453097553], - [-118.457210130329, 33.9608178349173], - [-118.457274135293, 33.9607840730792], - [-118.45730543619, 33.9607708016996], - [-118.457443692303, 33.960716084432], - [-118.457494643847, 33.9606997857756], - [-118.457521031859, 33.9606979230719], - [-118.457544191325, 33.9607032787177], - [-118.45757563236, 33.9607156187562], - [-118.45761030194, 33.9607426279514], - [-118.457612126418, 33.9607572971075], - [-118.457606371811, 33.9607787181836], - [-118.458206979121, 33.9614213479586], - [-118.45903483325, 33.9610329766315], - [-118.460003610588, 33.9605752177428], - [-118.460074211881, 33.9605405244548], - [-118.460200818641, 33.960454839878], - [-118.460241804275, 33.9604315560108], - [-118.460279701502, 33.9604166543324], - [-118.460317599628, 33.9604094367035], - [-118.460337390412, 33.9604059437494], - [-118.46037037565, 33.9604075739934], - [-118.460391850776, 33.9604150248337], - [-118.460409957219, 33.9604255022042], - [-118.460446451277, 33.9604539085235], - [-118.460468067438, 33.9604923272635], - [-118.460474804802, 33.9605118856985], - [-118.460476489143, 33.9605335393023], - [-118.460471716394, 33.9605530977278], - [-118.46046189152, 33.9605733550363], - [-118.460447152861, 33.960591516423], - [-118.46041767734, 33.960622017858], - [-118.46040293958, 33.9606308657085], - [-118.460374867227, 33.9606345911189], - [-118.46029584333, 33.960651355464], - [-118.458401099664, 33.9615154136488], - [-118.459804998469, 33.9636828535492], - [-118.459806401637, 33.96368494939], - [-118.461661422699, 33.962804614989], - [-118.461735393572, 33.9627608425224], - [-118.461774835003, 33.9627319713087], - [-118.46181918932, 33.962704962745], - [-118.461865227979, 33.9626828440289], - [-118.461911266637, 33.9626751601821], - [-118.461932742661, 33.9626791187081], - [-118.46198397448, 33.9626984434353], - [-118.462013871311, 33.9627238225466], - [-118.462027205903, 33.9627631708457], - [-118.462034223542, 33.9628230091041], - [-118.462024398667, 33.9628483874332], - [-118.462006432362, 33.9628753959513], - [-118.461994922248, 33.9628877364197], - [-118.461970359613, 33.9629061297718], - [-118.461924179919, 33.9629224280054], - [-118.461841928375, 33.9629443142], - [-118.46049964861, 33.9635461827161], - [-118.460356619749, 33.9636081155214], - [-118.460287561761, 33.9636444369952], - [-118.460221872456, 33.9636863463688], - [-118.460157727355, 33.9637350074097], - [-118.460106915947, 33.9637859974631], - [-118.460059474121, 33.9638418765303], - [-118.460020172827, 33.9638979880175], - [-118.459990837443, 33.963950141746], - [-118.459849352786, 33.9643326794267], - [-118.459807244257, 33.9644614328556], - [-118.459776644943, 33.9645878585613], - [-118.459751099551, 33.9647552608731], - [-118.459742116399, 33.9649233617072], - [-118.459740993505, 33.965006015415], - [-118.459690603407, 33.9654425630166], - [-118.459673479721, 33.9656372044845], - [-118.459667864352, 33.965803208532], - [-118.45967179538, 33.9659291658511], - [-118.459687374862, 33.9660413867795], - [-118.459741133642, 33.9665421887024], - [-118.459765275865, 33.9667354307251], - [-118.459796155453, 33.9669563776213], - [-118.459833632268, 33.9671549739426], - [-118.459877425138, 33.9673405311476], - [-118.459932867361, 33.9675223635843], - [-118.460003189278, 33.967711412642], - [-118.460096669763, 33.9679290975482], - [-118.460201800499, 33.9681505077236], - [-118.460313528463, 33.9683609745518], - [-118.460421607469, 33.9685430372506], - [-118.460519720566, 33.9686780707142], - [-118.460611095401, 33.9687965736701], - [-118.46071903427, 33.9689476713673], - [-118.460840446971, 33.9691236796227], - [-118.461109801419, 33.9695283109767], - [-118.461222932551, 33.9697082754843], - [-118.461321045648, 33.9698698477688], - [-118.461582398801, 33.9703212710962], - [-118.461712234106, 33.9705340606627], - [-118.461993378942, 33.9709689505932], - [-118.46225936381, 33.9713488954359], - [-118.46268297606, 33.9719111265045], - [-118.462918783822, 33.9722296062679], - [-118.463168066313, 33.9725750902764], - [-118.463422401828, 33.9729436208978], - [-118.463597151999, 33.9732097155996], - [-118.4640446253, 33.9739130131173], - [-118.464207585082, 33.9741541954396], - [-118.464418549037, 33.9744393759845], - [-118.464644531026, 33.9747294447364], - [-118.464871917083, 33.9749990262044], - [-118.465079372218, 33.9752304279227], - [-118.465251736463, 33.9753975768607], - [-118.465362762842, 33.9755000078023], - [-118.465497088825, 33.9756343321999], - [-118.465649802423, 33.9757963588178], - [-118.465812340896, 33.975974215267], - [-118.465975020404, 33.9761611500023], - [-118.466129418343, 33.9763480850716], - [-118.466499973398, 33.9768478943791], - [-118.466636264894, 33.9770224895226], - [-118.466837122802, 33.9772611026781], - [-118.467054542949, 33.9775076293458], - [-118.467268454276, 33.9777355327391], - [-118.467573741335, 33.9780516627819], - [-118.467628481973, 33.9781110239579], - [-118.467703154431, 33.9781976219073], - [-118.468124941305, 33.9787165097577], - [-118.468339273942, 33.9789918979087], - [-118.468522025407, 33.9792358592705], - [-118.468724707793, 33.9794998395738], - [-118.469105087722, 33.9799833361361], - [-118.469266223026, 33.9801826000151], - [-118.469399005703, 33.9803369357138], - [-118.469561684313, 33.9805119892594], - [-118.470006210241, 33.9809766243155], - [-118.470266721674, 33.9812573594964], - [-118.470537339153, 33.9815625361157], - [-118.470822975565, 33.9819256734716], - [-118.47092080749, 33.982022742245], - [-118.471045027426, 33.9821139917966], - [-118.471136122884, 33.9821661343535], - [-118.471233673635, 33.9822124577743], - [-118.471478324413, 33.982321398311], - [-118.47153306595, 33.9823628329229], - [-118.471582893702, 33.9824259155445], - [-118.471634406694, 33.9825101813158], - [-118.471689428505, 33.9826091123905], - [-118.47175596043, 33.9827208457511], - [-118.471910779679, 33.9829596753168], - [-118.472045385936, 33.9831347234612], - [-118.472121742735, 33.9832206175916], - [-118.472368919576, 33.9834759731957], - [-118.472611044291, 33.9837185250389], - [-118.472790286038, 33.9839177801619], - [-118.472916330452, 33.9840525560623], - [-118.473249830001, 33.9843847243329], - [-118.473916267652, 33.9849969154463], - [-118.474052137839, 33.9851060848129], - [-118.474211168391, 33.9852175824927], - [-118.474330335303, 33.9852927670696], - [-118.474782019008, 33.9855541684664], - [-118.474856411191, 33.9855881528682], - [-118.474953962841, 33.985613292006], - [-118.475067795557, 33.9856340081397], - [-118.475198192309, 33.9856465777027], - [-118.475338413935, 33.9856549574104], - [-118.47545056231, 33.9856563540283], - [-118.47563527839, 33.9856496040808], - [-118.475724408334, 33.9856551905525], - [-118.475820135506, 33.985668923588], - [-118.475917546121, 33.9856938303048], - [-118.476041485782, 33.9857294436645], - [-118.476071242476, 33.9857438753686], - [-118.476097770624, 33.9857725063728], - [-118.476122754569, 33.9858102149949], - [-118.476144510867, 33.9858593289138], - [-118.476191392145, 33.9859957314889], - [-118.476230131992, 33.9861202630314], - [-118.476258765791, 33.9862301296317], - [-118.476277433681, 33.9863162537742], - [-118.476333438249, 33.98659929899], - [-118.476351966002, 33.9866803018882], - [-118.476380599802, 33.9867843482285], - [-118.476422567295, 33.9868965416329], - [-118.476476045801, 33.9870152523351], - [-118.47659619547, 33.9872617514597], - [-118.47699426143, 33.9876546582854], - [-118.477093777696, 33.9877519536524], - [-118.477168310016, 33.9878203862122], - [-118.477238069588, 33.9878939395143], - [-118.477322848093, 33.9880017085804], - [-118.477424188837, 33.9881383408523], - [-118.477543918095, 33.9883001105027], - [-118.477673612364, 33.9884816649817], - [-118.477789972041, 33.9886597280241], - [-118.477945071565, 33.9889388081315], - [-118.478031394273, 33.9890554215105], - [-118.478155754346, 33.9891666808765], - [-118.478241936918, 33.9892244053438], - [-118.478334575682, 33.9892795690426], - [-118.478562944495, 33.9894085178288], - [-118.478607719224, 33.9894534407468], - [-118.478644354318, 33.9895218719365], - [-118.478724641246, 33.9897180872207], - [-118.478781487536, 33.9898370271123], - [-118.478888162476, 33.990029285161], - [-118.478986275573, 33.9901822064369], - [-118.479077791442, 33.9903060332016], - [-118.479174079163, 33.9904198516253], - [-118.479280332793, 33.9905348333055], - [-118.479724999757, 33.9910082582607], - [-118.480086711796, 33.9913806654181], - [-118.480202931336, 33.9915079813683], - [-118.480329115887, 33.9916592710831], - [-118.48047032027, 33.9918315082767], - [-118.480624858346, 33.9920293478655], - [-118.480862631622, 33.9923419338494], - [-118.481048750871, 33.9925937704155], - [-118.481519243501, 33.9932394182388], - [-118.48163069119, 33.9933937302283], - [-118.481812038588, 33.9936530116049], - [-118.481938364175, 33.9938215208007], - [-118.482089533569, 33.9940158649273], - [-118.482259090579, 33.9942297585442], - [-118.482587958415, 33.9946268225634], - [-118.482939985717, 33.9950418063842], - [-118.483356298257, 33.9955149731127], - [-118.481690484852, 33.9966053657474], - [-118.481447518416, 33.9963484207841], - [-118.480001511981, 33.9972965996987], - [-118.480016530914, 33.9973114949138], - [-118.478577260946, 33.9980685869596], - [-118.478440267865, 33.9981407349781], - [-118.477536197973, 33.9986185398746], - [-118.476665112422, 33.9990788867476], - [-118.475952075563, 33.9994559131675], - [-118.473979430895, 34.0004983123171], - [-118.473838787959, 34.0005727855598], - [-118.472728807136, 34.0011592600645], - [-118.471305258584, 34.0019107331595], - [-118.470364694633, 34.0024080649033], - [-118.470060530468, 34.0025693426833], - [-118.467071526497, 34.0041485883903], - [-118.466931866318, 34.0042221275574], - [-118.466548819291, 34.0044245926567], - [-118.458989215901, 34.0084174781402], - [-118.458076864441, 34.0088966207182], - [-118.456424244592, 34.0097748485191], - [-118.456064357032, 34.0099654328984], - [-118.45594729577, 34.0100275642161], - [-118.450970488061, 34.0126419411263], - [-118.450660148876, 34.0127966832594], - [-118.446957545702, 34.0147615395072], - [-118.44398594659, 34.0163382316249], - [-118.443528928689, 34.0165806896509], - [-118.4436030397, 34.0166698082982], - [-118.445381703962, 34.0188195543633], - [-118.44542816393, 34.0188765610167], - [-118.445896832082, 34.0194417351315], - [-118.445939080748, 34.0194249823808], - [-118.447160649397, 34.0208985141445], - [-118.447685321219, 34.0215339415689], - [-118.44879754783, 34.0228771429341], - [-118.448890748041, 34.0230476877806], - [-118.452994785858, 34.0280028888422], - [-118.453050088842, 34.0280696607717], - [-118.455453923948, 34.0290498235875], - [-118.455867289116, 34.0292182625491], - [-118.456203877073, 34.0293552938189], - [-118.457165495737, 34.0297473076105], - [-118.457727503339, 34.0316547741999], - [-118.459318082838, 34.0313269783793], - [-118.466543485994, 34.037498346851], - [-118.466656617125, 34.0375951198621], - [-118.470971477862, 34.0412812519263], - [-118.471525205691, 34.0417541617238], - [-118.471287152141, 34.0419472326938], - [-118.473733095776, 34.0440407451092], - [-118.474290191389, 34.0443191779945], - [-118.474335668601, 34.0442831239864], - [-118.474338897146, 34.0442803326789], - [-118.474342265828, 34.0442775413713], - [-118.474345354236, 34.0442747500636], - [-118.474348581883, 34.0442719587558], - [-118.474351530154, 34.044269167448], - [-118.474354617663, 34.04426637614], - [-118.474357565036, 34.0442633518508], - [-118.474360372271, 34.0442603283058], - [-118.474363320542, 34.0442573040163], - [-118.474365986742, 34.0442542804711], - [-118.47436865384, 34.0442510239446], - [-118.474371320938, 34.0442479996548], - [-118.474373847, 34.044244743128], - [-118.474376373961, 34.0442414866011], - [-118.474378759887, 34.0442382300741], - [-118.474381145812, 34.0442349735469], - [-118.474383532636, 34.0442317170196], - [-118.474385637388, 34.0442282282553], - [-118.474387883177, 34.0442249717278], - [-118.47438984869, 34.0442214822188], - [-118.474391954341, 34.044217993454], - [-118.474394620541, 34.044212876053], - [-118.476562367611, 34.0460690767338], - [-118.477201435393, 34.0466252308352], - [-118.477203259871, 34.04662383522], - [-118.477230489604, 34.0466029009894], - [-118.477258001408, 34.0465821997284], - [-118.477286072862, 34.0465619629234], - [-118.477314566525, 34.0465421920634], - [-118.477343481497, 34.0465228856601], - [-118.477372676744, 34.0465038122275], - [-118.477402433437, 34.0464854369715], - [-118.477432470406, 34.0464672939423], - [-118.477462928684, 34.0464496161152], - [-118.477493668134, 34.0464324034906], - [-118.477524828895, 34.0464156560687], - [-118.47755626993, 34.0463993738497], - [-118.477588131376, 34.0463835568341], - [-118.477620274894, 34.0463679720465], - [-118.477623783713, 34.0463665764271], - [-118.477656207505, 34.0463516898185], - [-118.477689051709, 34.0463372684138], - [-118.477722177085, 34.0463233122135], - [-118.477755583633, 34.0463098212176], - [-118.477789270457, 34.0462970284021], - [-118.477823237554, 34.0462844678159], - [-118.477857345687, 34.0462726046662], - [-118.477891874232, 34.0462612074662], - [-118.477915876318, 34.0462535311772], - [-118.477950686035, 34.0462428314133], - [-118.477985776925, 34.0462328298309], - [-118.478021007054, 34.0462230604789], - [-118.478056519254, 34.0462142215404], - [-118.478092170692, 34.0462056148324], - [-118.478127963166, 34.0461974740754], - [-118.478163895778, 34.0461900307565], - [-118.478199968526, 34.0461830526445], - [-118.478236322448, 34.0461765397394], - [-118.478272676369, 34.0461707242731], - [-118.478309170427, 34.0461651417822], - [-118.478345804623, 34.0461602574746], - [-118.478374298285, 34.0461570010213], - [-118.478450655085, 34.0456334078459], - [-118.479027120172, 34.0456915591386], - [-118.479148392735, 34.0451940157608], - [-118.479303493157, 34.0452202999141], - [-118.479475295956, 34.0450812011823], - [-118.479658889142, 34.0451121382487], - [-118.47975742265, 34.0447080991095], - [-118.479977791068, 34.0445296891199], - [-118.48006832418, 34.044607147269], - [-118.480262866033, 34.0444506022684], - [-118.482031424249, 34.0430170236227], - [-118.483829598123, 34.0415599268601], - [-118.485210617735, 34.0427406796087], - [-118.485349996741, 34.042859777476], - [-118.494425648209, 34.0506210159224], - [-118.494550710764, 34.0504916949115], - [-118.494487828694, 34.050437965788], - [-118.494653455575, 34.0501665307787], - [-118.494867507039, 34.0503495807549], - [-118.494917475827, 34.0502149100706], - [-118.495345859928, 34.0497266962513], - [-118.495417163703, 34.0495945827437], - [-118.497712780564, 34.0472176677823], - [-118.498895328194, 34.0459953408438], - [-118.499147417921, 34.0457343584646], - [-118.499428141447, 34.0454405782458], - [-118.503977125604, 34.0407287846769], - [-118.504592471574, 34.0412977681173], - [-118.504652125099, 34.0412649687216], - [-118.506610593453, 34.0405868871491], - [-118.507555087533, 34.0400751236012], - [-118.507563369102, 34.0400814039821], - [-118.508108674327, 34.0391348663434], - [-118.50832062014, 34.0392058159794], - [-118.508335358799, 34.0392102361862], - [-118.508341955128, 34.0391960458179], - [-118.508348272081, 34.0391820884423], - [-118.508354306963, 34.0391676658184], - [-118.508357816681, 34.0391590591398], - [-118.508127903664, 34.0390822935121], - [-118.508169590883, 34.038785465765], - [-118.508182504165, 34.0387342884623], - [-118.508274862654, 34.0383544119638], - [-118.508197242824, 34.0383413849507], - [-118.50821520913, 34.0382611295733], - [-118.508217735192, 34.0382506610542], - [-118.508219139259, 34.0382443805377], - [-118.508221385047, 34.0382339120166], - [-118.508223349663, 34.0382225137363], - [-118.508225034004, 34.0382129757149], - [-118.508226718345, 34.0382006469292], - [-118.508227981376, 34.0381901784027], - [-118.508228964133, 34.0381794776214], - [-118.508229806753, 34.0381690098366], - [-118.5082303682, 34.0381583090527], - [-118.508230648475, 34.0381478405209], - [-118.508230788612, 34.0381371397343], - [-118.508230648475, 34.0381266719443], - [-118.5082303682, 34.038115971155], - [-118.508229806753, 34.038105502618], - [-118.508229245306, 34.0380959653279], - [-118.508097585523, 34.0381041068626], - [-118.507700642457, 34.0362733212573], - [-118.508229104271, 34.0348405356765], - [-118.508716300175, 34.0339665216634], - [-118.508814273135, 34.0333630584941], - [-118.508809781558, 34.0333530553929], - [-118.508805148946, 34.0333432845572], - [-118.508800236959, 34.0333335137205], - [-118.508795183935, 34.0333237428825], - [-118.508789989876, 34.0333142043103], - [-118.508784516441, 34.0333046664815], - [-118.508778760935, 34.0332953609184], - [-118.508773006327, 34.0332860553543], - [-118.508766971445, 34.0332767497893], - [-118.508760654492, 34.0332674442231], - [-118.5087541983, 34.033258370923], - [-118.508747601073, 34.0332495306333], - [-118.508739880951, 34.0332395275187], - [-118.508604993521, 34.0330673743048], - [-118.509496851715, 34.0328486926915], - [-118.50951046748, 34.0328333384294], - [-118.510453698528, 34.0318909067406], - [-118.510527669402, 34.0318169262533], - [-118.51077063494, 34.0314914582441], - [-118.510780319677, 34.031482384754], - [-118.510834921077, 34.0314321336221], - [-118.510924050123, 34.0313704832594], - [-118.511035076502, 34.0313251172234], - [-118.511139926964, 34.0312739354198], - [-118.511242110327, 34.0312211254441], - [-118.511338398946, 34.0311615683604], - [-118.51137770024, 34.0311252757419], - [-118.511421352074, 34.0310850277683], - [-118.511467250595, 34.0310289606409], - [-118.511476655058, 34.0310173283749], - [-118.51151862345, 34.0309382289239], - [-118.511575890151, 34.030853080609], - [-118.511661932585, 34.0307900332053], - [-118.511733657671, 34.0307448998773], - [-118.511744043992, 34.0307393163711], - [-118.511848473144, 34.0306883672326], - [-118.511943076523, 34.0306299726346], - [-118.512018871875, 34.030576696965], - [-118.512729663844, 34.0299808867022], - [-118.513241283144, 34.0294464925278], - [-118.513360871367, 34.0293213269277], - [-118.51348523144, 34.0291749895881], - [-118.513580958611, 34.0290598279406], - [-118.513743637221, 34.0288685888473], - [-118.51381129204, 34.028789952696], - [-118.513898596607, 34.0287259731528], - [-118.513979726156, 34.0286759534124], - [-118.514069417547, 34.0286138349987], - [-118.51414689724, 34.0285426434796], - [-118.514205989318, 34.0284758726668], - [-118.514490221664, 34.0281806360071], - [-118.514620758553, 34.0280531420051], - [-118.514703150234, 34.0279854401926], - [-118.514800280574, 34.0279289049649], - [-118.514875514479, 34.0278400311653], - [-118.514906535102, 34.0277911738013], - [-118.515253088071, 34.0269443083554], - [-118.515893559021, 34.0264264133989], - [-118.516090907211, 34.0262668099135], - [-118.517582672029, 34.0250590721413], - [-118.51777988098, 34.0251872682534], - [-118.517940594974, 34.0252866149513], - [-118.518237039018, 34.025458318631], - [-118.518992746751, 34.0259883177711], - [-118.519581143262, 34.0263926783656], - [-118.519755050813, 34.0265073783175], - [-118.519924046376, 34.0266141685074], - [-118.520175855828, 34.0267744689381], - [-118.520489003146, 34.0269799051652], - [-118.520861803989, 34.0272281488146], - [-118.520961180117, 34.0272851498204], - [-118.521274467573, 34.027510825422], - [-118.521763628093, 34.0278767904969], - [-118.522457015203, 34.0284560968464], - [-118.522785321591, 34.0287043369218], - [-118.523189423332, 34.0289407106971], - [-118.523455829509, 34.0290668074614], - [-118.523738798824, 34.0291887163321], - [-118.524250136951, 34.0294027543837], - [-118.524433730137, 34.0294785981725], - [-118.524601041359, 34.0295707273461], - [-118.52505160217, 34.0298526978318], - [-118.525333168316, 34.0300167145139], - [-118.525637893928, 34.030191898709], - [-118.525912721811, 34.0303405600222], - [-118.526157793899, 34.0304631653757], - [-118.526369599575, 34.0305659952234], - [-118.526599651831, 34.0306611472462], - [-118.526859321542, 34.0307602545173], - [-118.527674682799, 34.0310468739551], - [-118.527790622065, 34.0310961947348], - [-118.528048887709, 34.0312339205336], - [-118.528414810152, 34.0314360889456], - [-118.528873512394, 34.0316719898236], - [-118.529391587886, 34.0319167304753], - [-118.529963983605, 34.0321414631895], - [-118.530284850147, 34.0322484785585], - [-118.530567539187, 34.0323282743409], - [-118.531086596538, 34.0324620436088], - [-118.531352722441, 34.0325346276864], - [-118.531886799623, 34.0327095736698], - [-118.532444315648, 34.0329233698272], - [-118.532978813242, 34.0331522873858], - [-118.533450569802, 34.0333774821049], - [-118.534123042335, 34.0337513323077], - [-118.534497107108, 34.0339299979797], - [-118.53493068437, 34.0341177357402], - [-118.535412124768, 34.034300122822], - [-118.535923182621, 34.0344685513605], - [-118.536229030229, 34.0345523002331], - [-118.536511579132, 34.0346246502592], - [-118.536769563604, 34.034689322888], - [-118.53702249595, 34.0347616720528], - [-118.537265602524, 34.0348458859057], - [-118.537517130804, 34.0349563877536], - [-118.537777080789, 34.0350822425711], - [-118.538028889343, 34.0352276391868], - [-118.538280698795, 34.0353802468161], - [-118.538532647486, 34.035539833159], - [-118.540478202559, 34.0365322394514], - [-118.540966521357, 34.0367797576366], - [-118.541339041028, 34.036962371252], - [-118.541577515888, 34.0370691482832], - [-118.541905119794, 34.0372033745879], - [-118.542308800225, 34.0373592354541], - [-118.543269997579, 34.0377219020168], - [-118.543789617275, 34.0379275439915], - [-118.544224738741, 34.0381036416108], - [-118.54462027774, 34.0382595008228], - [-118.545002342909, 34.0384009369941], - [-118.545414163872, 34.0385309746908], - [-118.545895323996, 34.0386563589561], - [-118.54640596054, 34.0387710430737], - [-118.546909998958, 34.0388706061185], - [-118.547367719341, 34.0389499304249], - [-118.547741081631, 34.0390111107772], - [-118.547954150338, 34.039038095089], - [-118.548206801512, 34.0390611244244], - [-118.54850394714, 34.0390764775593], - [-118.548817655905, 34.0390915984404], - [-118.549588522708, 34.0391102082939], - [-118.549786573381, 34.0391139302641], - [-118.549984624053, 34.0391069515698], - [-118.550190815259, 34.039091133194], - [-118.550434904589, 34.0390646141456], - [-118.550678852884, 34.0390264639211], - [-118.550922942215, 34.0389841264568], - [-118.551419121272, 34.0388966599801], - [-118.551776763045, 34.0388280355802], - [-118.552478852135, 34.038679156143], - [-118.552594229953, 34.0386528699624], - [-118.552701325305, 34.0386226287854], - [-118.552814878645, 34.0385840131129], - [-118.553028789972, 34.0384851472342], - [-118.553235963935, 34.0383625542182], - [-118.553441453556, 34.0382329815144], - [-118.553632064381, 34.0381110856394], - [-118.553791515344, 34.0380119869554], - [-118.553860573331, 34.0379738362575], - [-118.553952790785, 34.0379428973316], - [-118.554051605466, 34.0379126559015], - [-118.554165298944, 34.0378896255099], - [-118.554287273091, 34.0378668281108], - [-118.554524765195, 34.037832399374], - [-118.554729131922, 34.0378096019595], - [-118.554928866935, 34.0377982036222], - [-118.555133513937, 34.0377984358767], - [-118.555348127746, 34.0378130917323], - [-118.555516561862, 34.037832399374], - [-118.555843604321, 34.0378740399058], - [-118.555980737539, 34.0378896255099], - [-118.556203491882, 34.0378898585085], - [-118.556370241656, 34.0379047466026], - [-118.556553694705, 34.0379319635205], - [-118.55675862288, 34.0379736040034], - [-118.556965235395, 34.0380266427741], - [-118.557271364176, 34.0381415599968], - [-118.557523032593, 34.0382708998425], - [-118.557758419943, 34.0384121029976], - [-118.557988753372, 34.0385614481529], - [-118.558247159153, 34.0387059083083], - [-118.558545147401, 34.0388433887573], - [-118.558857873409, 34.03896528358], - [-118.559155720621, 34.0390683361179], - [-118.559415390332, 34.0391488237268], - [-118.55960543971, 34.0391981397962], - [-118.559871846786, 34.0392895601751], - [-118.560230891727, 34.0394421604967], - [-118.560643275036, 34.0396370977033], - [-118.561512535211, 34.0400639578166], - [-118.561878458552, 34.040243307681], - [-118.562222906768, 34.0403993959531], - [-118.56256538947, 34.0405368736572], - [-118.562931031638, 34.0406592312839], - [-118.5633359751, 34.0407655384242], - [-118.563595503776, 34.0408227625811], - [-118.563869911247, 34.0408764970529], - [-118.564122702558, 34.0409144141992], - [-118.564344053733, 34.0409490739309], - [-118.564771735351, 34.0409909451785], - [-118.565092181481, 34.0410286292836], - [-118.565449121669, 34.041078874731], - [-118.5658241683, 34.0411358664312], - [-118.566189249022, 34.0411970444623], - [-118.566524854222, 34.0412658991905], - [-118.567152832301, 34.0413875582869], - [-118.567154376505, 34.0415641139556], - [-118.567197467791, 34.0416450644285], - [-118.567332215084, 34.041893032925], - [-118.567345548777, 34.0419225754619], - [-118.569206044172, 34.045410339489], - [-118.569283805038, 34.0455552524455], - [-118.570119518527, 34.0471218361553], - [-118.570142397719, 34.0490891540114], - [-118.570161065609, 34.0503470226116], - [-118.570407119555, 34.0693514060781], - [-118.599216512027, 34.0743415711937], - [-118.577505214366, 34.1095350438016], - [-118.564764016128, 34.1301729906526], - [-118.56818056158, 34.1314791501466], - [-118.568744815868, 34.1316929298279], - [-118.575748166264, 34.1343470060176], - [-118.581291893563, 34.1364770378502], - [-118.588964770019, 34.1393779502442], - [-118.588964770019, 34.1393788796354], - [-118.604130577803, 34.1451443930412], - [-118.605704594166, 34.1457428779957], - [-118.612351004374, 34.1482685032269], - [-118.614989945659, 34.149271901431], - [-118.614983209192, 34.1472820525362], - [-118.616649443009, 34.1472781026214], - [-118.617005540578, 34.147280425882], - [-118.62379483951, 34.1472715974911], - [-118.629003805127, 34.1472774060149], - [-118.628990610672, 34.1473538408865], - [-118.629823096513, 34.1498977707744], - [-118.630087678212, 34.1498380651764], - [-118.630267060995, 34.150386336308], - [-118.631262085309, 34.1510916493602], - [-118.632174576907, 34.1505296757258], - [-118.638582933565, 34.1576998398777], - [-118.638607215925, 34.1576717315153], - [-118.638610023161, 34.1576684793505], - [-118.638612971431, 34.1576654598548], - [-118.638615918804, 34.1576624396156], - [-118.638619147349, 34.1576596520454], - [-118.638622374996, 34.1576568644752], - [-118.638625743678, 34.1576543095741], - [-118.638627428019, 34.1576531477147], - [-118.638630937737, 34.1576505928135], - [-118.638634586694, 34.1576482698381], - [-118.638638236549, 34.1576459468626], - [-118.638642025643, 34.1576438558129], - [-118.638643991156, 34.1576426946968], - [-118.638647921286, 34.157640603647], - [-118.638651991552, 34.1576387452664], - [-118.638656061819, 34.1576371195551], - [-118.638660273121, 34.1576354931004], - [-118.638664483525, 34.157633867389], - [-118.638668834964, 34.1576324736034], - [-118.639837066143, 34.1572719469739], - [-118.639860365747, 34.1573235172434], - [-118.639922406095, 34.1573042360989], - [-118.639898965456, 34.1572528984881], - [-118.640689904216, 34.1570087518988], - [-118.640772296796, 34.1569827343725], - [-118.640854268065, 34.1569553230415], - [-118.640935537751, 34.1569269825035], - [-118.641016245091, 34.1568972481583], - [-118.641096391882, 34.1568663526758], - [-118.641175836191, 34.1568342953115], - [-118.641254579814, 34.1568013087354], - [-118.641283213613, 34.1567887645412], - [-118.641281669409, 34.1583189098205], - [-118.640286645095, 34.1586845424635], - [-118.639955671608, 34.1588873358611], - [-118.639948653969, 34.1589256640135], - [-118.640023045255, 34.1590095224893], - [-118.639957075675, 34.1591591195652], - [-118.639746111721, 34.1590947738999], - [-118.64198712808, 34.1619802752876], - [-118.645564948974, 34.1619897993695], - [-118.645546561358, 34.1657004799518], - [-118.649925988505, 34.1657116291699], - [-118.65429292368, 34.1657267274388], - [-118.654275799994, 34.1694321339329], - [-118.655396869621, 34.1694349211143], - [-118.657015801748, 34.1694391018863], - [-118.657189850334, 34.1694409600072], - [-118.658641332001, 34.1694444443552], - [-118.65862968175, 34.1713297186714], - [-118.658606942695, 34.175676583067], - [-118.659211481033, 34.1757430059517], - [-118.658604837044, 34.176063507248], - [-118.658601188087, 34.1767542066278], - [-118.658780851144, 34.1767542066278], - [-118.661470322663, 34.1767558327142], - [-118.663849033688, 34.1767535102644], - [-118.666361790421, 34.1767523486679], - [-118.667423205624, 34.176751884178], - [-118.668165438628, 34.1767553682242], - [-118.668168947447, 34.1812398994368], - [-118.668166140212, 34.1812387379021], - [-118.668175824949, 34.1847841593447], - [-118.66817835191, 34.18583983791], - [-118.668188598094, 34.189871776419], - [-118.668152385208, 34.1913356083965], - [-118.667337725535, 34.1913483800329], - [-118.663524517291, 34.1901501947594], - [-118.66336197792, 34.1905073300577], - [-118.662975562211, 34.1903858852778], - [-118.663138241719, 34.1900287494653], - [-118.661313398661, 34.1894554252691], - [-118.659593966613, 34.1892482936852], - [-118.658809344807, 34.1893434999696], - [-118.658823661257, 34.1894250053806], - [-118.658812011006, 34.1894263986412], - [-118.658791097328, 34.1894291851622], - [-118.658758673536, 34.1894338293638], - [-118.65872639078, 34.1894389379852], - [-118.658694248161, 34.1894445110263], - [-118.658662104643, 34.1894505484872], - [-118.658630243197, 34.1894572829491], - [-118.658598380852, 34.189464249249], - [-118.65856679968, 34.1894716799683], - [-118.658535217609, 34.1894795751067], - [-118.65850391761, 34.1894879346643], - [-118.658472756849, 34.1894967586408], - [-118.658441877262, 34.1895060470362], - [-118.658411138709, 34.1895157998502], - [-118.658380539396, 34.1895257845014], - [-118.658350221255, 34.1895364661523], - [-118.658320184287, 34.1895476122214], - [-118.658290286557, 34.1895589908703], - [-118.658260670001, 34.1895708331939], - [-118.658231334617, 34.1895831406782], - [-118.657455274653, 34.189917057507], - [-118.657281085929, 34.191340949479], - [-118.657224098604, 34.1918046608568], - [-118.656617875925, 34.1967620607735], - [-118.658492688669, 34.1967669371326], - [-118.658488056057, 34.1954487845167], - [-118.658486512751, 34.1952402744883], - [-118.658485529994, 34.1949672138051], - [-118.668062412645, 34.1949755728187], - [-118.667972300842, 34.1986044678094], - [-118.667873346023, 34.2022125426823], - [-118.667874187745, 34.2022125426823], - [-118.66781495553, 34.2045312409675], - [-118.667808358302, 34.2093335098691], - [-118.662775968233, 34.2093479034945], - [-118.662753369315, 34.2129039822577], - [-118.658427419775, 34.212937875484], - [-118.658191471876, 34.2129385715485], - [-118.657643920863, 34.2129413572925], - [-118.65325214188, 34.212950875622], - [-118.652387092109, 34.2164234652866], - [-118.654042939605, 34.2164250906081], - [-118.658412260705, 34.2164206796577], - [-118.658402575968, 34.2235005223635], - [-118.654064695902, 34.2235005223635], - [-118.654063011561, 34.2245424860755], - [-118.654059221569, 34.2271135662077], - [-118.653303093425, 34.2271147271105], - [-118.653304075283, 34.2275610659263], - [-118.652874006841, 34.2275617626128], - [-118.652873024084, 34.227115191323], - [-118.649724148739, 34.2271200651833], - [-118.647923869214, 34.2343519540427], - [-118.647786735098, 34.2349019968478], - [-118.647739573545, 34.2350909136742], - [-118.646993410412, 34.2380868361376], - [-118.644563047157, 34.2379204378276], - [-118.644298324423, 34.2379021034113], - [-118.631475154016, 34.2370227611658], - [-118.630330644649, 34.2381915027354], - [-118.62995938801, 34.23858556643], - [-118.631819882507, 34.2385788358932], - [-118.631882764577, 34.2385793000426], - [-118.6324567045, 34.2385765151464], - [-118.632573485487, 34.2428010967018], - [-118.632626401647, 34.2458023148393], - [-118.632652088074, 34.247432019294], - [-118.632661491639, 34.2483915420622], - [-118.632661351501, 34.2494858715435], - [-118.632545693409, 34.2629104373731], - [-118.632560010758, 34.2634340764863], - [-118.63349397748, 34.269671098235], - [-118.629665189754, 34.2727759807836], - [-118.62953184563, 34.2728824600755], - [-118.629362990205, 34.2730135283431], - [-118.629191328442, 34.2731418133965], - [-118.629016998683, 34.2732677777218], - [-118.628840002724, 34.2733911904642], - [-118.628660619942, 34.273512051635], - [-118.62847857096, 34.2736301281536], - [-118.628293995017, 34.273745653123], - [-118.628107174184, 34.2738583942051], - [-118.627917966527, 34.2739685837595], - [-118.627726513082, 34.2740759887057], - [-118.627532673712, 34.2741803781916], - [-118.627336868827, 34.2742822161808], - [-118.627138818155, 34.2743810379884], - [-118.626938661832, 34.2744770759717], - [-118.626736681928, 34.2745700977938], - [-118.626532736511, 34.2746601042071], - [-118.62632682558, 34.2747473268249], - [-118.626119230308, 34.2748315340523], - [-118.625909810557, 34.2749127251563], - [-118.625698846602, 34.2749906685423], - [-118.625486058169, 34.2750658281675], - [-118.625271866568, 34.2751377400917], - [-118.6250562709, 34.2752066359256], - [-118.624839131028, 34.2752722848169], - [-118.624620728124, 34.2753346852882], - [-118.624400922052, 34.2753940704337], - [-118.624179992188, 34.2754502079154], - [-118.62395793943, 34.275503330084], - [-118.623734764675, 34.2755529722573], - [-118.623510466129, 34.2755993667853], - [-118.623285325861, 34.2756427452746], - [-118.623059343871, 34.2756826445276], - [-118.622832519262, 34.2757192961504], - [-118.622604993068, 34.275752700147], - [-118.614787123136, 34.276841338783], - [-118.607543052989, 34.2778494706594], - [-118.607504172107, 34.2778545739193], - [-118.607389918081, 34.2778691874272], - [-118.607275523019, 34.2778819459446], - [-118.607160707546, 34.2778930803251], - [-118.607045751038, 34.2779025905696], - [-118.606930654392, 34.2779102458256], - [-118.606815276573, 34.2779165085416], - [-118.606699899653, 34.2779209162701], - [-118.606584381698, 34.2779236998641], - [-118.60646886464, 34.2779248593238], - [-118.606353346685, 34.2779243953915], - [-118.606237828729, 34.2779220757298], - [-118.60612245091, 34.277918132676], - [-118.60600707399, 34.2779125654876], - [-118.605891837207, 34.2779053741642], - [-118.605776740562, 34.2778965594476], - [-118.60566192419, 34.277885889], - [-118.605547388991, 34.2778738267533], - [-118.60543299393, 34.2778599087742], - [-118.605319021077, 34.2778443673992], - [-118.605205327599, 34.2778272018852], - [-118.605092055432, 34.2778081806359], - [-118.604979204574, 34.2777877675831], - [-118.604866775026, 34.277765730388], - [-118.60475476589, 34.2777418381962], - [-118.604670970142, 34.2777228169276], - [-118.604606403731, 34.2777077387185], - [-118.604495798662, 34.2776803666366], - [-118.604385614902, 34.2776511388104], - [-118.604276272864, 34.2776205191722], - [-118.604167352136, 34.2775882761243], - [-118.604059274028, 34.2775544089226], - [-118.603951896605, 34.2775189175652], - [-118.603845362701, 34.2774820351309], - [-118.603739529482, 34.2774435285368], - [-118.600330422979, 34.2761440366293], - [-118.596152555323, 34.2746183486665], - [-118.593735525762, 34.2771885955106], - [-118.59258385772, 34.2780417701866], - [-118.59233022379, 34.2785488459757], - [-118.59200977766, 34.2791895293097], - [-118.591990548323, 34.2793706923302], - [-118.591710807553, 34.2802352105182], - [-118.591921209163, 34.280632093142], - [-118.592182703352, 34.2823615593288], - [-118.591998548719, 34.282298235515], - [-118.592674671107, 34.2872220127062], - [-118.590379756729, 34.295140784439], - [-118.589344027054, 34.2987148062267], - [-118.588710854916, 34.3008984308647], - [-118.588121897856, 34.3029301066171], - [-118.585102856019, 34.3034827153028], - [-118.571774664438, 34.298624130218], - [-118.571591071252, 34.2979543742835], - [-118.571029202889, 34.2969355832641], - [-118.570171031519, 34.295808939018], - [-118.570158258374, 34.2953604107346], - [-118.568720112198, 34.2946442447882], - [-118.566099277356, 34.2958233178582], - [-118.564479502609, 34.2968924473072], - [-118.5635623784, 34.2978518696146], - [-118.562108370671, 34.2987952789041], - [-118.559779769469, 34.2991969435566], - [-118.557403725543, 34.299248194715], - [-118.554804787137, 34.2971289990563], - [-118.554447706811, 34.2965737971335], - [-118.554046272169, 34.2964191102667], - [-118.551757813982, 34.2967180479441], - [-118.549816330074, 34.2969715302489], - [-118.546713633525, 34.2978720455118], - [-118.54083331737, 34.2988193973901], - [-118.541568953147, 34.3002245187822], - [-118.542604822959, 34.3013659454988], - [-118.54334705686, 34.3032633417692], - [-118.544308534489, 34.3039451138761], - [-118.543684485641, 34.3057202466062], - [-118.545005290281, 34.3085687280678], - [-118.545729416842, 34.3100383805663], - [-118.54710931356, 34.3133562498766], - [-118.545523365773, 34.3156628444646], - [-118.546308689164, 34.3173368712109], - [-118.534022824486, 34.3145357562226], - [-118.528729922984, 34.3185288417453], - [-118.52420465974, 34.3219349401903], - [-118.51984011049, 34.325228229143], - [-118.517062772059, 34.327323300465], - [-118.516136805732, 34.3280222646499], - [-118.510204134864, 34.3324975811448], - [-118.508199347577, 34.3340097001486], - [-118.507888587082, 34.3342442928398], - [-118.507826968044, 34.3342904227727], - [-118.507716362974, 34.3343738745141], - [-118.507642673273, 34.3344295089622], - [-118.507585685948, 34.3344723941951], - [-118.50725471336, 34.3347220528282], - [-118.503822026081, 34.3373110914849], - [-118.498074211431, 34.3338492865868], - [-118.495160161091, 34.3320923667378], - [-118.49273049942, 34.3306284326199], - [-118.488605126615, 34.3304578124647], - [-118.485875511182, 34.3303442200494], - [-118.484414907124, 34.3302834828173], - [-118.480120960066, 34.3302327135049], - [-118.475828416176, 34.3301821763545], - [-118.472351514717, 34.3301232935813], - [-118.46811301078, 34.3300910706848], - [-118.458212313318, 34.3300141057083], - [-118.449428754454, 34.3299561501071], - [-118.440647302141, 34.3299236949529], - [-118.433403371233, 34.3298919348833], - [-118.431843391046, 34.329884285106], - [-118.427277843752, 34.3298849802031], - [-118.422717069207, 34.3298840529124], - [-118.419994892723, 34.3298840529124], - [-118.419997559821, 34.3291691087082], - [-118.419476677094, 34.3291677177603], - [-118.419474010894, 34.3298847487513], - [-118.419021904981, 34.3298854438485], - [-118.418872279791, 34.3296482892822], - [-118.418865542426, 34.3296378568606], - [-118.418858523889, 34.3296274251794], - [-118.418851225077, 34.3296169927552], - [-118.418843786128, 34.329606792524], - [-118.418836066007, 34.3295965922915], - [-118.418828205748, 34.3295863920579], - [-118.418820065215, 34.32957665547], - [-118.418811643509, 34.3295666874284], - [-118.418803221803, 34.329557182291], - [-118.418794518925, 34.3295474456997], - [-118.418785535772, 34.3295381727546], - [-118.418776412482, 34.3295288998085], - [-118.418767149055, 34.3295196268614], - [-118.418757604455, 34.3295105861079], - [-118.418747918819, 34.3295017768062], - [-118.418737953808, 34.3294929675036], - [-118.418727987898, 34.3294843896529], - [-118.418725040526, 34.329482071415], - [-118.418721671843, 34.3294792895294], - [-118.418718303161, 34.3294767398385], - [-118.418714934479, 34.3294739579527], - [-118.418711565796, 34.3294714075198], - [-118.418708056079, 34.3294690892815], - [-118.418704407122, 34.3294665395903], - [-118.418700757267, 34.3294642213519], - [-118.41869710831, 34.3294619031134], - [-118.418693458455, 34.3294595848748], - [-118.418689669361, 34.3294572666362], - [-118.418685879369, 34.3294551798505], - [-118.41868194924, 34.3294530938066], - [-118.418678159248, 34.3294510070208], - [-118.418674229118, 34.3294489209767], - [-118.418670158852, 34.3294470663856], - [-118.418666228722, 34.3294452117945], - [-118.418662158456, 34.3294433572033], - [-118.418657947154, 34.329441502612], - [-118.418653876887, 34.3294398794738], - [-118.418649665585, 34.3294380248824], - [-118.418645455181, 34.3294366339389], - [-118.418641244778, 34.3294350115424], - [-118.418637033476, 34.3294336205988], - [-118.418632682036, 34.3294319974604], - [-118.418628330597, 34.3294308387116], - [-118.418623980056, 34.329429447768], - [-118.418619628617, 34.3294282882774], - [-118.418615137041, 34.3294271295286], - [-118.418610785601, 34.3294259700379], - [-118.418606294025, 34.3294250427421], - [-118.418601802449, 34.3294241154463], - [-118.418597310872, 34.3294231881505], - [-118.418592819296, 34.3294222608546], - [-118.418588327719, 34.3294215657537], - [-118.418583696006, 34.3294208699109], - [-118.418579204429, 34.3294201748099], - [-118.418574572716, 34.329419711162], - [-118.418570081139, 34.3294190153191], - [-118.418565449426, 34.3294185516712], - [-118.418560816814, 34.3294183202181], - [-118.4185561851, 34.3294180880232], - [-118.418551553386, 34.3294176243753], - [-118.418546920775, 34.3294176243753], - [-118.418542429198, 34.3294173929222], - [-118.418537797485, 34.3294173929222], - [-118.418533165771, 34.3294173929222], - [-118.418528534057, 34.3294176243753], - [-118.418523901445, 34.3294176243753], - [-118.418519269732, 34.3294178565702], - [-118.418514638018, 34.3294183202181], - [-118.418510006305, 34.3294185516712], - [-118.418505514728, 34.3294190153191], - [-118.418500882116, 34.3294194789671], - [-118.418496250403, 34.3294201748099], - [-118.418491758826, 34.3294206384578], - [-118.418487127113, 34.3294213335588], - [-118.418482635536, 34.3294222608546], - [-118.41847814396, 34.3294229566974], - [-118.418473652383, 34.3294238839933], - [-118.418469160807, 34.3294248112891], - [-118.418464669231, 34.3294259700379], - [-118.418460317791, 34.3294268973337], - [-118.418455826215, 34.3294280568244], - [-118.41844951016, 34.3294299114159], - [-118.418225632923, 34.3294952857412], - [-118.419468115251, 34.327931388092], - [-118.419702800119, 34.327836569983], - [-118.420343692378, 34.3277619212238], - [-118.42075298728, 34.3277276105917], - [-118.420761408985, 34.3260027877633], - [-118.420763373601, 34.3255778364212], - [-118.419063031651, 34.3256606009552], - [-118.415906155012, 34.324492379887], - [-118.414361754308, 34.3244462438248], - [-118.414360912586, 34.3246963967104], - [-118.413603660649, 34.3246945420142], - [-118.413603099202, 34.3251076745714], - [-118.41221941339, 34.3259093592188], - [-118.41228580428, 34.3260199434337], - [-118.412460694587, 34.3263006935348], - [-118.413597344595, 34.3274127867045], - [-118.413591168677, 34.3298859074938], - [-118.412299980593, 34.3298777940706], - [-118.409065202951, 34.3298509026329], - [-118.40521409748, 34.3298154333698], - [-118.405050436113, 34.3257463796222], - [-118.405040610341, 34.3254987804205], - [-118.404994431545, 34.3243590728011], - [-118.404925935005, 34.3226265302918], - [-118.404922004875, 34.3224795418686], - [-118.404924250664, 34.3220821617826], - [-118.40273194006, 34.32130385669], - [-118.40068125515, 34.3195527030099], - [-118.40067479806, 34.3195473700605], - [-118.396271930274, 34.3198652378824], - [-118.396290458026, 34.3180744065661], - [-118.396536091561, 34.3180755654717], - [-118.396542126443, 34.3174242817567], - [-118.397567188624, 34.3170078653601], - [-118.398310685557, 34.3170660613914], - [-118.399681458087, 34.3164553449443], - [-118.400751716306, 34.3164734300055], - [-118.400786807196, 34.3150815648589], - [-118.400925624755, 34.3096931138781], - [-118.4018482224, 34.3098823264883], - [-118.401948862458, 34.309777286088], - [-118.402457813762, 34.3096126523849], - [-118.402490799001, 34.3094334096012], - [-118.403027962794, 34.3093937586236], - [-118.403092248931, 34.3097246493428], - [-118.403336057089, 34.3100288732169], - [-118.403894837044, 34.3100451046427], - [-118.404126434402, 34.3090429241587], - [-118.403578462079, 34.3089745198264], - [-118.403663942168, 34.3088411880939], - [-118.403702120567, 34.3086484952884], - [-118.403679522548, 34.30834728051], - [-118.403763318296, 34.3079213122418], - [-118.40548162745, 34.3079173698536], - [-118.405467591273, 34.304368551861], - [-118.40561258475, 34.3007303015769], - [-118.401063880867, 34.3006751085948], - [-118.401217577223, 34.2970696292682], - [-118.40347262886, 34.2971106782863], - [-118.403519931448, 34.2935182659523], - [-118.403796163398, 34.2935252236456], - [-118.405746910733, 34.2935746228807], - [-118.407550840113, 34.2936235589832], - [-118.409084152013, 34.2936634497006], - [-118.408971722466, 34.2918129175646], - [-118.410228942553, 34.2918479390305], - [-118.410360742474, 34.2919724837592], - [-118.410320178149, 34.2913061541018], - [-118.410313440784, 34.2911964511345], - [-118.4103058608, 34.2910756161334], - [-118.409412037092, 34.2890165175711], - [-118.409054535457, 34.2886764993703], - [-118.407535259732, 34.2872257237541], - [-118.406180487995, 34.2859317181652], - [-118.40160062425, 34.286427613107], - [-118.401264598638, 34.2879201507137], - [-118.401171818839, 34.2883615290317], - [-118.401155396737, 34.2884547677969], - [-118.400833968748, 34.2884496651806], - [-118.400830318893, 34.2884723950145], - [-118.400827090348, 34.2884953564082], - [-118.400824424149, 34.2885183185378], - [-118.40082217836, 34.2885412799189], - [-118.400820494019, 34.288564242036], - [-118.40081923009, 34.2885872034045], - [-118.400818528505, 34.2886101655091], - [-118.400818248231, 34.2886331268651], - [-118.400818388368, 34.2886563205225], - [-118.400819089952, 34.2886792826081], - [-118.400820212846, 34.2887022439452], - [-118.400821897188, 34.2887252060183], - [-118.400822458635, 34.2887321641086], - [-118.400901763705, 34.289623722641], - [-118.400926607512, 34.289864701217], - [-118.400928291853, 34.2898865029356], - [-118.400929835159, 34.2899113201497], - [-118.400930817916, 34.2899363689183], - [-118.400931239226, 34.2899614176796], - [-118.400931099089, 34.2899864664333], - [-118.400930537641, 34.2900112828757], - [-118.400929414747, 34.2900363316145], - [-118.400927730406, 34.2900611487844], - [-118.400925624755, 34.2900861975084], - [-118.40092281752, 34.2901110139213], - [-118.400919588975, 34.290135831069], - [-118.400917764496, 34.290147891556], - [-118.400660340574, 34.2912843527516], - [-118.40065262135, 34.291317286704], - [-118.400642795578, 34.2913557869419], - [-118.400632128084, 34.291394054862], - [-118.400620618868, 34.2914323235069], - [-118.400325577993, 34.2923785880055], - [-118.400322490484, 34.2923883289082], - [-118.400314489189, 34.2924124492337], - [-118.400306067483, 34.2924363372551], - [-118.400297085229, 34.2924602260119], - [-118.400287539731, 34.2924838824648], - [-118.400277574719, 34.2925073066141], - [-118.400267047362, 34.292530731499], - [-118.400256099594, 34.2925539240805], - [-118.400244729618, 34.2925768843588], - [-118.399892562178, 34.2932682524558], - [-118.399882034822, 34.2932895894437], - [-118.399871928775, 34.2933109264262], - [-118.399862244038, 34.2933324949559], - [-118.399852979712, 34.2933542957746], - [-118.399844277732, 34.2933760965877], - [-118.399835996163, 34.2933981296897], - [-118.399828135905, 34.2934201620437], - [-118.399826591701, 34.2934250328059], - [-118.396995775661, 34.2933533680803], - [-118.396755617358, 34.2969875319139], - [-118.393722820518, 34.2969838212971], - [-118.393647586613, 34.2987929598185], - [-118.387408505792, 34.2987853064647], - [-118.387401768427, 34.2969673558042], - [-118.382890120948, 34.2969606299385], - [-118.382963670512, 34.2933343503449], - [-118.382382993224, 34.2933336549451], - [-118.382384677565, 34.2915311251514], - [-118.386923135264, 34.291536459138], - [-118.386928328425, 34.2897387617626], - [-118.384631168258, 34.2897355146928], - [-118.384630185501, 34.2881794576392], - [-118.385773993285, 34.2848220904462], - [-118.384570952388, 34.2845384182628], - [-118.384397466147, 34.2844975954211], - [-118.381870953512, 34.2839019507941], - [-118.379279033643, 34.2836210596525], - [-118.377115216703, 34.2833686971955], - [-118.376784384251, 34.2834109122916], - [-118.376425058138, 34.2834568385809], - [-118.374812862477, 34.2836646663238], - [-118.37468737951, 34.282831960553], - [-118.372649326709, 34.2828363672815], - [-118.373530798582, 34.2831706106919], - [-118.373528833068, 34.2861506730747], - [-118.369850372117, 34.286131653709], - [-118.37012337642, 34.2856679969816], - [-118.370134745498, 34.2856480497273], - [-118.370145834302, 34.2856278708945], - [-118.370156501796, 34.2856076913146], - [-118.37016674798, 34.2855872801559], - [-118.370176573752, 34.2855666374184], - [-118.37018583718, 34.2855459939335], - [-118.370194820332, 34.2855251188695], - [-118.370203242038, 34.2855040122262], - [-118.370211242434, 34.2854831371518], - [-118.370218822419, 34.2854617981815], - [-118.370225980195, 34.2854404592057], - [-118.370232718458, 34.2854191202245], - [-118.370238473065, 34.2853991729112], - [-118.370506563584, 34.2844440157825], - [-118.37051105516, 34.2844275470898], - [-118.370515125427, 34.2844110791361], - [-118.370518775282, 34.284394610437], - [-118.370522143964, 34.2843781424769], - [-118.370525231474, 34.2843616737713], - [-118.370527898572, 34.2843449734852], - [-118.37053014436, 34.2843282731957], - [-118.370532109874, 34.2843115729029], - [-118.370533794215, 34.2842948726069], - [-118.370535057246, 34.2842781723074], - [-118.370535898968, 34.2842614720047], - [-118.37053660145, 34.2842447716987], - [-118.370536741587, 34.2842280713893], - [-118.37053660145, 34.2842111394989], - [-118.37053618014, 34.2841944391829], - [-118.370535337521, 34.2841777388635], - [-118.370534074489, 34.2841610385409], - [-118.370510213439, 34.2838875699137], - [-118.370509090545, 34.2838736529303], - [-118.370508247925, 34.2838597359447], - [-118.370507826615, 34.2838458189567], - [-118.370507546341, 34.2838321342873], - [-118.370507686478, 34.2838182172948], - [-118.370507967651, 34.2838043002999], - [-118.370508669235, 34.2837903833027], - [-118.370509651992, 34.2837764663032], - [-118.370510915023, 34.2837627808804], - [-118.370512459227, 34.2837488638763], - [-118.370514283705, 34.2837351791912], - [-118.370516389356, 34.2837212621826], - [-118.370518775282, 34.2837075767508], - [-118.37052144238, 34.2836938920589], - [-118.370524389752, 34.2836802066226], - [-118.370527618297, 34.2836665219263], - [-118.370531267254, 34.283653068807], - [-118.370535057246, 34.2836396156856], - [-118.370539127513, 34.283626162562], - [-118.370543619089, 34.2836127094362], - [-118.370548250803, 34.2835992563083], - [-118.370554006309, 34.2835841791534], - [-118.37056397132, 34.2835591284912], - [-118.37057435854, 34.2835343101434], - [-118.370585447344, 34.2835097233678], - [-118.370596956559, 34.283485136585], - [-118.370608887085, 34.2834610136969], - [-118.370621520092, 34.2834368908018], - [-118.370634573512, 34.2834127678997], - [-118.370648048241, 34.283389108893], - [-118.370662084417, 34.2833654498795], - [-118.370676682041, 34.2833420224395], - [-118.370691700974, 34.2833190596377], - [-118.370707281354, 34.2832960960874], - [-118.371361928618, 34.2823481060007], - [-118.370188644415, 34.2819032171422], - [-118.369527259787, 34.2834670444196], - [-118.36891177368, 34.2832626950884], - [-118.368117326101, 34.2820572352408], - [-118.367492996978, 34.2804156750361], - [-118.36741874583, 34.2803743862731], - [-118.367293121828, 34.2805935879909], - [-118.366434809421, 34.2806418354063], - [-118.364291204575, 34.2807694125733], - [-118.363530022509, 34.2805017322581], - [-118.36132578138, 34.2797332480478], - [-118.360528526565, 34.2799649773071], - [-118.354720356807, 34.2788531827664], - [-118.35305328037, 34.2788466877854], - [-118.351746793079, 34.2788383370949], - [-118.351741318746, 34.2824798557425], - [-118.343128159512, 34.2824698813664], - [-118.326843387752, 34.2824404236231], - [-118.326926061504, 34.286065085524], - [-118.308228049277, 34.2861070676933], - [-118.299490248798, 34.2861010371601], - [-118.299407716081, 34.2932643093798], - [-118.290619946814, 34.2932325361813], - [-118.290632298649, 34.2927243889871], - [-118.286298348713, 34.2926534198115], - [-118.28642509561, 34.2890995504562], - [-118.286705959273, 34.2783421657793], - [-118.273692458316, 34.2780714618085], - [-118.27354957049, 34.2816661586135], - [-118.256119868053, 34.2816311329041], - [-118.23880105096, 34.2815940199469], - [-118.238914323127, 34.267088790148], - [-118.247632753234, 34.2672365711351], - [-118.256702228784, 34.2671945802833], - [-118.25651245968, 34.2522705924215], - [-118.26082844331, 34.2523274418209], - [-118.261741917665, 34.2523404358791], - [-118.265276648171, 34.2523845228472], - [-118.265305000798, 34.2506412133386], - [-118.265308930927, 34.2502701786597], - [-118.265971158175, 34.2505173031286], - [-118.266684617241, 34.2507839190628], - [-118.266703144994, 34.2477770777096], - [-118.266101273754, 34.2477742931177], - [-118.266011161053, 34.243941446747], - [-118.266036286033, 34.2403604547173], - [-118.266745253523, 34.2408009219825], - [-118.266746376417, 34.2406298869142], - [-118.2667497451, 34.2404263625053], - [-118.266778799311, 34.236289620143], - [-118.266778237864, 34.2362376339855], - [-118.266786519432, 34.2350484418665], - [-118.266788765221, 34.2348783241935], - [-118.266790730734, 34.2345988935355], - [-118.266791853629, 34.2344826181736], - [-118.266796345205, 34.2338680522831], - [-118.26680223995, 34.2330385668874], - [-118.266805047185, 34.2326574743272], - [-118.266883509635, 34.2218508564893], - [-118.273697371202, 34.2218011826273], - [-118.301522125681, 34.2215938980201], - [-118.334922190427, 34.2213367070164], - [-118.336093929528, 34.2213271903771], - [-118.337473966383, 34.2213165127541], - [-118.334557951427, 34.2156225931901], - [-118.335230564996, 34.2153874374974], - [-118.336429675763, 34.2145733231557], - [-118.336043820603, 34.2143142535872], - [-118.337717775439, 34.2125942996817], - [-118.337744583862, 34.2114679183763], - [-118.337748795164, 34.211275465942], - [-118.339579533865, 34.2113054136476], - [-118.339818569274, 34.2111990889555], - [-118.3398469228, 34.210127708928], - [-118.339942649073, 34.2065076685722], - [-118.340831560793, 34.2065229913322], - [-118.344340043424, 34.2065854427872], - [-118.348786984355, 34.2066527699833], - [-118.348866709836, 34.206653931168], - [-118.350139510305, 34.2066732002942], - [-118.350670920389, 34.2066799333807], - [-118.352316380665, 34.2067052387264], - [-118.353630728215, 34.2067261333495], - [-118.354176314613, 34.2067328664318], - [-118.359646914556, 34.2068180696297], - [-118.360739630657, 34.2068333923333], - [-118.361831645175, 34.2068496436826], - [-118.363005209652, 34.2068645020563], - [-118.362139879606, 34.2011317613075], - [-118.365958982595, 34.2011324582126], - [-118.365958982595, 34.201063733539], - [-118.370092917243, 34.2010641978951], - [-118.370096426961, 34.2010641978951], - [-118.370098672749, 34.2010639653456], - [-118.370101058675, 34.201063733539], - [-118.370103304463, 34.2010635009895], - [-118.370105550251, 34.201063269183], - [-118.370107796039, 34.2010628048269], - [-118.370110041828, 34.2010623404708], - [-118.37011341051, 34.2010616435651], - [-118.370115516161, 34.2010609474024], - [-118.370117761949, 34.2010602504968], - [-118.370119866702, 34.2010595543341], - [-118.37012295511, 34.2010581612658], - [-118.370125060761, 34.2010574643601], - [-118.370128008133, 34.2010558394851], - [-118.370129972749, 34.2010549107728], - [-118.370131798125, 34.201053749511], - [-118.370134605361, 34.2010518920864], - [-118.370136289702, 34.2010507315675], - [-118.37013811418, 34.2010495703056], - [-118.370140500106, 34.2010474810743], - [-118.370142886031, 34.2010453910999], - [-118.370144290098, 34.2010437662247], - [-118.370145693266, 34.2010423731561], - [-118.370147798917, 34.201040051375], - [-118.370149623396, 34.2010374970443], - [-118.370150747188, 34.2010358721689], - [-118.370151869184, 34.2010342465506], - [-118.370152851941, 34.2010326216752], - [-118.370153834698, 34.2010307642501], - [-118.370155097729, 34.2010282099191], - [-118.370156220623, 34.2010254237813], - [-118.370157063243, 34.2010226376434], - [-118.370157904964, 34.2010198515055], - [-118.370158326274, 34.2010179940801], - [-118.370158606548, 34.201015904848], - [-118.370158887721, 34.2010131187099], - [-118.370159027858, 34.2010112612844], - [-118.370159027858, 34.2010094038588], - [-118.37016029089, 34.196351548045], - [-118.365960666936, 34.1955214614251], - [-118.365960666936, 34.1956043540239], - [-118.361162681461, 34.1946556077614], - [-118.361152434378, 34.1945868778084], - [-118.361043093238, 34.1938563864003], - [-118.359973816878, 34.1867694089351], - [-118.358868608804, 34.1794338065909], - [-118.357770418369, 34.1721667792106], - [-118.356353326008, 34.1688575080581], - [-118.356288899734, 34.1687069990983], - [-118.356244545417, 34.1686034078991], - [-118.356105587721, 34.1682796265152], - [-118.356089304858, 34.1682406054324], - [-118.356026563824, 34.1680942762113], - [-118.355999894639, 34.1680317962509], - [-118.354787730453, 34.1652001572725], - [-118.354665194858, 34.1649137599012], - [-118.355426798234, 34.1649153854725], - [-118.355444904677, 34.1649105080154], - [-118.355471572963, 34.1649035396881], - [-118.355498382284, 34.1648968032662], - [-118.35552547188, 34.1648905321423], - [-118.355552562374, 34.1648847248299], - [-118.35557965197, 34.1648793828157], - [-118.355607022738, 34.1648742727072], - [-118.355634392608, 34.1648696271537], - [-118.355661903514, 34.1648654461554], - [-118.355689554557, 34.1648614970629], - [-118.355717206498, 34.164858013269], - [-118.355744997678, 34.164854993287], - [-118.355772789756, 34.1648524386037], - [-118.355800580936, 34.1648501158264], - [-118.355828513151, 34.1648482576046], - [-118.355856445367, 34.1648468639381], - [-118.355884377582, 34.1648459348271], - [-118.355912308899, 34.1648452376223], - [-118.355940381252, 34.1648450057162], - [-118.35717023147, 34.1648445411607], - [-118.357163072795, 34.1612913133014], - [-118.354427141308, 34.1612938688358], - [-118.353115600993, 34.1612941007517], - [-118.3529999429, 34.1610237176493], - [-118.351052283075, 34.1564737652742], - [-118.349776955646, 34.1534953841118], - [-118.349470967002, 34.1527807986955], - [-118.346333741908, 34.1454475860491], - [-118.345535925646, 34.1435903107807], - [-118.345232463065, 34.1428784296264], - [-118.345130981286, 34.1426409796828], - [-118.342485723947, 34.1446904152131], - [-118.342325430364, 34.1448161077456], - [-118.342228159887, 34.1448625743012], - [-118.342083026273, 34.1449313447565], - [-118.341084211966, 34.1454055337368], - [-118.340919005497, 34.1454840616529], - [-118.340869739192, 34.1455075274529], - [-118.340004969696, 34.1455951158002], - [-118.339153254517, 34.1453353696791], - [-118.338816246148, 34.1452331437952], - [-118.336649621972, 34.1464993420425], - [-118.332584465037, 34.1478363806964], - [-118.331040344607, 34.1483228668726], - [-118.330078866979, 34.1497523390391], - [-118.329079351088, 34.1501686541376], - [-118.329038225316, 34.1501860783666], - [-118.325733266792, 34.1532577320723], - [-118.322122600797, 34.1557317933308], - [-118.321881740011, 34.1557638511137], - [-118.317688853423, 34.1563176590073], - [-118.315830603816, 34.1544580669987], - [-118.315720981503, 34.1544378567179], - [-118.314647916049, 34.1542771005514], - [-118.312918377954, 34.153998331907], - [-118.312332508404, 34.1549716950874], - [-118.31033235283, 34.1607649481305], - [-118.309657774646, 34.1612643679443], - [-118.308740370162, 34.1614439264387], - [-118.307262220209, 34.1609621616095], - [-118.304347046975, 34.1588445937289], - [-118.302962518544, 34.1585363383174], - [-118.298838970217, 34.1576078500553], - [-118.297427492327, 34.1588764176643], - [-118.295478849744, 34.158797902199], - [-118.291463240287, 34.155712511823], - [-118.28797146093, 34.1556732530002], - [-118.283920760583, 34.1565206900182], - [-118.28268192811, 34.1567815628719], - [-118.280554885503, 34.1563480900883], - [-118.279326860661, 34.1550987654056], - [-118.279045435551, 34.153493990258], - [-118.278995325728, 34.1532087143557], - [-118.275598009613, 34.1532156836482], - [-118.275560252523, 34.1531471526188], - [-118.275549304755, 34.153125082801], - [-118.275382835254, 34.1528082113815], - [-118.272240275964, 34.1466961241811], - [-118.271122716055, 34.1445226704853], - [-118.267339404642, 34.1371685257987], - [-118.267268802451, 34.1370314369037], - [-118.265455469506, 34.1335086411046], - [-118.265357356409, 34.1333167082779], - [-118.263485070626, 34.1296782672397], - [-118.263218523413, 34.1291726188951], - [-118.262941589879, 34.1286711503162], - [-118.262654128988, 34.128173628827], - [-118.262356280878, 34.1276802879926], - [-118.262032466964, 34.1271660292149], - [-118.262011973697, 34.1271341933331], - [-118.261833012225, 34.1268609122983], - [-118.261789780802, 34.1267956125009], - [-118.261745987932, 34.1267314749376], - [-118.2614186643, 34.1262513711978], - [-118.261080673174, 34.1257763772058], - [-118.260732857174, 34.1253062610332], - [-118.260681625355, 34.1252391009416], - [-118.2603683379, 34.1248321912824], - [-118.260000870355, 34.1243725269956], - [-118.259623718073, 34.123918437577], - [-118.259414859769, 34.1236760537792], - [-118.259237022089, 34.1234696903531], - [-118.258841060881, 34.1230267509381], - [-118.258435977282, 34.1225896194236], - [-118.258021629357, 34.122158527179], - [-118.25759829828, 34.1217332430162], - [-118.257382562474, 34.1215222269109], - [-118.257075591074, 34.1212284767421], - [-118.256633030659, 34.1208173646025], - [-118.256181767366, 34.1204125248907], - [-118.255843354931, 34.1201180732928], - [-118.255603196628, 34.1199117011831], - [-118.255488240119, 34.119812001335], - [-118.254270741736, 34.1187652581311], - [-118.254265969886, 34.118769208635], - [-118.254168839545, 34.1188500853442], - [-118.254025529512, 34.11894978707], - [-118.251500421842, 34.1207046507283], - [-118.251145447167, 34.1209516910132], - [-118.253652167222, 34.1233074809101], - [-118.253347862919, 34.1242389028157], - [-118.253276700179, 34.1244538631232], - [-118.25287638843, 34.124303275031], - [-118.252494042988, 34.1243557949825], - [-118.247331957751, 34.1250655082959], - [-118.241735313394, 34.12583470624], - [-118.235250319662, 34.1267245034604], - [-118.235324290536, 34.1276068558948], - [-118.235551535556, 34.1280102668855], - [-118.236031151477, 34.1288349757341], - [-118.236435393354, 34.1314891416633], - [-118.236621934811, 34.1327197614981], - [-118.236562561561, 34.1332962602029], - [-118.236459115166, 34.1337465814373], - [-118.236414058366, 34.1339429278082], - [-118.236285486991, 34.1345031528875], - [-118.235742427554, 34.1345038495993], - [-118.235652596026, 34.1345043143217], - [-118.235107852248, 34.134507102656], - [-118.233725429467, 34.1345133767797], - [-118.233725850777, 34.1345150029361], - [-118.233722060785, 34.1351193725408], - [-118.233718972377, 34.1356050025105], - [-118.232641275209, 34.1356052344969], - [-118.230867944244, 34.1356061639296], - [-118.23041499661, 34.1356063966595], - [-118.229677115045, 34.1356059319432], - [-118.229200025188, 34.1356056992133], - [-118.228650649696, 34.1356056992133], - [-118.227928628786, 34.1356054672269], - [-118.227916838398, 34.1364043108341], - [-118.227894099344, 34.1377840293327], - [-118.227888906183, 34.1380888748409], - [-118.229161566514, 34.138084227814], - [-118.229163110718, 34.1383221552645], - [-118.229168444016, 34.1390779883357], - [-118.228795924346, 34.1390779883357], - [-118.228796766067, 34.1394920325632], - [-118.228798310271, 34.1403719282684], - [-118.228803082122, 34.142704175819], - [-118.228775852389, 34.1428417201875], - [-118.228778519487, 34.1445022251218], - [-118.228780765275, 34.1459020244682], - [-118.228642087853, 34.1457974759686], - [-118.228249355191, 34.1464517151173], - [-118.228695705597, 34.1467400337278], - [-118.228614576948, 34.1468905818464], - [-118.228086535545, 34.1467228414873], - [-118.227464312074, 34.1477594808684], - [-118.227016698636, 34.1476330961116], - [-118.227019786146, 34.1477137132526], - [-118.227021470487, 34.1478047845537], - [-118.227021190212, 34.1478958557565], - [-118.227018944424, 34.1479869268612], - [-118.227014873259, 34.1480779978676], - [-118.227008978514, 34.1481690687759], - [-118.227000978118, 34.1482599068909], - [-118.226991292483, 34.1483505129568], - [-118.226979502993, 34.1484411189256], - [-118.226965887228, 34.1485317247971], - [-118.226951851052, 34.1486132703702], - [-118.226242883562, 34.1497943891875], - [-118.224979207282, 34.1493683147914], - [-118.222466871859, 34.1492584268359], - [-118.221999607774, 34.1488639454947], - [-118.21944783092, 34.1482801193935], - [-118.21919110948, 34.1475190247849], - [-118.216805379918, 34.1475313376589], - [-118.216216562995, 34.1472295468303], - [-118.213207767342, 34.1472729914476], - [-118.211626452168, 34.147180990268], - [-118.210983033846, 34.1462228710172], - [-118.210497522282, 34.1462867618323], - [-118.210131037494, 34.1463353181653], - [-118.209836699101, 34.1463734200575], - [-118.208786091529, 34.1465114231087], - [-118.208624956225, 34.1466254965395], - [-118.20797859053, 34.1470808579287], - [-118.207735483956, 34.147251850143], - [-118.206018999281, 34.1473940336229], - [-118.205443937362, 34.1474356195505], - [-118.204241458811, 34.1492832855107], - [-118.203585267344, 34.1496972797269], - [-118.202712778624, 34.150637935438], - [-118.198932274446, 34.1511585562214], - [-118.198202954725, 34.1516310844194], - [-118.198207024992, 34.1490500353505], - [-118.196957525025, 34.1490514292777], - [-118.194425679093, 34.1490486414234], - [-118.183804786097, 34.1490625806943], - [-118.184225028767, 34.146285135159], - [-118.184573547249, 34.1454055337368], - [-118.183629193307, 34.1447106285697], - [-118.183373313588, 34.1445198824815], - [-118.18231302038, 34.1437385411543], - [-118.182324951804, 34.1434743745594], - [-118.182060369207, 34.1433289317655], - [-118.182063457615, 34.1433249824094], - [-118.182066404987, 34.1433212650182], - [-118.182069212222, 34.1433173149182], - [-118.182072019458, 34.1433131328527], - [-118.182074686556, 34.1433091834958], - [-118.182077353654, 34.14330500143], - [-118.182079879716, 34.1433010513292], - [-118.182082265642, 34.1432968692629], - [-118.182084652465, 34.1432926871965], - [-118.182086898254, 34.1432885051298], - [-118.182089144042, 34.1432840910976], - [-118.182091108657, 34.1432799090305], - [-118.182093214308, 34.1432754942544], - [-118.182095038787, 34.1432710802216], - [-118.182096863265, 34.1432668981538], - [-118.182098548505, 34.1432624833771], - [-118.182100232846, 34.1432580693435], - [-118.182101776151, 34.1432536545663], - [-118.182103180218, 34.1432490078234], - [-118.182104583387, 34.1432445937891], - [-118.182105846418, 34.1432401790112], - [-118.182106969312, 34.1432355322675], - [-118.182107952069, 34.1432311182326], - [-118.182108934826, 34.1432264714884], - [-118.182109776547, 34.1432218247441], - [-118.182110619167, 34.1432174099649], - [-118.182111320751, 34.14321276322], - [-118.182111882198, 34.1432081164749], - [-118.182112303508, 34.1432034697295], - [-118.182112724818, 34.1431990556928], - [-118.18211314523, 34.1431923175395], - [-118.1821187597, 34.1430675526852], - [-118.181253149381, 34.1423138459865], - [-118.180920351416, 34.1418389422586], - [-118.180769462296, 34.1413047881735], - [-118.18043792826, 34.1410111067463], - [-118.180527338479, 34.1407950271963], - [-118.180543058996, 34.1407994421021], - [-118.180552743733, 34.140801997513], - [-118.180568604388, 34.1408059477306], - [-118.180584606078, 34.1408096652325], - [-118.18060060687, 34.1408131500185], - [-118.18061660856, 34.1408164028324], - [-118.180632890525, 34.140819423674], - [-118.180649032352, 34.1408222117999], - [-118.180665314317, 34.1408247672102], - [-118.180681596281, 34.1408270906483], - [-118.180698018383, 34.1408291821144], - [-118.180714440484, 34.1408310408648], - [-118.180730862586, 34.1408326668996], - [-118.180747425723, 34.1408340609624], - [-118.180763847825, 34.1408349903376], - [-118.180780410962, 34.1408359197127], - [-118.180796973201, 34.1408366171158], - [-118.180813536338, 34.1408370818034], - [-118.180830098577, 34.1408370818034], - [-118.180846661714, 34.1408370818034], - [-118.18128094056, 34.1408301114896], - [-118.181889549165, 34.1407044130271], - [-118.182527914464, 34.1403452082102], - [-118.183250356684, 34.1396284207968], - [-118.183961288789, 34.1394920325632], - [-118.184225870488, 34.1395055087161], - [-118.18433072095, 34.1395106203597], - [-118.184692994436, 34.1395292081521], - [-118.185260196995, 34.1392922134923], - [-118.185690545711, 34.1388797951017], - [-118.185684509931, 34.1388588836742], - [-118.18540743626, 34.1388458721167], - [-118.185480003067, 34.1386067840194], - [-118.185538113286, 34.1385710021248], - [-118.185536287909, 34.1373393060929], - [-118.185900526909, 34.1373142119219], - [-118.185899685188, 34.1361879867755], - [-118.185218228603, 34.1360732025536], - [-118.185543166309, 34.1347310985446], - [-118.185631032324, 34.1345765789945], - [-118.185681282284, 34.1344892112144], - [-118.186109104938, 34.1337365894439], - [-118.182829271394, 34.131215874707], - [-118.182851869414, 34.1297551827803], - [-118.182591779291, 34.1291909762138], - [-118.181352384473, 34.1295023592161], - [-118.181449795986, 34.1282005847402], - [-118.180908279855, 34.1283358289338], - [-118.180729178245, 34.127849460431], - [-118.180678507873, 34.1277137506996], - [-118.180205067871, 34.1264298418319], - [-118.178595399171, 34.12672520098], - [-118.17846191491, 34.1267142786262], - [-118.178460230568, 34.1267142786262], - [-118.177821163685, 34.1266612953743], - [-118.177689363765, 34.1266503737558], - [-118.177799547525, 34.1268311673705], - [-118.176530677186, 34.1266248116454], - [-118.176902214997, 34.1234859577585], - [-118.176803540453, 34.123477824428], - [-118.176843403194, 34.1231408560716], - [-118.173356396586, 34.1245563461607], - [-118.172816704933, 34.1247759534199], - [-118.172779369153, 34.1249184067346], - [-118.17255142165, 34.1257958975594], - [-118.172549877446, 34.1258019395727], - [-118.172547631658, 34.1258098406664], - [-118.172545105595, 34.1258177417593], - [-118.172542578634, 34.1258254108383], - [-118.172539771399, 34.1258333119298], - [-118.172536824027, 34.1258409802637], - [-118.172533876654, 34.1258486493405], - [-118.172530507972, 34.125856317673], - [-118.172527139289, 34.1258637539917], - [-118.17252363047, 34.1258714230665], - [-118.172519840478, 34.1258788593839], - [-118.172516050486, 34.1258862957006], - [-118.172511980219, 34.1258934992599], - [-118.172507768917, 34.1259009355754], - [-118.172503417478, 34.1259081398771], - [-118.172499066937, 34.1259153434346], - [-118.172494434325, 34.1259223149785], - [-118.172489662474, 34.1259295192785], - [-118.172484609451, 34.1259364908212], - [-118.172479556427, 34.1259432296069], - [-118.172474363266, 34.1259502011485], - [-118.17246902907, 34.1259569406767], - [-118.172463554737, 34.1259634474478], - [-118.172457940266, 34.1259701862313], - [-118.172452045522, 34.1259766930014], - [-118.172445026984, 34.1259841293095], - [-118.172440114996, 34.1259894745274], - [-118.172433939079, 34.1259955165269], - [-118.172427622126, 34.1260017905387], - [-118.172421165934, 34.1260078325372], - [-118.172414708843, 34.1260138745354], - [-118.172407971479, 34.1260196845204], - [-118.172401093977, 34.1260254937614], - [-118.172394216475, 34.1260310709893], - [-118.172387198836, 34.1260366482169], - [-118.172380040162, 34.1260422254441], - [-118.17237274135, 34.1260475706582], - [-118.172365301503, 34.1260526831158], - [-118.172357862554, 34.1260577955731], - [-118.172350283468, 34.1260629080301], - [-118.172342563346, 34.1260677877306], - [-118.172334703088, 34.1260726681744], - [-118.172326842829, 34.1260773158618], - [-118.172318842433, 34.1260819635489], - [-118.172310701002, 34.1260863784796], - [-118.172302560468, 34.1260907941537], - [-118.1722942789, 34.1260949770715], - [-118.172285857194, 34.126099159989], - [-118.172277435488, 34.1261031101502], - [-118.172268873645, 34.1261068282988], - [-118.172260170767, 34.1261107792033], - [-118.172251468787, 34.1261142645954], - [-118.172242765908, 34.1261177507311], - [-118.172233922892, 34.1261212361229], - [-118.172224940638, 34.1261242574898], - [-118.172215957485, 34.126127510869], - [-118.172206974332, 34.1261305314921], - [-118.172197850144, 34.1261333201026], - [-118.172188726854, 34.1261358767007], - [-118.172179463427, 34.126138432555], - [-118.172170199101, 34.126140989153], - [-118.172160934776, 34.1261433129948], - [-118.172151531211, 34.1261454040806], - [-118.172142126749, 34.1261474959099], - [-118.171622086641, 34.1262555541076], - [-118.171604120335, 34.1262595050051], - [-118.171586154029, 34.1262636879145], - [-118.171568327861, 34.1262681028358], - [-118.171550642728, 34.1262727505124], - [-118.171532956697, 34.1262776309444], - [-118.171515411701, 34.1262827433881], - [-118.171498006842, 34.126288320599], - [-118.171480742121, 34.1262938978096], - [-118.171463617536, 34.1262997070317], - [-118.17144649385, 34.1263059817646], - [-118.171429650439, 34.1263124885089], - [-118.171412807027, 34.1263189952526], - [-118.171396103753, 34.1263259667633], - [-118.171379681651, 34.1263331702852], - [-118.171363259549, 34.1263406065619], - [-118.171347117722, 34.1263482755935], - [-118.17133111693, 34.1263559438806], - [-118.171315255377, 34.1263640776779], - [-118.17129953486, 34.1263724434862], - [-118.171284095515, 34.1263810413053], - [-118.171268796307, 34.1263898718789], - [-118.171253637237, 34.1263987024515], - [-118.171238618304, 34.1264079977901], - [-118.171223879645, 34.1264172931277], - [-118.171209423057, 34.1264270532311], - [-118.171194965571, 34.1264368133334], - [-118.171180929395, 34.1264470382012], - [-118.171167033355, 34.1264572630678], - [-118.171153278352, 34.1264677206882], - [-118.171139803623, 34.1264784103188], - [-118.171128714819, 34.1264872408822], - [-118.170941191503, 34.1266427047517], - [-118.170876905366, 34.1266961527806], - [-118.170866097735, 34.1267052160757], - [-118.170808970273, 34.1267521573551], - [-118.170768405948, 34.1267856204295], - [-118.170765318438, 34.1267881762642], - [-118.170756756595, 34.1267951477361], - [-118.170748053717, 34.1268018871969], - [-118.170739210701, 34.1268083939031], - [-118.170730087411, 34.1268149006087], - [-118.170720964121, 34.1268214073138], - [-118.170711699796, 34.1268276812643], - [-118.170702296231, 34.1268337232038], - [-118.170692750733, 34.126839765143], - [-118.170683206133, 34.1268455750712], - [-118.170673381259, 34.1268511522451], - [-118.170663415349, 34.1268567294185], - [-118.170653449439, 34.1268620738376], - [-118.170643343393, 34.1268674189999], - [-118.170633097208, 34.1268725314079], - [-118.170622851024, 34.1268774110617], - [-118.170612323667, 34.1268822914588], - [-118.170601796311, 34.1268869391016], - [-118.170591128817, 34.1268915867442], - [-118.170580461323, 34.1268957696223], - [-118.170569653691, 34.1268999525003], - [-118.170558705923, 34.126904135378], - [-118.170547757256, 34.1269078534913], - [-118.170536668453, 34.1269115716045], - [-118.170525580547, 34.1269152897175], - [-118.170514351606, 34.1269185430663], - [-118.170502981629, 34.1269217964149], - [-118.170491753587, 34.1269248170096], - [-118.170480243473, 34.1269278383477], - [-118.170468874395, 34.126930626932], - [-118.170457224144, 34.1269331827623], - [-118.17044571403, 34.1269355065824], - [-118.170434064677, 34.1269378304024], - [-118.170422414427, 34.1269399222121], - [-118.170410624038, 34.126941781268], - [-118.170398973787, 34.1269434075701], - [-118.170387183399, 34.1269450346158], - [-118.170375252874, 34.1269464289076], - [-118.170363462486, 34.1269475904456], - [-118.170351672098, 34.1269487527273], - [-118.170339741573, 34.1269494495014], - [-118.170327811047, 34.1269501470191], - [-118.170315879624, 34.126950611783], - [-118.170303949098, 34.1269510765469], - [-118.170292018573, 34.1269513085571], - [-118.17028008715, 34.1269513085571], - [-118.170268156624, 34.1269510765469], - [-118.170256226099, 34.126950611783], - [-118.170244295574, 34.1269501470191], - [-118.170226329268, 34.1269492174912], - [-118.170200782978, 34.1269473584355], - [-118.170175377723, 34.1269452666259], - [-118.170149972469, 34.1269429428061], - [-118.170124566316, 34.1269399222121], - [-118.170099302097, 34.1269369008744], - [-118.170074036081, 34.1269331827623], - [-118.170048911999, 34.1269292326399], - [-118.170023927156, 34.1269250497634], - [-118.169998943212, 34.1269201693688], - [-118.169974098506, 34.1269152897175], - [-118.169949394836, 34.1269099445582], - [-118.169924691165, 34.126904135378], - [-118.169900268668, 34.1268978606895], - [-118.169875845272, 34.1268915867442], - [-118.169851703048, 34.1268846152802], - [-118.169827560825, 34.1268774110617], - [-118.169803559637, 34.1268699748321], - [-118.169779837826, 34.1268620738376], - [-118.169756257049, 34.1268539408318], - [-118.169732817309, 34.1268453423172], - [-118.169709657842, 34.1268365117912], - [-118.169686497478, 34.1268272164997], - [-118.169663759321, 34.1268176891966], - [-118.169641020267, 34.1268076963842], - [-118.169618562384, 34.1267977043142], - [-118.169596385675, 34.1267870147239], - [-118.169574348204, 34.1267760923781], - [-118.169552592805, 34.1267649380202], - [-118.169531116781, 34.1267535516501], - [-118.169509782692, 34.1267416997698], - [-118.169488868115, 34.1267296158771], - [-118.169468094574, 34.1267172999719], - [-118.169447462069, 34.126704518556], - [-118.169427249077, 34.1266915051272], - [-118.169407318155, 34.1266782596856], - [-118.169265973635, 34.1265822852001], - [-118.169252780079, 34.1265734546466], - [-118.169239445487, 34.1265648568471], - [-118.169225970757, 34.1265564910579], - [-118.169212355891, 34.1265483572791], - [-118.169198459852, 34.1265404562545], - [-118.169184423676, 34.1265325552291], - [-118.169170247362, 34.1265251189692], - [-118.169155930013, 34.1265176827087], - [-118.16914133239, 34.1265104792025], - [-118.169126734766, 34.1265037397185], - [-118.169111997006, 34.1264970009775], - [-118.16909711821, 34.1264904942474], - [-118.169081959139, 34.1264839875169], - [-118.169066800069, 34.1264779455523], - [-118.169051500861, 34.1264721355988], - [-118.169036061517, 34.1264665584], - [-118.169020481136, 34.1264612139559], - [-118.169004900756, 34.1264558687679], - [-118.168989040101, 34.1264509890898], - [-118.168973179447, 34.126446341423], - [-118.168957317894, 34.1264416937559], - [-118.168941176965, 34.1264375108553], - [-118.168925035137, 34.1264335599661], - [-118.16890889331, 34.1264296098203], - [-118.168892471208, 34.1264261236975], - [-118.168885172397, 34.1264247293971], - [-118.168561919031, 34.1263587324838], - [-118.1685415668, 34.1263545495791], - [-118.168510265903, 34.1263475780708], - [-118.168479246178, 34.1263403745501], - [-118.16844836659, 34.126332473506], - [-118.168417627139, 34.1263243397057], - [-118.168387168861, 34.1263155091253], - [-118.16835685072, 34.126306446532], - [-118.168326672716, 34.1262969184267], - [-118.168296635748, 34.1262869262963], - [-118.168267019192, 34.1262764686535], - [-118.168237402635, 34.1262655469853], - [-118.168208207388, 34.1262541598044], - [-118.168179153177, 34.1262425406098], - [-118.168150378342, 34.1262302246336], - [-118.168121885578, 34.1262176758998], - [-118.168093532052, 34.1262048944083], - [-118.168065599837, 34.1261914161346], - [-118.168037948794, 34.1261777058465], - [-118.168010578026, 34.1261635300442], - [-118.16796875067, 34.126140989153], - [-118.167953871874, 34.1261333201026], - [-118.16793885294, 34.1261256517952], - [-118.167923553733, 34.126118447512], - [-118.167908254525, 34.1261114759843], - [-118.16789281518, 34.1261045044559], - [-118.167877093764, 34.1260979976956], - [-118.167861373247, 34.126091723691], - [-118.167845512592, 34.1260854489422], - [-118.167829511801, 34.1260796397054], - [-118.167813369973, 34.1260738297245], - [-118.167797088009, 34.1260684852556], - [-118.167780665907, 34.1260633727989], - [-118.167764243805, 34.126058260342], - [-118.167747540531, 34.1260536126536], - [-118.167730977394, 34.1260491969775], - [-118.167714133982, 34.1260447820449], - [-118.16769729057, 34.1260408311373], - [-118.167680307022, 34.1260371129858], - [-118.167663323473, 34.1260336275905], - [-118.167646198889, 34.1260303742077], - [-118.16762879403, 34.1260271208247], - [-118.16760928352, 34.1260234026727], - [-118.167589773909, 34.1260194517641], - [-118.167570403537, 34.1260150368299], - [-118.1675511742, 34.1260103891391], - [-118.167531944863, 34.1260055086917], - [-118.167512855663, 34.1260003962312], - [-118.1674939066, 34.1259950517577], - [-118.167475097675, 34.125989241771], - [-118.167456289648, 34.125983199771], - [-118.167437621758, 34.1259771577707], - [-118.167419234142, 34.1259704189878], - [-118.167400846527, 34.1259636794607], - [-118.167382740084, 34.1259567079202], - [-118.167364633641, 34.1259492716096], - [-118.167346807473, 34.1259418352985], - [-118.16732912234, 34.1259339342171], - [-118.167311436308, 34.1259258011221], - [-118.167294171587, 34.1259174352697], - [-118.167276906865, 34.1259088366598], - [-118.167258379113, 34.1258990764966], - [-118.167253467125, 34.125896752648], - [-118.167250098442, 34.1258953583389], - [-118.16724672976, 34.1258939640297], - [-118.167241535701, 34.1258923377074], - [-118.16723634254, 34.1258907106415], - [-118.167232693584, 34.125889781102], - [-118.167229184764, 34.1258888515625], - [-118.167225534909, 34.1258881547797], - [-118.167220060576, 34.1258872252401], - [-118.167216270584, 34.1258867604704], - [-118.167212621627, 34.1258865277136], - [-118.167208831635, 34.1258860629439], - [-118.167205042541, 34.1258860629439], - [-118.167199427172, 34.1258860629439], - [-118.167195778215, 34.1258860629439], - [-118.167191988223, 34.1258862957006], - [-118.16718651389, 34.1258869924834], - [-118.167182724796, 34.1258874572532], - [-118.167177250463, 34.1258883867927], - [-118.167173600608, 34.1258893163322], - [-118.16716826731, 34.1258907106415], - [-118.16716475849, 34.125891640181], - [-118.167161248772, 34.1258928024772], - [-118.167157880988, 34.1258939640297], - [-118.167156055612, 34.1258946615561], - [-118.166520217274, 34.1261470311416], - [-118.166511795568, 34.1261502845199], - [-118.166505198341, 34.1261528403738], - [-118.166498460976, 34.1261551642153], - [-118.166491583474, 34.1261572560445], - [-118.166484705074, 34.1261595798859], - [-118.166477827572, 34.1261616709712], - [-118.16647095007, 34.1261635300442], - [-118.166463932432, 34.1261653891172], - [-118.166456913894, 34.1261670161779], - [-118.166449895357, 34.1261688752508], - [-118.166442877718, 34.1261702695554], - [-118.166435719043, 34.12617166386], - [-118.166428560369, 34.1261730581646], - [-118.166421402593, 34.1261742197133], - [-118.166414243918, 34.1261753820056], - [-118.166407085244, 34.1261765435542], - [-118.166399786432, 34.1261774730906], - [-118.166392628656, 34.1261781706147], - [-118.166385329844, 34.1261788673951], - [-118.166378030134, 34.1261795649192], - [-118.166370731322, 34.1261800296873], - [-118.166363432511, 34.1261804944555], - [-118.166356133699, 34.1261807264677], - [-118.166348835786, 34.1261814239918], - [-118.166326798315, 34.1261809592236], - [-118.166322166602, 34.1261807264677], - [-118.16631486779, 34.1261804944555], - [-118.166307568978, 34.1261800296873], - [-118.166300270166, 34.1261795649192], - [-118.166292971355, 34.1261788673951], - [-118.166285672543, 34.1261781706147], - [-118.166278373731, 34.1261772410783], - [-118.166271215057, 34.126176311542], - [-118.166263916245, 34.1261753820056], - [-118.166256757571, 34.1261742197133], - [-118.166249599795, 34.1261728254087], - [-118.166242581257, 34.12617166386], - [-118.166235422583, 34.1261700367995], - [-118.166228404944, 34.1261684104825], - [-118.166221246269, 34.1261667834219], - [-118.166214368767, 34.1261651571049], - [-118.16620735023, 34.1261632980319], - [-118.166200472728, 34.126161206203], - [-118.166193595227, 34.1261591151176], - [-118.166186717725, 34.1261570232885], - [-118.166179839325, 34.126154699447], - [-118.166173242995, 34.1261523756055], - [-118.166166505631, 34.1261498197516], - [-118.166159908403, 34.1261472631539], - [-118.166153311176, 34.1261447072999], - [-118.166146713948, 34.1261419186897], - [-118.166140257757, 34.1261391300794], - [-118.166133941702, 34.1261361087131], - [-118.166127484611, 34.1261330880902], - [-118.166121308694, 34.1261300667236], - [-118.166114992639, 34.1261268133445], - [-118.166108956859, 34.1261235599653], - [-118.166102780941, 34.1261200745736], - [-118.166096886196, 34.126116588438], - [-118.166090850416, 34.1261131030459], - [-118.166085095808, 34.1261093848976], - [-118.166079200165, 34.1261056667492], - [-118.166073585694, 34.1261017158444], - [-118.166067971224, 34.1260979976956], - [-118.166062497789, 34.1260938147781], - [-118.166057023456, 34.1260898646164], - [-118.166051689259, 34.1260856816984], - [-118.166046355962, 34.1260814987802], - [-118.166041162801, 34.1260770831056], - [-118.166036108879, 34.126072900187], - [-118.166031055856, 34.1260684852556], - [-118.166026143868, 34.1260638375677], - [-118.166021371119, 34.1260591898796], - [-118.166016599268, 34.1260545421913], - [-118.166011966656, 34.1260498945027], - [-118.166007475079, 34.1260450140575], - [-118.166003124539, 34.1260403663684], - [-118.165998773099, 34.12603525391], - [-118.165994561797, 34.1260303742077], - [-118.165990491531, 34.1260252617487], - [-118.165986421264, 34.1260201492895], - [-118.165982631272, 34.1260150368299], - [-118.16597884128, 34.12600992437], - [-118.165975192323, 34.1260045791534], - [-118.165963963382, 34.1259883122326], - [-118.165961296284, 34.125982502989], - [-118.165958208774, 34.1259769257579], - [-118.165955260504, 34.1259715805392], - [-118.165952313131, 34.1259657712945], - [-118.165949646033, 34.1259601940623], - [-118.165946979833, 34.1259546168297], - [-118.165944452872, 34.1259488068402], - [-118.165942066947, 34.125942997594], - [-118.165939821159, 34.1259374203602], - [-118.165794126098, 34.125553055116], - [-118.167604230497, 34.1249720886128], - [-118.168273896693, 34.1243072252761], - [-118.168466051723, 34.1239189023575], - [-118.168015490912, 34.1239293595483], - [-118.168069249692, 34.1238714947242], - [-118.168051423523, 34.1231166872551], - [-118.167964259093, 34.1228220130523], - [-118.167484221863, 34.1211097218114], - [-118.167704027935, 34.1207495041201], - [-118.167942924105, 34.1203523329152], - [-118.167952187532, 34.1203376920796], - [-118.168902015808, 34.1199286668488], - [-118.168950300255, 34.119671165859], - [-118.169011497983, 34.1193458024764], - [-118.168845309656, 34.1186729931388], - [-118.168878575169, 34.1178479523855], - [-118.169351875034, 34.1173640796695], - [-118.169921322483, 34.1167772470241], - [-118.170130461959, 34.116556922131], - [-118.171337853397, 34.1153121221847], - [-118.172496259702, 34.1138069960634], - [-118.173830819346, 34.1131436722501], - [-118.175480912233, 34.113122521664], - [-118.175642889259, 34.1126321141161], - [-118.176987554949, 34.1122074796841], - [-118.177004819671, 34.1122023663894], - [-118.176953587852, 34.1121351958902], - [-118.176898144731, 34.1120619830868], - [-118.177030646235, 34.1109986413593], - [-118.177458468889, 34.1108963739451], - [-118.177249189276, 34.1104633634182], - [-118.17800700266, 34.1103278578384], - [-118.178006862523, 34.1031085800004], - [-118.178006019903, 34.1029621379626], - [-118.17800616004, 34.098599908608], - [-118.175257175133, 34.0986231545911], - [-118.169291238753, 34.0986694137066], - [-118.168543531416, 34.0986675540292], - [-118.167965240952, 34.0986661592711], - [-118.167784596036, 34.0986698786259], - [-118.16649354809, 34.0986749927385], - [-118.166292831218, 34.0986752255701], - [-118.166291146876, 34.0986754576578], - [-118.166288479778, 34.0986754576578], - [-118.166285952817, 34.0986756904894], - [-118.166073585694, 34.0986749927385], - [-118.161484317487, 34.0986724360542], - [-118.161286126677, 34.0986731330612], - [-118.160378688103, 34.0986738308122], - [-118.160179374399, 34.0986738308122], - [-118.159223791516, 34.0986740628999], - [-118.159058726082, 34.0986742957315], - [-118.156684927944, 34.0986747606508], - [-118.15536987881, 34.0986747606508], - [-118.1553790021, 34.0983158421788], - [-118.155379703684, 34.0982935259541], - [-118.155381669198, 34.0982498233304], - [-118.15538461657, 34.0982063527731], - [-118.1553885467, 34.0981628829374], - [-118.155393319449, 34.0981194123354], - [-118.155398933021, 34.098075942455], - [-118.155405530248, 34.0980327046418], - [-118.155412970096, 34.0979894668064], - [-118.155421390903, 34.097946228949], - [-118.155430795366, 34.0979034559931], - [-118.155440901413, 34.0978604501816], - [-118.155451990217, 34.0978179100163], - [-118.155464060879, 34.0977753690857], - [-118.155476974161, 34.0977330609678], - [-118.155490730063, 34.0976909856632], - [-118.155505327687, 34.0976489095937], - [-118.155520908067, 34.0976072991724], - [-118.155537330169, 34.0975659208213], - [-118.15555459489, 34.0975245424501], - [-118.155572701333, 34.0974836289845], - [-118.155591650396, 34.0974429475902], - [-118.15561144118, 34.0974024990114], - [-118.155632214721, 34.0973625153398], - [-118.155653689846, 34.0973225316494], - [-118.155676007591, 34.097283012867], - [-118.155704079943, 34.0972358231393], - [-118.15573327519, 34.0971886326414], - [-118.155763453194, 34.0971416749531], - [-118.155794613056, 34.0970954150026], - [-118.155826615538, 34.097049387119], - [-118.155859740914, 34.0970038241389], - [-118.15589370891, 34.0969589581554], - [-118.155928798901, 34.096914557821], - [-118.155964591376, 34.0968706216486], - [-118.156001506744, 34.0968271511268], - [-118.156039123696, 34.0967841447685], - [-118.156077722508, 34.0967418361551], - [-118.156117304974, 34.0967002252878], - [-118.156157589025, 34.0966588464933], - [-118.156198854934, 34.0966183975398], - [-118.156240963463, 34.0965784134978], - [-118.156283773576, 34.0965391264616], - [-118.156327566446, 34.0965003050822], - [-118.156372061799, 34.0964621807103], - [-118.156417397974, 34.0964247533468], - [-118.156463436633, 34.0963880237366], - [-118.156510317911, 34.0963517590424], - [-118.156557900773, 34.0963164241973], - [-118.15660618522, 34.0962815542698], - [-118.156655311388, 34.0962476141933], - [-118.158541773484, 34.0949520698266], - [-118.160673166633, 34.0935002825896], - [-118.160666148994, 34.0930495149282], - [-118.160657447014, 34.0926987088921], - [-118.160623057708, 34.0912450239103], - [-118.160623479018, 34.0911076277094], - [-118.16061772441, 34.0904197156627], - [-118.160609302704, 34.0893096048878], - [-118.16059863521, 34.088036738245], - [-118.160585440756, 34.0864337119135], - [-118.160574351952, 34.0851208125796], - [-118.160574351952, 34.0850810552317], - [-118.160553298136, 34.0825898137113], - [-118.160541507748, 34.0811696684093], - [-118.160534489211, 34.0803272883448], - [-118.16051975145, 34.078404641356], - [-118.160508382372, 34.0769237379614], - [-118.160506978305, 34.0751316924686], - [-118.161155730824, 34.0749231175378], - [-118.16119797949, 34.074909166044], - [-118.161239947882, 34.0748947494979], - [-118.161281494964, 34.0748794028494], - [-118.161322761771, 34.0748638240452], - [-118.161363747406, 34.0748475472908], - [-118.161404311731, 34.0748305725858], - [-118.161441647511, 34.074814295825], - [-118.161444454746, 34.0748131335711], - [-118.16148417735, 34.0747952287583], - [-118.16151449549, 34.0747808121929], - [-118.161555199953, 34.0747610479148], - [-118.161593799662, 34.074741282888], - [-118.161632118199, 34.0747210535498], - [-118.161670016324, 34.0747003584117], - [-118.161707493139, 34.074678966064], - [-118.161744548645, 34.0746571086597], - [-118.161773182444, 34.0746394367116], - [-118.161739775896, 34.0743817978921], - [-118.161706370245, 34.0741343894796], - [-118.16162552187, 34.0734979609438], - [-118.16157779887, 34.0730238330418], - [-118.161575693219, 34.0730005800295], - [-118.161573868741, 34.0729756996712], - [-118.161572745847, 34.0729508185616], - [-118.161571903227, 34.0729257052863], - [-118.16157176309, 34.0729008249061], - [-118.1615721844, 34.0728759437745], - [-118.161573026121, 34.0728510633796], - [-118.161574430188, 34.0728259500748], - [-118.161576254666, 34.0728010689212], - [-118.161578781627, 34.072776420663], - [-118.161581587964, 34.072751540239], - [-118.161584957545, 34.0727282871519], - [-118.161589167948, 34.0727020107837], - [-118.161593659525, 34.0726773624968], - [-118.161598712548, 34.0726527142027], - [-118.161604186882, 34.0726282988046], - [-118.161610221764, 34.0726038826554], - [-118.161616818991, 34.0725794672433], - [-118.161623837528, 34.0725552839835], - [-118.161631416614, 34.0725311007168], - [-118.161639558046, 34.0725071496026], - [-118.161648119889, 34.0724831992257], - [-118.161657243179, 34.0724594810017], - [-118.161666787779, 34.072435762771], - [-118.161676893826, 34.0724122766934], - [-118.161684754084, 34.0723941392134], - [-118.161692193033, 34.0723757695697], - [-118.161699211571, 34.072357167018], - [-118.161705948935, 34.0723385644622], - [-118.16171226499, 34.0723197289983], - [-118.161718160633, 34.0723011264343], - [-118.161723634068, 34.0722822917062], - [-118.161728828127, 34.0722632240696], - [-118.161733459841, 34.0722441564287], - [-118.16173781128, 34.0722250887836], - [-118.161741741409, 34.0722060211341], - [-118.161745250229, 34.072186720576], - [-118.161748338637, 34.0721676529179], - [-118.161751004837, 34.0721481201905], - [-118.161753390762, 34.0721288203633], - [-118.161755216139, 34.0721095197875], - [-118.161756759444, 34.0720902199514], - [-118.161757742201, 34.0720706872062], - [-118.161758443786, 34.0720513866172], - [-118.161758724958, 34.072031853863], - [-118.161758584821, 34.0720125540093], - [-118.161758023374, 34.0719930212462], - [-118.161757040617, 34.0719737206395], - [-118.161755777586, 34.0719541878674], - [-118.161753952209, 34.0719348879959], - [-118.161751706421, 34.071915587376], - [-118.161749180358, 34.0718962874958], - [-118.161746232986, 34.0718772197723], - [-118.161742864304, 34.071857686978], - [-118.161739074311, 34.0718386192458], - [-118.161734863908, 34.0718195515094], - [-118.161730231296, 34.0718004837687], - [-118.161725178272, 34.0717814160237], - [-118.161716054982, 34.0717486284215], - [-118.161710721684, 34.0717277003956], - [-118.161705667763, 34.0717067723646], - [-118.161701176186, 34.0716856121667], - [-118.161697246057, 34.0716646841253], - [-118.1616935971, 34.0716435231727], - [-118.161690368555, 34.071622362959], - [-118.161687702355, 34.071600969834], - [-118.161685455669, 34.0715798088655], - [-118.16168363119, 34.0715586486359], - [-118.161682228022, 34.0715372554948], - [-118.161681245265, 34.0715158623482], - [-118.161680823955, 34.0714944691963], - [-118.161680823955, 34.0714733082012], - [-118.161681245265, 34.0714519150386], - [-118.161682086986, 34.0714305218705], - [-118.161683350916, 34.0714093616036], - [-118.161685175394, 34.0713879684247], - [-118.161687281045, 34.0713668074031], - [-118.161689947245, 34.0713454142135], - [-118.161693035653, 34.0713242539253], - [-118.161696544473, 34.0713030928878], - [-118.161700614739, 34.0712821647518], - [-118.161704966178, 34.0712610044479], - [-118.161709879065, 34.0712400763016], - [-118.161715072225, 34.0712191481501], - [-118.161720826833, 34.0711984521564], - [-118.161727002751, 34.0711777569019], - [-118.161733599978, 34.0711570608982], - [-118.161740618515, 34.0711365977967], - [-118.161748197601, 34.0711161346903], - [-118.16175605786, 34.0710959044864], - [-118.161764339429, 34.0710756735336], - [-118.161773041409, 34.0710556754834], - [-118.161782305734, 34.0710356774286], - [-118.161786797311, 34.071025910935], - [-118.161830590181, 34.0709361521552], - [-118.161876067392, 34.0708468583525], - [-118.161923509219, 34.0707584946009], - [-118.161972776422, 34.0706705958302], - [-118.162023868104, 34.0705836271155], - [-118.162076644127, 34.0704973562947], - [-118.162131385664, 34.0704117826257], - [-118.162187951679, 34.0703271390203], - [-118.162246201137, 34.0702431925714], - [-118.162313855956, 34.0701494801889], - [-118.162316101744, 34.070146224657], - [-118.162324383313, 34.0701352949987], - [-118.162332945156, 34.0701245982491], - [-118.162341787273, 34.0701139014982], - [-118.162350770426, 34.0701032047459], - [-118.162359894614, 34.0700927409024], - [-118.162369298179, 34.0700825092238], - [-118.162378983814, 34.0700722775439], - [-118.162388808688, 34.0700622780289], - [-118.162398914735, 34.0700525114231], - [-118.162409161818, 34.0700427448161], - [-118.162419548139, 34.070032978208], - [-118.162430215633, 34.0700236766755], - [-118.162441023264, 34.0700143751419], - [-118.162452112068, 34.0700053065178], - [-118.162463341009, 34.0699962371485], - [-118.162474850225, 34.0699874006888], - [-118.162486360338, 34.0699787971386], - [-118.162498150726, 34.069970425754], - [-118.162510081252, 34.0699620543686], - [-118.16252229295, 34.0699539151488], - [-118.162534644785, 34.0699460088388], - [-118.16254699662, 34.0699383354387], - [-118.16255962873, 34.0699308942044], - [-118.162572401875, 34.0699234529695], - [-118.162585455294, 34.0699162439005], - [-118.162598509612, 34.0699092677415], - [-118.162622932109, 34.0698964784874], - [-118.16263416105, 34.0698906646482], - [-118.162645109717, 34.0698843864754], - [-118.162656057485, 34.069878107558], - [-118.162666724979, 34.0698718293842], - [-118.162677392473, 34.0698650853883], - [-118.16268791983, 34.069858342136], - [-118.162698166014, 34.069851598139], - [-118.162708412198, 34.069844389808], - [-118.162718518245, 34.0698371807323], - [-118.162728344018, 34.0698299724], - [-118.162738028755, 34.0698222982453], - [-118.162747713492, 34.0698148570008], - [-118.162757117955, 34.0698069506779], - [-118.162766241245, 34.0697990443542], - [-118.162775365433, 34.0697911380297], - [-118.162784348586, 34.0697829995376], - [-118.162793050566, 34.0697746281337], - [-118.162801613308, 34.0697662567289], - [-118.162807789225, 34.0697599778027], - [-118.162452252205, 34.0697588154795], - [-118.163598305777, 34.0675564091053], - [-118.164126908626, 34.0665411111224], - [-118.164138699014, 34.0665176241549], - [-118.164170841633, 34.0664641381096], - [-118.164183754915, 34.0664413483931], - [-118.164366365345, 34.0660497363888], - [-118.164447916203, 34.0658655571956], - [-118.164525536033, 34.0656802144788], - [-118.164599365872, 34.0654937089749], - [-118.164669126342, 34.0653060406763], - [-118.164735095921, 34.0651176739345], - [-118.164796995234, 34.0649281436403], - [-118.164854965316, 34.0647379156358], - [-118.164908864233, 34.0645467569909], - [-118.164958833021, 34.0643549006251], - [-118.165004731542, 34.0641623457897], - [-118.165046558898, 34.0639690924797], - [-118.165084315988, 34.0637753743623], - [-118.165118142948, 34.0635811899459], - [-118.16513540767, 34.0634707261421], - [-118.165311983217, 34.0622986342272], - [-118.169269062043, 34.0622946803431], - [-118.170488805316, 34.062294215224], - [-118.171847226909, 34.0622958435129], - [-118.172843654392, 34.0618367697463], - [-118.173035529146, 34.0621253768298], - [-118.173330148712, 34.0619897946115], - [-118.173406084201, 34.0621037491188], - [-118.17307567306, 34.0622972388701], - [-118.175937648961, 34.0623007268909], - [-118.176293887565, 34.0622988664147], - [-118.177694697961, 34.0623000295844], - [-118.17778059936, 34.0622997966528], - [-118.178020337252, 34.0622997966528], - [-118.178986306457, 34.0623009598225], - [-118.180904349725, 34.062302587367], - [-118.181314206074, 34.0623030524861], - [-118.181315048694, 34.061758861472], - [-118.188019007674, 34.061763977814], - [-118.189220363331, 34.0617646758691], - [-118.189430484666, 34.061764908058], - [-118.192637611266, 34.061767001479], - [-118.192587221168, 34.0559367291994], - [-118.192580905113, 34.0552050388472], - [-118.192579922356, 34.0551017737117], - [-118.192576132364, 34.0546828977734], - [-118.19257585209, 34.0546219616801], - [-118.192568553278, 34.0537788524688], - [-118.192567289349, 34.0537293126676], - [-118.192554376066, 34.0524573060998], - [-118.192554657239, 34.0522382093044], - [-118.192553394208, 34.0520877256685], - [-118.1925407612, 34.0506810245259], - [-118.192539076859, 34.0504947182343], - [-118.192523496478, 34.0486607115183], - [-118.192521531863, 34.048356706392], - [-118.192502021353, 34.0461656069898], - [-118.192496547918, 34.0455352483732], - [-118.192496126608, 34.0454656996663], - [-118.192424261386, 34.0372917733233], - [-118.192413032445, 34.035998351061], - [-118.192395627586, 34.0340065352607], - [-118.192296671869, 34.0340072335444], - [-118.191850181325, 34.0340090946414], - [-118.191855375384, 34.0344213266376], - [-118.191410428146, 34.0344231877256], - [-118.191393444597, 34.0334044682088], - [-118.192208244408, 34.03338097206], - [-118.19238987208, 34.0333753887273], - [-118.1923525363, 34.0290479623817], - [-118.192316182379, 34.0249262218504], - [-118.192280389905, 34.0208045141691], - [-118.192263406356, 34.0188993635184], - [-118.192244457293, 34.0166826055818], - [-118.192230140843, 34.0150847455347], - [-118.191448185236, 34.0150891662536], - [-118.191428394452, 34.0128641648651], - [-118.19229358436, 34.0129421180601], - [-118.194648293298, 34.0131438639143], - [-118.200520889331, 34.0136483437299], - [-118.200668970317, 34.0136616075994], - [-118.20099446947, 34.013693951536], - [-118.201222416974, 34.0137195477429], - [-118.201285018769, 34.0137274592962], - [-118.201449241583, 34.013747470869], - [-118.201773056395, 34.0137912170813], - [-118.202095748313, 34.0138396171198], - [-118.202417457474, 34.013892904041], - [-118.202737762569, 34.0139506117069], - [-118.20293062008, 34.0139878424383], - [-118.203193377301, 34.014041361586], - [-118.203510735023, 34.0141102383526], - [-118.203653061402, 34.0141435128847], - [-118.203826548541, 34.0141840012123], - [-118.204140818753, 34.0142621855144], - [-118.204453264487, 34.0143450235654], - [-118.204552219305, 34.0143727141671], - [-118.204764024981, 34.0144322830333], - [-118.205072960998, 34.014524195478], - [-118.205038432453, 34.0126305386635], - [-118.205382319222, 34.0127678290392], - [-118.206988619239, 34.0127508426482], - [-118.208292018123, 34.015014240362], - [-118.208713103412, 34.0150133096052], - [-118.210144231949, 34.0150056312332], - [-118.21201272774, 34.0149974867375], - [-118.212712992352, 34.0149942290879], - [-118.212709763807, 34.0142996491726], - [-118.21474809778, 34.0149846891999], - [-118.215618200575, 34.0149809661712], - [-118.217162600381, 34.0155000940357], - [-118.21716091604, 34.0149737524304], - [-118.21984589688, 34.0149614202679], - [-118.220110057269, 34.0149611872063], - [-118.220682312852, 34.0149576972382], - [-118.2210444462, 34.0149556026616], - [-118.221967605292, 34.0149516473146], - [-118.223244194854, 34.0149460627693], - [-118.223261179301, 34.0149879468499], - [-118.223330237289, 34.0151636267014], - [-118.223396628178, 34.0153425645704], - [-118.223407575946, 34.0153739771137], - [-118.223716933272, 34.0153674618433], - [-118.223704161026, 34.0149442012542], - [-118.224400495508, 34.0149474589057], - [-118.225614062863, 34.0149414089813], - [-118.225812113535, 34.0149421066772], - [-118.225816745249, 34.0152394832155], - [-118.227461926149, 34.0152048129809], - [-118.22748185707, 34.015166652028], - [-118.227501087305, 34.0151282579968], - [-118.227519474022, 34.0150896316316], - [-118.227537019018, 34.0150507729319], - [-118.227553863328, 34.015011681153], - [-118.227569723983, 34.0149721239774], - [-118.227584883053, 34.0149325667834], - [-118.227587268979, 34.0149260514791], - [-118.228121345263, 34.0149234915228], - [-118.229700274512, 34.0149160454599], - [-118.229749682751, 34.0149158131428], - [-118.230211753675, 34.014913020869], - [-118.230475632891, 34.0149113916703], - [-118.233562890096, 34.0148720675099], - [-118.239721824126, 34.0148001660218], - [-118.239771371604, 34.0147997006422], - [-118.239714385177, 34.0126703299981], - [-118.239644344433, 34.0100324513486], - [-118.239550582775, 34.008052126915], - [-118.239237856766, 34.0042686713011], - [-118.238937903903, 34.0007054408617], - [-118.238728904564, 33.9984484106787], - [-118.238516537441, 33.996160133256], - [-118.238250972985, 33.9929715234927], - [-118.237940071455, 33.9893975785103], - [-118.239010750985, 33.9894092164793], - [-118.241006836292, 33.9894252765014], - [-118.247892281909, 33.9894992939327], - [-118.252007127358, 33.9894999925825], - [-118.256316374522, 33.9894965008233], - [-118.256316093349, 33.9894141040534], - [-118.256316374522, 33.9885428817128], - [-118.256316654796, 33.9881830313066], - [-118.256316935969, 33.9877964111737], - [-118.25631862031, 33.9821742819976], - [-118.256305145581, 33.9796439346689], - [-118.256276090471, 33.9748374636656], - [-118.256275388887, 33.9714001133608], - [-118.256273002961, 33.9601752005832], - [-118.255910307267, 33.9601742692256], - [-118.255910869612, 33.9600368938635], - [-118.255933888043, 33.9548143696676], - [-118.255935572384, 33.9546532344932], - [-118.255941889337, 33.9530651518566], - [-118.254112133393, 33.9510364612477], - [-118.254102869068, 33.9510425157223], - [-118.253876325631, 33.9511894534039], - [-118.253546475039, 33.9511927134998], - [-118.253582127376, 33.9474246575188], - [-118.25360711132, 33.9473829733818], - [-118.253614129858, 33.9455544238539], - [-118.253645991304, 33.9437766038318], - [-118.253650623916, 33.9433781375248], - [-118.253653290116, 33.9431857739924], - [-118.249190628667, 33.9431813495329], - [-118.249176873664, 33.94381339954], - [-118.249154696056, 33.945679247129], - [-118.249152450268, 33.9465585939695], - [-118.24914978317, 33.9474654108839], - [-118.249083953727, 33.9482683572927], - [-118.249044652434, 33.9492729611282], - [-118.249110622013, 33.9492734268666], - [-118.248949346572, 33.9533741553813], - [-118.247596259175, 33.9533676353566], - [-118.247495620016, 33.9533883593467], - [-118.247460950436, 33.9542615722914], - [-118.244197680029, 33.9542753111427], - [-118.244205961598, 33.9533198990731], - [-118.243434813622, 33.9533161733423], - [-118.242973164007, 33.9533089551111], - [-118.241767456013, 33.9533070922455], - [-118.241767456013, 33.9532824092724], - [-118.240911250156, 33.9532810121228], - [-118.240911250156, 33.9533045304326], - [-118.239773617392, 33.9533035989998], - [-118.239575707755, 33.9532921893192], - [-118.239040648714, 33.9532917236027], - [-118.238975239683, 33.9533024350813], - [-118.236536875134, 33.9532994075519], - [-118.236536875134, 33.9532761217263], - [-118.233908320171, 33.9532754235241], - [-118.233939200657, 33.9481807973794], - [-118.230513811291, 33.9482208513935], - [-118.230509179577, 33.9470970031229], - [-118.23492257472, 33.9470511263246], - [-118.234913871842, 33.94603508561], - [-118.2343242123, 33.9460311263096], - [-118.230179329883, 33.9460024826698], - [-118.230176522648, 33.9452691457195], - [-118.230166556738, 33.945260063764], - [-118.23015125753, 33.9452458576967], - [-118.230136238597, 33.9452311866117], - [-118.230121500837, 33.945216514779], - [-118.23010704335, 33.9452016104359], - [-118.230092867037, 33.9451864735824], - [-118.230079111135, 33.9451711034732], - [-118.230065636406, 33.9451555001081], - [-118.230052582986, 33.9451396642321], - [-118.230039809841, 33.9451238283532], - [-118.230027317869, 33.9451075267102], - [-118.230015106171, 33.9450912250639], - [-118.230003315783, 33.9450749234146], - [-118.229991946705, 33.9450581560006], - [-118.229980857901, 33.9450413885833], - [-118.229970190407, 33.9450243886544], - [-118.229959804086, 33.9450071554686], - [-118.229949698039, 33.9449899222793], - [-118.229940153439, 33.9449726890866], - [-118.229930889113, 33.9449549901282], - [-118.22992190596, 33.9449372911662], - [-118.229913344117, 33.9449195922004], - [-118.229905202686, 33.9449016599772], - [-118.229897483463, 33.9448834952416], - [-118.229890043615, 33.9448653305021], - [-118.229883025976, 33.9448471657587], - [-118.229876288612, 33.9448287685026], - [-118.229870112694, 33.9448103704974], - [-118.229864217051, 33.9447917399793], - [-118.229858743616, 33.9447731094571], - [-118.229848496534, 33.9447356158914], - [-118.228658650092, 33.9390123564472], - [-118.228700478347, 33.938867959991], - [-118.228831014337, 33.9384282479839], - [-118.228835927223, 33.9384107809365], - [-118.230479422884, 33.9329120915192], - [-118.230843380711, 33.9316944084309], - [-118.230486861832, 33.930027869012], - [-118.230406154492, 33.9296484380412], - [-118.230250352486, 33.9289191536164], - [-118.234512156925, 33.9291234281523], - [-118.238959097856, 33.9293922224394], - [-118.239156025634, 33.9294586060192], - [-118.243476641876, 33.9294513850195], - [-118.24465427662, 33.9294436989176], - [-118.247529868286, 33.9294073628463], - [-118.254215158478, 33.9293987443006], - [-118.253542264635, 33.9285469387833], - [-118.253781721354, 33.9283582685659], - [-118.253816390934, 33.928188930494], - [-118.253826215808, 33.9273049686356], - [-118.253562476729, 33.9273049686356], - [-118.253596303689, 33.9242442222134], - [-118.253602619744, 33.9236260033078], - [-118.253606409736, 33.9231002571029], - [-118.254185682957, 33.9231009562981], - [-118.254515392514, 33.9231011888662], - [-118.258640063735, 33.9231025865112], - [-118.260721488096, 33.9231032857063], - [-118.265347250362, 33.9231051492329], - [-118.269465323457, 33.9231065468778], - [-118.269663093855, 33.9231179606042], - [-118.273240634474, 33.9231188923673], - [-118.273239931992, 33.9232863769959], - [-118.273906510679, 33.9232868428765], - [-118.277343829672, 33.9232859111152], - [-118.277344671393, 33.9230797583073], - [-118.277655011476, 33.9225707813224], - [-118.277681119213, 33.9225754401679], - [-118.277707366189, 33.9225796331285], - [-118.277733755099, 33.9225833602045], - [-118.277760142212, 33.9225868539654], - [-118.277786530224, 33.9225898825871], - [-118.277813059271, 33.9225926778938], - [-118.277839727556, 33.922594774001], - [-118.277866255705, 33.9225968708536], - [-118.277892924889, 33.9225982685068], - [-118.277919593175, 33.9225994328451], - [-118.277946402496, 33.9226001320444], - [-118.277973071681, 33.9226005979288], - [-118.27798696772, 33.9226005979288], - [-118.277991178123, 33.9226005979288], - [-118.277995388527, 33.9226008304983], - [-118.277999599829, 33.9226012963827], - [-118.278002266927, 33.9226017622671], - [-118.278006337194, 33.9226024614664], - [-118.27801040746, 33.9226033932352], - [-118.278014477727, 33.9226045575734], - [-118.278018267719, 33.9226059552265], - [-118.278022057711, 33.9226073528796], - [-118.278025847703, 33.9226089838475], - [-118.278028233629, 33.9226101481857], - [-118.278031742448, 33.922612011723], - [-118.278035251268, 33.9226141085752], - [-118.278038479813, 33.9226162046819], - [-118.278041568221, 33.9226185341034], - [-118.278044655731, 33.9226210968397], - [-118.278047462966, 33.9226236588305], - [-118.278050130064, 33.9226262215666], - [-118.278052656127, 33.9226290168721], - [-118.278054901915, 33.9226320447469], - [-118.278057007566, 33.922635073367], - [-118.278058411633, 33.9226371694732], - [-118.278060095974, 33.9226401980931], - [-118.278061780315, 33.9226434592823], - [-118.278062763072, 33.9226455553882], - [-118.278064026103, 33.922649049892], - [-118.278065007962, 33.9226523110808], - [-118.278065850581, 33.9226558048389], - [-118.278066412028, 33.9226592993422], - [-118.278066833338, 33.9226627931], - [-118.278066973475, 33.922666287603], - [-118.278066973475, 33.9226697813605], - [-118.278051814405, 33.9230238524762], - [-118.278040445327, 33.9232856778022], - [-118.278134347122, 33.9232856778022], - [-118.278398087099, 33.9231270456669], - [-118.279886483236, 33.9231265797854], - [-118.279918204545, 33.92320414865], - [-118.281409689089, 33.9232036827689], - [-118.281461201183, 33.923109108854], - [-118.281948397087, 33.9230981610081], - [-118.281971556553, 33.9232055462932], - [-118.282098584623, 33.9232053137254], - [-118.282121042505, 33.9219504534042], - [-118.282233331915, 33.9137122244323], - [-118.282252421115, 33.9061059948264], - [-118.28225564966, 33.9055200301551], - [-118.282280072158, 33.8971993575173], - [-118.283116347992, 33.8969295296245], - [-118.282787480156, 33.8889752819602], - [-118.282119218027, 33.8728578201543], - [-118.28158359664, 33.8730750486865], - [-118.281530540343, 33.867670509332], - [-118.281496993657, 33.8642430515698], - [-118.281484922994, 33.8627406978892], - [-118.285453230762, 33.8609704745138], - [-118.285437369209, 33.8608497231493], - [-118.285598645549, 33.8605312930564], - [-118.285952637466, 33.8604499373807], - [-118.285952918639, 33.8607476205109], - [-118.290667248504, 33.8586444654934], - [-118.290662336516, 33.8541587117782], - [-118.28614282788, 33.8541610430699], - [-118.285265707446, 33.8541617428304], - [-118.285287182572, 33.8522551912561], - [-118.285876983149, 33.8522549577487], - [-118.286206411534, 33.8522523936437], - [-118.286200095479, 33.8471265475021], - [-118.286200095479, 33.8463473621179], - [-118.29911155321, 33.8463720760663], - [-118.299106921496, 33.8424655510601], - [-118.299106780461, 33.8424242819116], - [-118.299103131504, 33.8395174114793], - [-118.299097517034, 33.8351274239623], - [-118.299095411383, 33.8312735526162], - [-118.299092182837, 33.8281865062592], - [-118.299086147057, 33.8230295986979], - [-118.299084743889, 33.8219586728346], - [-118.299082638238, 33.8201848057617], - [-118.29908600692, 33.8190809591998], - [-118.29908642823, 33.8189298293502], - [-118.299150994641, 33.8170381185354], - [-118.299157872143, 33.8168732240814], - [-118.299181874229, 33.8161805255309], - [-118.299230579985, 33.8147855492744], - [-118.299270161553, 33.8136503789183], - [-118.298502803569, 33.8133681584419], - [-118.298505610805, 33.8132069895137], - [-118.298508418938, 33.8130458195355], - [-118.298676010434, 33.8099144977896], - [-118.298684292003, 33.8097631181207], - [-118.298730470798, 33.8089444057319], - [-118.298854830871, 33.8066078838215], - [-118.299027897599, 33.8034049458438], - [-118.29962850491, 33.8034233743562], - [-118.299649699761, 33.8028189756024], - [-118.299060601665, 33.8028010142448], - [-118.299094007316, 33.8021819154006], - [-118.299098498892, 33.8008774543442], - [-118.299101447163, 33.8000068694865], - [-118.299109447559, 33.7976906317126], - [-118.297468477961, 33.7976852665724], - [-118.290827822361, 33.7976624048158], - [-118.286946960196, 33.7976488743856], - [-118.285887930918, 33.797646074986], - [-118.285801468072, 33.7976456084194], - [-118.285764833876, 33.7976458413294], - [-118.285611137521, 33.7976451418527], - [-118.282919280077, 33.79763604343], - [-118.283002934789, 33.7981907893993], - [-118.27530493515, 33.7985848018623], - [-118.270335426252, 33.7988390774996], - [-118.264604034602, 33.7991318430943], - [-118.26461540368, 33.8007356233314], - [-118.264616666711, 33.8010489105559], - [-118.264641511417, 33.8045556475985], - [-118.264434197318, 33.8045614788369], - [-118.260822127256, 33.8046582839051], - [-118.256685946819, 33.8047742157573], - [-118.258619991778, 33.7994383714887], - [-118.25143024096, 33.7998057845868], - [-118.247659842829, 33.7999982378686], - [-118.246033892165, 33.8000780185296], - [-118.237493721048, 33.80048531864], - [-118.237132851629, 33.8000973808649], - [-118.235418051295, 33.7982633403884], - [-118.230058618767, 33.7925479827132], - [-118.230006123018, 33.7926245041605], - [-118.229952505274, 33.7927045253643], - [-118.229900430835, 33.7927852460098], - [-118.229850180875, 33.7928666660949], - [-118.229801616154, 33.7929490200335], - [-118.229754734876, 33.7930318397381], - [-118.229709678974, 33.7931153596228], - [-118.229666307414, 33.7931995796856], - [-118.229624760332, 33.7932844991778], - [-118.229584897592, 33.7933698851758], - [-118.229546859329, 33.7934559705994], - [-118.229510646443, 33.7935425232721], - [-118.229476257138, 33.7936295416992], - [-118.229458010558, 33.7936778339691], - [-118.228349012492, 33.7966676792082], - [-118.228206825352, 33.7973936620765], - [-118.227193133148, 33.8001048457166], - [-118.224629425768, 33.8042570685523], - [-118.224579877392, 33.8043405774842], - [-118.22453313715, 33.804423386167], - [-118.224487941111, 33.8045066620452], - [-118.224444569551, 33.8045908701535], - [-118.224403021571, 33.8046753118162], - [-118.22436315883, 33.8047606864513], - [-118.224325121466, 33.8048465275286], - [-118.224307996882, 33.8048866488722], - [-118.224301820964, 33.8048847827636], - [-118.22426602849, 33.8049701571897], - [-118.224231078635, 33.805058097798], - [-118.224214936808, 33.8051007845621], - [-118.224198374569, 33.8051458046799], - [-118.224167494981, 33.8052339779972], - [-118.224138439872, 33.8053223841127], - [-118.224111350276, 33.8054112574076], - [-118.224086085159, 33.8055003634989], - [-118.224062785555, 33.8055897023859], - [-118.224041309532, 33.805679741337], - [-118.224021658885, 33.8057697801933], - [-118.224003973752, 33.8058600525891], - [-118.223988112199, 33.8059507906645], - [-118.223974076023, 33.8060417622774], - [-118.223962145497, 33.8061327337935], - [-118.223951899313, 33.8062239388461], - [-118.223943757882, 33.8063153766879], - [-118.223937441827, 33.806406814432], - [-118.223933231423, 33.8064975519277], - [-118.223931687219, 33.8065386055334], - [-118.223930002878, 33.806630276671], - [-118.223930283152, 33.8067219469639], - [-118.223932529839, 33.8068136179051], - [-118.223936600105, 33.8069050551167], - [-118.223942634988, 33.8069964922307], - [-118.223950496145, 33.8070879292469], - [-118.223960321019, 33.8071791325349], - [-118.223972111407, 33.8072701028419], - [-118.223985726273, 33.8073610730521], - [-118.224001306654, 33.8074518102822], - [-118.224018711512, 33.8075423137866], - [-118.224038080986, 33.807632350683], - [-118.224059275837, 33.8077223874847], - [-118.224082295166, 33.8078119576805], - [-118.224107280009, 33.8079010612718], - [-118.224134089331, 33.807989931142], - [-118.22416272313, 33.8080783351566], - [-118.224193181408, 33.8081662718241], - [-118.224225604302, 33.8082539755201], - [-118.224259712435, 33.808340979737], - [-118.224293960705, 33.8084233180404], - [-118.225398888505, 33.8110133304695], - [-118.227538563222, 33.8215687330834], - [-118.22789859092, 33.8233115537095], - [-118.228078675287, 33.8241996254711], - [-118.228515340956, 33.8243801305258], - [-118.228490215976, 33.8244482279411], - [-118.226623124252, 33.829489618136], - [-118.226048061435, 33.8280218668744], - [-118.224687956399, 33.8243859611593], - [-118.224587878686, 33.8243861939965], - [-118.224587737651, 33.8243092345291], - [-118.224581983043, 33.8215731638538], - [-118.224516013464, 33.8215731638538], - [-118.222768790236, 33.8129396950392], - [-118.222774684981, 33.812733742071], - [-118.222453958577, 33.8124566499152], - [-118.222448624381, 33.8046298256688], - [-118.222448344106, 33.8044180214489], - [-118.222448203969, 33.8041569973125], - [-118.226809243501, 33.7921525434196], - [-118.226977677617, 33.7908651938467], - [-118.227243383108, 33.7909568810259], - [-118.227625307241, 33.7899571784831], - [-118.227379814742, 33.7896951770255], - [-118.222844445451, 33.7848518624779], - [-118.220858466191, 33.7827305074017], - [-118.220680908785, 33.782542214394], - [-118.220950683644, 33.7823394555694], - [-118.221846051556, 33.781666076929], - [-118.226011568275, 33.7785333733585], - [-118.226855142022, 33.7778958987186], - [-118.230880577252, 33.7748524282238], - [-118.232653205734, 33.7735293495313], - [-118.23595549716, 33.7710609481034], - [-118.239872151799, 33.768108247462], - [-118.239964229115, 33.7680389380184], - [-118.24020340556, 33.7678585478553], - [-118.240491568035, 33.7676412848956], - [-118.241908800533, 33.7665727000418], - [-118.242159487091, 33.7663839047807], - [-118.245461918654, 33.7638938269841], - [-118.241165164361, 33.7585453997651], - [-118.240779590373, 33.758300573123], - [-118.248978401682, 33.7559066538704], - [-118.248617812537, 33.7550472818013], - [-118.247713321335, 33.7528922782222], - [-118.243650269153, 33.7432100835843], - [-118.237235455404, 33.7279184866084], - [-118.231815526731, 33.7150069161401], - [-118.251486245528, 33.708511170706], - [-118.251493403304, 33.7084752077014], - [-118.25152133552, 33.7084576928721], - [-118.25155249628, 33.7084492859321], - [-118.251620010064, 33.7084481186751], - [-118.251718543573, 33.7084537233019], - [-118.251978073146, 33.7083717552242], - [-118.253633499332, 33.7078374453103], - [-118.255586212181, 33.7072169601134], - [-118.256259528232, 33.706998142344], - [-118.257711289275, 33.7065187041484], - [-118.259368259664, 33.7059850829313], - [-118.265391604679, 33.7040273553374], - [-118.265656046241, 33.7039393110376], - [-118.265812128522, 33.7038937714141], - [-118.266061691287, 33.7038258113073], - [-118.26623419567, 33.7037826070118], - [-118.266345923633, 33.7037578518942], - [-118.266620190968, 33.7037092757937], - [-118.266750025374, 33.7036910597489], - [-118.266912704882, 33.7036749451786], - [-118.267093490833, 33.7036628011443], - [-118.267241431682, 33.7036569630393], - [-118.26738937343, 33.7036555618044] - ], - [ - [-118.296866746858, 33.7378013037589], - [-118.296854536059, 33.7402386821332], - [-118.29685495647, 33.7404062916373], - [-118.296840499882, 33.7434056990105], - [-118.298022626202, 33.7434094339069], - [-118.298019397657, 33.7441459059861], - [-118.299367993477, 33.7441501077083], - [-118.299436490018, 33.7441503407638], - [-118.299515935225, 33.7441505745663], - [-118.299519303907, 33.7434141025272], - [-118.301679470993, 33.7434208723996], - [-118.301680173475, 33.7432560699633], - [-118.30168733215, 33.741607561312], - [-118.303002802594, 33.7416108294148], - [-118.303003644316, 33.7414460235007], - [-118.303005749967, 33.7407039257183], - [-118.302183370172, 33.7407020582112], - [-118.302187721611, 33.7394113680279], - [-118.30218870347, 33.7392741040807], - [-118.30241805504, 33.7392745709653], - [-118.304159944071, 33.7397862749006], - [-118.304157136836, 33.740591408344], - [-118.304156856562, 33.740706493914], - [-118.304156716424, 33.7408442224451], - [-118.304154891048, 33.7414075061186], - [-118.304154189464, 33.7416136306458], - [-118.304384382755, 33.7416140975176], - [-118.308561970136, 33.7416257693121], - [-118.30856618054, 33.7416260023744], - [-118.308792163428, 33.7416264692462], - [-118.309131417585, 33.7416274029897], - [-118.309136470608, 33.7407781590919], - [-118.309136190334, 33.7406406642667], - [-118.309146997965, 33.7389099333606], - [-118.309151910851, 33.7387451222573], - [-118.309154858224, 33.7380305477012], - [-118.309155559808, 33.7378381882634], - [-118.301275790562, 33.7378150774619], - [-118.300576648845, 33.7378132098919], - [-118.296866746858, 33.7378013037589] - ], - [ - [-118.297046691088, 33.8472890513839], - [-118.297056797135, 33.8562041626354], - [-118.297057077409, 33.8568203064299], - [-118.29705876175, 33.8581840586943], - [-118.2980539271, 33.8581831262216], - [-118.299125308214, 33.8581821937488], - [-118.299124746767, 33.856005074859], - [-118.299112816241, 33.8481332762046], - [-118.299112676104, 33.8472939478632], - [-118.297046691088, 33.8472890513839] - ], - [ - [-118.451313251936, 33.9642358229124], - [-118.432271493975, 33.9750209092912], - [-118.432392907574, 33.975196206954], - [-118.432449332553, 33.9752800142957], - [-118.432507442772, 33.9753626571932], - [-118.432567236434, 33.9754446019901], - [-118.432628855473, 33.9755256155184], - [-118.432692017817, 33.9756059309506], - [-118.432756865401, 33.9756850819496], - [-118.432823537463, 33.9757633016881], - [-118.432891612694, 33.9758405901685], - [-118.432961512402, 33.9759169473934], - [-118.433032816178, 33.9759921401967], - [-118.433105804295, 33.9760664024948], - [-118.433180196479, 33.9761395003771], - [-118.433256272105, 33.9762116670146], - [-118.433333751798, 33.9762826692417], - [-118.433412775695, 33.9763522753838], - [-118.433493202761, 33.9764209495443], - [-118.433575033894, 33.976488460048], - [-118.433658268195, 33.9765548068978], - [-118.433742906562, 33.9766197561845], - [-118.433828807961, 33.976683541823], - [-118.433916113427, 33.9767459306494], - [-118.434004822062, 33.9768073882545], - [-118.434043561908, 33.9768334611645], - [-118.435941112809, 33.9781021779258], - [-118.435782783842, 33.9781722481586], - [-118.435983641749, 33.9783065675893], - [-118.436308579455, 33.9785274851661], - [-118.436586635884, 33.9787223291012], - [-118.436853463371, 33.9789146122931], - [-118.437194964214, 33.9791683509485], - [-118.437531129963, 33.9794272101541], - [-118.437852277677, 33.979683042726], - [-118.438027590193, 33.9798273699237], - [-118.438149844615, 33.9799279332457], - [-118.438470010471, 33.9802005240574], - [-118.43878442082, 33.9804775372603], - [-118.439383906135, 33.981038312017], - [-118.439600203388, 33.9812459535653], - [-118.439756004495, 33.9813998217709], - [-118.44004500959, 33.9816954535067], - [-118.440327838768, 33.9819952742486], - [-118.440604350992, 33.9822990515456], - [-118.440874407024, 33.9826067846106], - [-118.441138006865, 33.9829184734005], - [-118.441395008579, 33.9832341186167], - [-118.441499718904, 33.9833674996905], - [-118.441712928646, 33.9834080026893], - [-118.442946006511, 33.9836417092709], - [-118.445924623262, 33.984205954385], - [-118.447759713403, 33.9845534843083], - [-118.448314141918, 33.9846659138025], - [-118.448634588048, 33.9847376076575], - [-118.448953349836, 33.9848139568911], - [-118.449270567421, 33.9848949614908], - [-118.452844318048, 33.9858362851436], - [-118.45300166336, 33.9858774849006], - [-118.454872124665, 33.9863697903594], - [-118.463295654932, 33.9810057224903], - [-118.462243081845, 33.9798024614879], - [-118.462234379865, 33.9797924520758], - [-118.462217115144, 33.9797719669316], - [-118.462200411869, 33.9797512486243], - [-118.462183989767, 33.9797305310568], - [-118.462168129113, 33.9797093471675], - [-118.46215268887, 33.9796879308594], - [-118.462137669936, 33.9796662821323], - [-118.462123072313, 33.9796446326548], - [-118.462109036137, 33.9796225183443], - [-118.46209542127, 33.9796004032832], - [-118.462094438513, 33.9795987741521], - [-118.462084473502, 33.9795801512515], - [-118.462071700357, 33.9795575706067], - [-118.462059488659, 33.9795347575419], - [-118.462047698271, 33.979511944471], - [-118.462036329193, 33.9794888989799], - [-118.462025521561, 33.9794656203235], - [-118.462015274479, 33.9794421085016], - [-118.462005309468, 33.9794185974182], - [-118.461996045142, 33.979394853169], - [-118.461987203025, 33.9793711089131], - [-118.461978780421, 33.9793471314912], - [-118.461971061197, 33.9793231548075], - [-118.46196362135, 33.9792989449575], - [-118.461956884884, 33.9792745019411], - [-118.461950567931, 33.9792502920772], - [-118.461944673186, 33.9792258497917], - [-118.461939479127, 33.9792011743394], - [-118.461934707276, 33.9791764988799], - [-118.461930355837, 33.9791518234132], - [-118.46192670688, 33.9791271479394], - [-118.461923478335, 33.9791022392984], - [-118.4619206711, 33.9790775638102], - [-118.461918565449, 33.9790526558995], - [-118.461916881108, 33.9790277472367], - [-118.461915758214, 33.9790028393114], - [-118.461904107963, 33.9786699520155], - [-118.461903126104, 33.9786490010232], - [-118.461901722037, 33.9786278168644], - [-118.461899897559, 33.9786068658617], - [-118.461897511633, 33.9785859148539], - [-118.461894844535, 33.9785649638409], - [-118.46189161599, 33.9785440128227], - [-118.461888107171, 33.9785230617994], - [-118.461884036904, 33.9785021107709], - [-118.461879545328, 33.978481392899], - [-118.461874632442, 33.9784606742771], - [-118.461869298245, 33.9784401888121], - [-118.461863543638, 33.9784194709251], - [-118.46185736772, 33.9783989854501], - [-118.461850630355, 33.9783787323872], - [-118.461843612716, 33.9783584800645], - [-118.461836172869, 33.9783382269921], - [-118.461828172473, 33.978318207077], - [-118.461819890905, 33.9782984203194], - [-118.461811890509, 33.9782802627137], - [-118.461433475195, 33.9783619715364], - [-118.460997230836, 33.9778607743841], - [-118.460634255765, 33.9774436119059], - [-118.459642599235, 33.9762724262474], - [-118.458654733595, 33.9751072779621], - [-118.45746937873, 33.9735333121618], - [-118.455750367992, 33.9712276015818], - [-118.45508701785, 33.9703343085466], - [-118.454686707, 33.9697860350947], - [-118.454775555772, 33.9697464568588], - [-118.454964482256, 33.9696656710101], - [-118.453406466685, 33.9673489126598], - [-118.453382184324, 33.9673132916], - [-118.453204907193, 33.9670495059082], - [-118.453079424226, 33.9668630167838], - [-118.451725213935, 33.9648483915701], - [-118.451612503215, 33.964680756242], - [-118.451313251936, 33.9642358229124] - ], - [ - [-118.380410770764, 33.9762768497312], - [-118.380380312486, 33.9762770821541], - [-118.380349713172, 33.9762777809125], - [-118.380319254894, 33.9762789445167], - [-118.380288795718, 33.9762803412886], - [-118.38025833744, 33.9762824368189], - [-118.380228019299, 33.976284764772], - [-118.380197701158, 33.9762875583156], - [-118.380167383017, 33.9762908174497], - [-118.380137205014, 33.9762945421743], - [-118.380107168046, 33.9762987324892], - [-118.380077130179, 33.9763033883944], - [-118.380047233348, 33.9763085098899], - [-118.380017476654, 33.9763138638078], - [-118.379987860996, 33.9763199164834], - [-118.37995824444, 33.9763262023263], - [-118.379941681302, 33.9763299270493], - [-118.379667695141, 33.9763934797378], - [-118.379417429892, 33.9764514460475], - [-118.377612658791, 33.9768693114026], - [-118.37759188525, 33.9768742004428], - [-118.377537846196, 33.9768858401266], - [-118.377483525969, 33.9768967810554], - [-118.377429065605, 33.9769070239745], - [-118.377374324068, 33.9769163357181], - [-118.377319443292, 33.9769249494524], - [-118.377264421481, 33.9769328644329], - [-118.377209258635, 33.9769398482385], - [-118.377153956549, 33.9769459008696], - [-118.377098373291, 33.9769512547473], - [-118.37704293017, 33.9769559106168], - [-118.376987206775, 33.9769596353122], - [-118.376960397453, 33.9769612652389], - [-118.371821612718, 33.9772354954847], - [-118.371821332444, 33.977515544272], - [-118.371819648103, 33.9780851846026], - [-118.371817402314, 33.9793427089121], - [-118.371816981005, 33.9797694059158], - [-118.371814454942, 33.9799351492181], - [-118.37181936693, 33.9819510461], - [-118.371813472185, 33.9822220013433], - [-118.371815296663, 33.9830069286678], - [-118.370868837968, 33.9830104206936], - [-118.370194820332, 33.9830122829087], - [-118.369373703569, 33.9830146106775], - [-118.369195584716, 33.9830153086357], - [-118.368547253508, 33.9830171708507], - [-118.368532094437, 33.983017404], - [-118.368360291639, 33.9830178695537], - [-118.367657500965, 33.9830199641731], - [-118.367473346331, 33.9830206628761], - [-118.366258093737, 33.9830243873058], - [-118.366226512565, 33.9830243873058], - [-118.365993090728, 33.9830232230491], - [-118.36597259836, 33.9830229906447], - [-118.365591515948, 33.9830239217521], - [-118.365413397096, 33.9830269474787], - [-118.362494012559, 33.9830360261475], - [-118.362315332259, 33.9830364917011], - [-118.361388944623, 33.9830392850229], - [-118.36119047264, 33.9830395174273], - [-118.360707347901, 33.9830409140881], - [-118.360661168207, 33.9830409140881], - [-118.360263944867, 33.9830427763025], - [-118.360085264567, 33.9830425438982], - [-118.359635264305, 33.9830437074097], - [-118.358484438883, 33.9830476649876], - [-118.358413415381, 33.983047897392], - [-118.358349831727, 33.9830481305412], - [-118.357042641953, 33.9830523205232], - [-118.357040677338, 33.9830523205232], - [-118.356774831709, 33.9830527860768], - [-118.356105867995, 33.9830546482909], - [-118.355918484817, 33.9830560449515], - [-118.355852515237, 33.9830553462488], - [-118.355505119649, 33.9830565105051], - [-118.355174848645, 33.9830572084629], - [-118.352695357425, 33.9830639593611], - [-118.35269339281, 33.9878829992561], - [-118.35269339281, 33.9879095342959], - [-118.352693252672, 33.9880477956852], - [-118.3526929715, 33.9890100328611], - [-118.352692831363, 33.9893396210287], - [-118.352082538416, 33.9893447417387], - [-118.350650286985, 33.9893561473279], - [-118.35022681577, 33.9893594059609], - [-118.349950723958, 33.9893617335559], - [-118.347084396617, 33.9893845439832], - [-118.347021514547, 33.9893854750209], - [-118.347005652994, 33.989385241889], - [-118.34699021365, 33.989385241889], - [-118.344038826631, 33.9894087509606], - [-118.34249947895, 33.9894199234093], - [-118.342496812751, 33.9889527737695], - [-118.342504813147, 33.9888152125079], - [-118.342505936041, 33.9885128550711], - [-118.342486285394, 33.9884718889812], - [-118.342486285394, 33.9884581563986], - [-118.33997226563, 33.9884760786958], - [-118.339837939647, 33.9884770097435], - [-118.33821732228, 33.9884886478385], - [-118.33768872033, 33.9884923720286], - [-118.337278162397, 33.9884953983054], - [-118.33727760095, 33.9885500969539], - [-118.337276618193, 33.9888524542582], - [-118.33727717964, 33.9889900154595], - [-118.337784447501, 33.9889860589031], - [-118.337786552254, 33.9894753201128], - [-118.337265810562, 33.9894792766464], - [-118.337288829891, 33.9950059638108], - [-118.337294444361, 33.9963125787619], - [-118.333401650773, 33.9963302670345], - [-118.333203600999, 33.9963311979961], - [-118.331641092954, 33.9963381802079], - [-118.331635619519, 33.9975910120791], - [-118.331568526147, 33.9982519824478], - [-118.331509995516, 33.9984169918741], - [-118.331496801061, 34.0014962483449], - [-118.331719695541, 34.0021937264287], - [-118.331749031823, 34.0022733181158], - [-118.331770787223, 34.0023289387904], - [-118.331793666415, 34.0023840947257], - [-118.331817808638, 34.0024392498804], - [-118.331843073755, 34.0024937072012], - [-118.33186932163, 34.002547932136], - [-118.331896832535, 34.0026019239406], - [-118.331925466335, 34.0026552171693], - [-118.331955223029, 34.0027082780141], - [-118.331986102617, 34.00276087338], - [-118.332017964961, 34.0028130032682], - [-118.332051090337, 34.0028646676793], - [-118.332085197572, 34.0029158666143], - [-118.332120288462, 34.0029663677241], - [-118.332156502246, 34.003016402615], - [-118.33219453961, 34.0030676014585], - [-118.332232858147, 34.0031164730531], - [-118.332283669554, 34.0031783770325], - [-118.332324233879, 34.0032260853282], - [-118.332365921997, 34.0032733274095], - [-118.332385993055, 34.0032952032166], - [-118.332388379879, 34.0032984613151], - [-118.332431751439, 34.0033445401226], - [-118.332475964721, 34.0033896880209], - [-118.332521301795, 34.0034343704533], - [-118.332598781488, 34.0035088411218], - [-118.33264636435, 34.0035518948195], - [-118.332694788934, 34.0035942499616], - [-118.332744056138, 34.003635906549], - [-118.332794165062, 34.0036768653276], - [-118.332845256744, 34.0037171262982], - [-118.332886242379, 34.0037485431425], - [-118.332906314336, 34.0037636703187], - [-118.332959511669, 34.0038018363964], - [-118.33296947668, 34.0038095157816], - [-118.33302323546, 34.0038472163991], - [-118.333077695824, 34.0038839861211], - [-118.33320795154, 34.0039735835341], - [-118.333219320618, 34.0039817283426], - [-118.333273780982, 34.0040212906384], - [-118.333327259488, 34.0040615514457], - [-118.333380035511, 34.0041027423658], - [-118.333431828777, 34.0041446317951], - [-118.333482780322, 34.0041872197328], - [-118.333532889246, 34.0042305054331], - [-118.333582015414, 34.0042747219859], - [-118.333630159724, 34.0043194039527], - [-118.333677461413, 34.0043650167696], - [-118.333723781244, 34.0044110949985], - [-118.333769118318, 34.0044578709844], - [-118.333813472636, 34.004505345471], - [-118.333856704059, 34.004553518457], - [-118.333880846282, 34.0045807460716], - [-118.333922533501, 34.0046298498855], - [-118.333963237963, 34.0046796514512], - [-118.334002960567, 34.0047299184226], - [-118.334041560276, 34.0047806507987], - [-118.334074545515, 34.0048257979335], - [-118.334231189243, 34.0050445517524], - [-118.334332108677, 34.0051865087164], - [-118.334916575058, 34.006003803867], - [-118.334948718576, 34.0060494157792], - [-118.334996581712, 34.0061194622243], - [-118.335043041681, 34.0061902071242], - [-118.335087957445, 34.0062614173916], - [-118.335131329005, 34.0063335584512], - [-118.335173156361, 34.0064061648751], - [-118.335213580549, 34.0064792366622], - [-118.335252461431, 34.006553239236], - [-118.335289657074, 34.0066274748298], - [-118.335325449548, 34.0067024081223], - [-118.335359556783, 34.0067778067723], - [-118.335392120712, 34.0068536707785], - [-118.3354230003, 34.0069300001397], - [-118.335440546194, 34.0069758438933], - [-118.335452336582, 34.0070067948545], - [-118.335480127762, 34.0070838218385], - [-118.335506235499, 34.0071615472572], - [-118.335530657997, 34.0072395056875], - [-118.335553537189, 34.0073176963843], - [-118.335574732039, 34.0073963524293], - [-118.335594382686, 34.0074752407389], - [-118.335612207956, 34.0075543620572], - [-118.335628489921, 34.0076339487207], - [-118.335642947407, 34.0077135353096], - [-118.335655860689, 34.0077933549052], - [-118.33566708963, 34.0078734067623], - [-118.33567663423, 34.0079534585438], - [-118.335684494489, 34.008033742586], - [-118.335690670407, 34.0081142596326], - [-118.33599792298, 34.0079911573218], - [-118.336044522187, 34.0079720752264], - [-118.336382233038, 34.0078371041853], - [-118.33640862105, 34.007826166495], - [-118.336434587751, 34.0078145310489], - [-118.336460274179, 34.0078026632646], - [-118.336485679433, 34.0077905623972], - [-118.336510804413, 34.0077777630287], - [-118.336535789256, 34.0077649644029], - [-118.336560351891, 34.0077514672755], - [-118.336584775287, 34.007737970146], - [-118.336608776475, 34.0077237745144], - [-118.336632498286, 34.0077095796252], - [-118.336656079063, 34.0076946862334], - [-118.336679098392, 34.007679792839], - [-118.336701976685, 34.0076642009416], - [-118.336724434568, 34.007648609786], - [-118.336746612175, 34.0076325524641], - [-118.33676850861, 34.0076162628019], - [-118.336789983736, 34.0075995077176], - [-118.336811038449, 34.0075825202927], - [-118.33683181199, 34.0075650667005], - [-118.336852164221, 34.0075476138494], - [-118.336872236178, 34.0075296948306], - [-118.336891886825, 34.0075115434705], - [-118.336911116162, 34.0074931597692], - [-118.336930065224, 34.0074743098994], - [-118.33694241706, 34.0074617435654], - [-118.33696066364, 34.0074424290135], - [-118.336978349671, 34.0074231137124], - [-118.33699575453, 34.0074033337315], - [-118.337012738078, 34.0073833206635], - [-118.337029300317, 34.0073630745083], - [-118.337045442144, 34.007342829093], - [-118.337058916874, 34.0073251431136], - [-118.33734609749, 34.0069434973976], - [-118.337347781831, 34.0069421011296], - [-118.337349326035, 34.0069407048617], - [-118.337350869341, 34.0069393085936], - [-118.337352413545, 34.0069379123256], - [-118.337353816714, 34.0069362829739], - [-118.337355080643, 34.0069348867058], - [-118.337595238946, 34.0070323930774], - [-118.337597064323, 34.0070300659665], - [-118.337601414864, 34.0070242478168], - [-118.337624715366, 34.0069970209856], - [-118.337657279295, 34.0069597871888], - [-118.337690684945, 34.0069230187985], - [-118.337724933215, 34.0068869481544], - [-118.337759742933, 34.0068511105789], - [-118.33779539527, 34.0068159714956], - [-118.337831749191, 34.0067812970766], - [-118.337868944833, 34.0067470888119], - [-118.337906701923, 34.0067135782969], - [-118.337945161495, 34.0066805331928], - [-118.337984462789, 34.00664818584], - [-118.33802432553, 34.0066160715593], - [-118.338064889855, 34.0065848881158], - [-118.338106015627, 34.0065541700855], - [-118.338147843881, 34.0065239174692], - [-118.338190232685, 34.0064945956922], - [-118.338233323971, 34.0064657393305], - [-118.338276976703, 34.0064373483843], - [-118.338321189985, 34.0064098882798], - [-118.338365965612, 34.0063828935922], - [-118.338411442823, 34.0063565974071], - [-118.338457341345, 34.0063307658952], - [-118.338503801313, 34.0063058659724], - [-118.338550681692, 34.0062816638094], - [-118.338598124418, 34.0062579270659], - [-118.338646127691, 34.0062351211689], - [-118.338694552275, 34.0062127806924], - [-118.338743399067, 34.0061913710635], - [-118.338792805509, 34.0061706591973], - [-118.338842494023, 34.0061504134976], - [-118.338892602947, 34.0061310979027], - [-118.338934571339, 34.0061157388069], - [-118.338939062916, 34.0061141101841], - [-118.339219506168, 34.0060128793363], - [-118.33956156756, 34.005884188838], - [-118.339615045167, 34.0058613828406], - [-118.339667961327, 34.0058381114083], - [-118.33972031604, 34.0058136760252], - [-118.339772250342, 34.0057887759508], - [-118.339823482161, 34.0057629442662], - [-118.339874293568, 34.0057364148022], - [-118.33992454263, 34.0057091875581], - [-118.339974231143, 34.0056812617886], - [-118.340023357311, 34.005652637493], - [-118.340071781895, 34.0056233154152], - [-118.34011964593, 34.0055932955546], - [-118.340166807482, 34.0055623440783], - [-118.340191370117, 34.005546054019], - [-118.340677162853, 34.005217459585], - [-118.340713656912, 34.005193024768], - [-118.340757309645, 34.0051648657402], - [-118.340801663063, 34.0051374052243], - [-118.340846578828, 34.0051104101322], - [-118.340891915902, 34.0050841135531], - [-118.34093795456, 34.005058514743], - [-118.340984414528, 34.0050336137025], - [-118.341031435943, 34.0050094111769], - [-118.34107887777, 34.0049859071669], - [-118.341126881942, 34.0049631009282], - [-118.341175446663, 34.0049409924616], - [-118.341224292557, 34.0049195825121], - [-118.341273699897, 34.0048988710804], - [-118.341323528548, 34.0048788574222], - [-118.34137377761, 34.0048595415381], - [-118.341424307845, 34.0048409241732], - [-118.341475259389, 34.0048232376727], - [-118.341526632244, 34.0048062496925], - [-118.34157842551, 34.0047899594882], - [-118.341630358913, 34.0047743670603], - [-118.341682714524, 34.0047594731537], - [-118.341735349512, 34.004745510114], - [-118.341788406707, 34.0047322455964], - [-118.34184160404, 34.0047196788567], - [-118.34189494151, 34.004708042985], - [-118.341948699392, 34.0046971048917], - [-118.342002598309, 34.004686865322], - [-118.342056778399, 34.0046775566211], - [-118.342111098626, 34.0046689464442], - [-118.34216555899, 34.0046612663921], - [-118.342220159491, 34.0046542848645], - [-118.342274900129, 34.0046480018614], - [-118.342329921941, 34.0046426489838], - [-118.342384943752, 34.004637994631], - [-118.342439965563, 34.0046342711487], - [-118.342495128409, 34.0046312461915], - [-118.342550290358, 34.0046289190148], - [-118.342605592443, 34.0046275227088], - [-118.342660895427, 34.0046270572735], - [-118.345698745291, 34.004615188299], - [-118.345799384451, 34.004615421389], - [-118.345912235309, 34.0046172831306], - [-118.346024946029, 34.0046207735237], - [-118.346137655851, 34.0046258933127], - [-118.346250226434, 34.0046326424974], - [-118.346362655982, 34.0046410203327], - [-118.346474805255, 34.004651259908], - [-118.346586813493, 34.0046628957875], - [-118.346698541457, 34.0046761603159], - [-118.346809989146, 34.004691054237], - [-118.346921155662, 34.0047075775501], - [-118.347031900869, 34.0047257295094], - [-118.348911765738, 34.0050475766949], - [-118.348970437404, 34.0050573507882], - [-118.349029389345, 34.0050659616703], - [-118.349088621559, 34.00507387403], - [-118.349147854673, 34.0050810878672], - [-118.349207227923, 34.0050871384941], - [-118.349266881448, 34.0050924913438], - [-118.349326534973, 34.0050969125829], - [-118.349386188497, 34.005100636045], - [-118.349445983058, 34.0051031962974], - [-118.349505917755, 34.0051050580283], - [-118.349565711417, 34.0051059888938], - [-118.349625646114, 34.0051062212378], - [-118.349685440675, 34.0051055234611], - [-118.349745374474, 34.0051038940742], - [-118.349805169034, 34.0051013345665], - [-118.349864962696, 34.0050978434484], - [-118.349924616221, 34.0050936545534], - [-118.349984129608, 34.0050885347926], - [-118.350043642996, 34.0050827172546], - [-118.350102876109, 34.0050757357615], - [-118.350162108324, 34.0050680557462], - [-118.350221060264, 34.0050594456089], - [-118.350279872068, 34.0050501369489], - [-118.350338543734, 34.0050398974217], - [-118.350396934227, 34.005028727027], - [-118.350455043548, 34.0050168581086], - [-118.350512872595, 34.0050040590667], - [-118.350570561504, 34.0049903284115], - [-118.350627829103, 34.0049758999763], - [-118.35068481553, 34.0049605406716], - [-118.35074152258, 34.004944483586], - [-118.350797807423, 34.0049274948854], - [-118.350853811991, 34.0049095760584], - [-118.350909394351, 34.0048909587045], - [-118.350964557198, 34.0048716428231], - [-118.351019297836, 34.0048513968138], - [-118.351073618063, 34.004830452276], - [-118.35112751698, 34.0048088092091], - [-118.351180994587, 34.0047862360125], - [-118.351233911646, 34.0047629642857], - [-118.351286266359, 34.004738761683], - [-118.351338199762, 34.0047138605487], - [-118.351389712754, 34.004688261627], - [-118.351440523263, 34.0046619649172], - [-118.351490773223, 34.004634969674], - [-118.351540460838, 34.0046070435512], - [-118.351589587904, 34.0045786519837], - [-118.351638152625, 34.0045493295352], - [-118.35168601666, 34.0045195416405], - [-118.35173331835, 34.0044888228633], - [-118.351779918455, 34.0044574055482], - [-118.351825957113, 34.0044255235297], - [-118.351871153152, 34.0043929429719], - [-118.351915788642, 34.0043596638741], - [-118.351959721649, 34.0043256869801], - [-118.352002812935, 34.004291012289], - [-118.352045342774, 34.0042558714018], - [-118.352087029993, 34.004220032716], - [-118.352127875491, 34.0041837285772], - [-118.352155386396, 34.004158828025], - [-118.352193143486, 34.0041229892982], - [-118.352233146364, 34.004084124835], - [-118.353991598532, 34.0023359205092], - [-118.354008723117, 34.0023189320315], - [-118.354082272681, 34.0022477184585], - [-118.35415736555, 34.0021774357233], - [-118.354233863385, 34.0021085492774], - [-118.354311763489, 34.0020403605776], - [-118.354391208697, 34.0019735689175], - [-118.354471916935, 34.0019079404581], - [-118.354554028342, 34.0018434752021], - [-118.354637543816, 34.0017801738969], - [-118.35472232232, 34.0017182688984], - [-118.354808363857, 34.0016572947581], - [-118.354895668424, 34.0015977169298], - [-118.354984236921, 34.0015395354166], - [-118.355476064539, 34.0012216309244], - [-118.355477748881, 34.0012200022077], - [-118.355480134806, 34.0012179072923], - [-118.355482661767, 34.0012158131215], - [-118.355485187829, 34.0012139513053], - [-118.355531086351, 34.0011818349698], - [-118.355576142252, 34.0011490200682], - [-118.355620637605, 34.0011157404441], - [-118.355664289439, 34.0010815291526], - [-118.355707380725, 34.0010468531372], - [-118.355749630289, 34.0010114785524], - [-118.355791176473, 34.0009756384976], - [-118.355831881834, 34.000939099872], - [-118.355871884711, 34.0009018634194], - [-118.355911186005, 34.0008643945948], - [-118.35594950544, 34.0008259940967], - [-118.355987121494, 34.0007871288695], - [-118.356023896726, 34.0007477974232], - [-118.356059969474, 34.0007080012466], - [-118.356095060364, 34.0006675064935], - [-118.356129308634, 34.00062677862], - [-118.356162714285, 34.0005853529131], - [-118.356195278214, 34.0005434617283], - [-118.356226860284, 34.0005011050649], - [-118.356257598837, 34.0004582829222], - [-118.356287496566, 34.0004149952994], - [-118.35631641064, 34.0003714752977], - [-118.356344482993, 34.0003272567129], - [-118.356371572588, 34.0002828050032], - [-118.356397820462, 34.0002378885555], - [-118.356422945443, 34.0001927389819], - [-118.356447227803, 34.0001471239244], - [-118.356470527407, 34.0001012757399], - [-118.356492985289, 34.0000549628151], - [-118.356514320277, 34.0000084167623], - [-118.356534673406, 33.9999614052231], - [-118.35655404288, 33.9999141605549], - [-118.356572570633, 33.9998666835021], - [-118.356589975492, 33.9998189740643], - [-118.356606397593, 33.9997710314963], - [-118.356621697699, 33.9997228557978], - [-118.356636154287, 33.9996744477131], - [-118.356649488879, 33.9996258072418], - [-118.356661840714, 33.9995771659979], - [-118.356673209793, 33.9995280600077], - [-118.356683456875, 33.9994789532443], - [-118.356692720302, 33.9994296140929], - [-118.356698334773, 33.9993972647232], - [-118.356878137967, 33.9982969006903], - [-118.356902561363, 33.9981491134537], - [-118.356904525978, 33.9981388730945], - [-118.356909017555, 33.9981172290673], - [-118.356913930441, 33.9980953519266], - [-118.356919404774, 33.9980739402516], - [-118.356925300418, 33.998052295463], - [-118.356931616472, 33.9980308837771], - [-118.356938353837, 33.9980094720858], - [-118.356945511613, 33.9979882934974], - [-118.356953091598, 33.9979671141589], - [-118.356961232131, 33.9979461679235], - [-118.356969794872, 33.9979252216829], - [-118.356978636989, 33.9979045085457], - [-118.356988041452, 33.9978840277671], - [-118.356997867225, 33.997863313875], - [-118.357008113409, 33.9978430661952], - [-118.357018780903, 33.9978228177658], - [-118.357029869707, 33.9978028024402], - [-118.357041378922, 33.9977830202188], - [-118.357053309447, 33.9977632372481], - [-118.357065661282, 33.9977436873816], - [-118.357078434427, 33.9977243706196], - [-118.357091628882, 33.9977052862174], - [-118.357105103612, 33.9976864341753], - [-118.357118998752, 33.9976675828738], - [-118.357133316101, 33.9976489639324], - [-118.357148053862, 33.9976305773515], - [-118.357163212932, 33.9976126569852], - [-118.357178653175, 33.9975947358702], - [-118.35719451383, 33.9975770478609], - [-118.357210795795, 33.9975595929573], - [-118.357227358034, 33.9975423704149], - [-118.357244342481, 33.9975253802337], - [-118.357261607202, 33.9975086231586], - [-118.357279152198, 33.9974920991898], - [-118.357297258641, 33.9974760399476], - [-118.357311856264, 33.9974620757106], - [-118.35770360617, 33.9971004011703], - [-118.357737714303, 33.9971092452251], - [-118.358117672922, 33.9973899271157], - [-118.358365972656, 33.9988750125049], - [-118.358378605664, 33.9989459958], - [-118.358392781977, 33.9990169797804], - [-118.358408502495, 33.9990877305964], - [-118.358425627079, 33.9991582482483], - [-118.358444154832, 33.9992285334816], - [-118.35846422589, 33.9992985862969], - [-118.358485701914, 33.9993681728455], - [-118.358508721243, 33.9994375269776], - [-118.358533143741, 33.9995066486937], - [-118.358558970305, 33.9995750717867], - [-118.358586341073, 33.9996434948246], - [-118.358614974873, 33.9997112192415], - [-118.358645152877, 33.9997787112459], - [-118.358676734049, 33.9998457377351], - [-118.358709719288, 33.999912066352], - [-118.358735685989, 33.9999623361449], - [-118.35896419494, 34.0003961446034], - [-118.359091923695, 34.0005897751316], - [-118.359107925385, 34.0006144443739], - [-118.359127154722, 34.0006451645524], - [-118.359145542338, 34.0006758847198], - [-118.359163508643, 34.000707070333], - [-118.359180632329, 34.0007384882906], - [-118.359197195467, 34.0007701393372], - [-118.359213056121, 34.0008022558284], - [-118.359228215192, 34.0008343723074], - [-118.359242672678, 34.0008667218746], - [-118.359256568717, 34.000899303785], - [-118.359269622136, 34.0009318856829], - [-118.359282114109, 34.0009649330238], - [-118.359293904497, 34.0009979803517], - [-118.359304993301, 34.001031260022], - [-118.359315379622, 34.001064772779], - [-118.359325065257, 34.0010982855229], - [-118.359333907375, 34.0011320313531], - [-118.359342188943, 34.0011657764251], - [-118.359349768928, 34.0011997545832], - [-118.35935664643, 34.0012337327277], - [-118.35936268221, 34.0012679439579], - [-118.359368156543, 34.0013021544295], - [-118.359372788257, 34.0013365979864], - [-118.359376718386, 34.0013708091751], - [-118.359379946033, 34.0014052527042], - [-118.359382472994, 34.0014396962193], - [-118.359384297472, 34.0014743720744], - [-118.359385420366, 34.0015088155615], - [-118.359385701539, 34.0015432590346], - [-118.359385420366, 34.0015779355922], - [-118.359384297472, 34.0016123790372], - [-118.359382472994, 34.0016468224684], - [-118.359379946033, 34.0016812658855], - [-118.359376718386, 34.0017157092887], - [-118.35937264812, 34.0017501526779], - [-118.359368015508, 34.0017843629553], - [-118.359362541174, 34.0018185739637], - [-118.359356365257, 34.0018527842136], - [-118.359349627892, 34.0018867620968], - [-118.359342048806, 34.0019207399665], - [-118.359333767238, 34.0019547178226], - [-118.359324784085, 34.0019884633126], - [-118.359315099348, 34.0020219756919], - [-118.357812385862, 34.0070093543048], - [-118.35779947258, 34.0070535694108], - [-118.357787401918, 34.007098017577], - [-118.357776453251, 34.0071426980585], - [-118.357766348102, 34.0071876108547], - [-118.357757083777, 34.0072325243718], - [-118.357748802208, 34.0072774371205], - [-118.357741643534, 34.0073225829281], - [-118.357735187342, 34.0073679617942], - [-118.357729854044, 34.0074133398914], - [-118.357725362467, 34.007458718709], - [-118.357721992887, 34.0075040967576], - [-118.357719466824, 34.0075497078641], - [-118.357717782483, 34.0075950866088], - [-118.357717221036, 34.0076406976663], - [-118.35771750131, 34.0076863086994], - [-118.35771876524, 34.0077319197079], - [-118.357721011028, 34.0077772976106], - [-118.357724238675, 34.0078229085703], - [-118.357728449977, 34.007868287169], - [-118.357733503001, 34.0079136649988], - [-118.3578098598, 34.0085319700477], - [-118.357813789929, 34.0085661775935], - [-118.357818702815, 34.00861620993], - [-118.357822632945, 34.0086664745713], - [-118.35782544018, 34.0087169715169], - [-118.357827264658, 34.0087672360986], - [-118.357827966243, 34.0088175006505], - [-118.357827544933, 34.008867998251], - [-118.357826141764, 34.0089182627433], - [-118.357823755839, 34.0089685272058], - [-118.357820246121, 34.0090187916385], - [-118.357815754545, 34.0090690560415], - [-118.357810140972, 34.009119087337], - [-118.357808035321, 34.0091346789673], - [-118.358627466845, 34.0090618417983], - [-118.36144845801, 34.0083371937161], - [-118.36141687594, 34.0078487396283], - [-118.361416735803, 34.0078454817044], - [-118.361416455528, 34.007840594446], - [-118.361416314493, 34.007835707932], - [-118.361416455528, 34.007830820673], - [-118.361416455528, 34.0078259341585], - [-118.361416735803, 34.0078210468989], - [-118.361417016975, 34.0078161603838], - [-118.361417437387, 34.0078112731237], - [-118.361417998834, 34.007806386608], - [-118.361418560281, 34.0078014993473], - [-118.361419262764, 34.0077966128311], - [-118.361420104485, 34.0077917255699], - [-118.361421087242, 34.0077868390531], - [-118.361422069999, 34.0077821848726], - [-118.361423192893, 34.0077772976106], - [-118.361424455924, 34.0077726434296], - [-118.361425859991, 34.0077677569117], - [-118.361427263159, 34.0077631027303], - [-118.361428807363, 34.0077582154671], - [-118.361430351567, 34.0077535612851], - [-118.361432176046, 34.0077489071029], - [-118.361434000524, 34.0077442529204], - [-118.361435825002, 34.0077395987377], - [-118.361437930653, 34.0077349445547], - [-118.361440036304, 34.0077305234529], - [-118.361442141057, 34.0077258692694], - [-118.361444527881, 34.0077214474225], - [-118.361446913806, 34.0077170263201], - [-118.361449439869, 34.0077126044727], - [-118.36145196683, 34.0077081833698], - [-118.361454633928, 34.007703761522], - [-118.361457441163, 34.0076995727555], - [-118.361460248398, 34.0076951516519], - [-118.361463195771, 34.007690962885], - [-118.36146628328, 34.0076867741179], - [-118.361469371688, 34.0076825853507], - [-118.361472600234, 34.0076786289201], - [-118.361475828779, 34.0076744401524], - [-118.361479196563, 34.0076704844662], - [-118.36148270628, 34.0076665280351], - [-118.3614862151, 34.0076625723485], - [-118.361489864057, 34.0076588489987], - [-118.361493513912, 34.0076551256488], - [-118.361497303904, 34.007651169217], - [-118.361501234033, 34.0076476789485], - [-118.361505164163, 34.0076439555981], - [-118.361509234429, 34.0076404645846], - [-118.361513304696, 34.0076369743156], - [-118.361517374962, 34.0076334833018], - [-118.361521726401, 34.0076299930326], - [-118.361525937704, 34.0076267351003], - [-118.361530289143, 34.0076234771679], - [-118.361534780719, 34.0076202192353], - [-118.361539272296, 34.0076171936398], - [-118.361543904009, 34.0076139357071], - [-118.361548535723, 34.007610910856], - [-118.361553167436, 34.007608118342], - [-118.361557940186, 34.007605092746], - [-118.361562712036, 34.0076023002318], - [-118.361567624923, 34.0075997407994], - [-118.361572537809, 34.0075969482851], - [-118.361577590832, 34.0075943881078], - [-118.361582643856, 34.0075918286751], - [-118.361587696879, 34.0075895015796], - [-118.36159289004, 34.007587174484], - [-118.361598083201, 34.0075848473884], - [-118.361603276361, 34.0075825202927], - [-118.36160847042, 34.0075804255342], - [-118.361613803718, 34.0075783315202], - [-118.361619278051, 34.0075764698435], - [-118.361624611349, 34.0075746081668], - [-118.361630085683, 34.00757274649], - [-118.361635560016, 34.0075708848132], - [-118.361641174486, 34.0075692554736], - [-118.36164664882, 34.0075676268786], - [-118.36165226329, 34.0075662306209], - [-118.361657877761, 34.0075648343632], - [-118.361663492231, 34.0075634381055], - [-118.361994184545, 34.0074875747333], - [-118.361986744698, 34.0073740122582], - [-118.361986043114, 34.0073621436676], - [-118.361985481667, 34.0073456216157], - [-118.361985201393, 34.007328866478], - [-118.361985201393, 34.0073123436749], - [-118.361985621804, 34.0072958216133], - [-118.361986324287, 34.0072792988038], - [-118.361987447181, 34.0072627767358], - [-118.361988850349, 34.0072460215817], - [-118.361990674828, 34.0072297318454], - [-118.361992780479, 34.007213209023], - [-118.361995166404, 34.0071966869421], - [-118.361997973639, 34.0071801641134], - [-118.362001202184, 34.0071638743644], - [-118.362004711902, 34.0071475846123], - [-118.362008500996, 34.0071312948572], - [-118.362012712298, 34.0071150050988], - [-118.362017203875, 34.0070989484207], - [-118.362022115863, 34.0070828909947], - [-118.362027309921, 34.0070668343105], - [-118.362032783357, 34.0070510099619], - [-118.362038679, 34.0070351856103], - [-118.362044854917, 34.0070193612558], - [-118.362051452145, 34.0070035368983], - [-118.362058329647, 34.0069881779603], - [-118.362065488321, 34.0069725859359], - [-118.36207292727, 34.0069572269923], - [-118.362080787529, 34.0069421011296], - [-118.362088928062, 34.0069269745196], - [-118.362097349768, 34.0069118486515], - [-118.362106192783, 34.0068969551198], - [-118.362115316972, 34.0068822939246], - [-118.362124580399, 34.0068676334717], - [-118.362134265136, 34.0068532053554], - [-118.362144371183, 34.0068387772366], - [-118.362154618265, 34.0068245814548], - [-118.362165144724, 34.0068106187545], - [-118.36217609339, 34.006796656052], - [-118.362187322331, 34.0067829264313], - [-118.36219869141, 34.0067694291479], - [-118.362210481798, 34.0067559318623], - [-118.36222255246, 34.006742666914], - [-118.362234764158, 34.006729635048], - [-118.362247397166, 34.0067168362642], - [-118.362260169413, 34.0067042698182], - [-118.362273223731, 34.0066917033703], - [-118.36228669846, 34.0066793692603], - [-118.362300313326, 34.0066672682328], - [-118.36231406833, 34.0066554002882], - [-118.362328245542, 34.0066437646816], - [-118.362342561992, 34.0066321290735], - [-118.362357159616, 34.0066209588882], - [-118.362372038412, 34.0066097887014], - [-118.362387056447, 34.0065990839377], - [-118.362402356553, 34.0065883791726], - [-118.362417795897, 34.0065779067462], - [-118.362433516415, 34.0065676674032], - [-118.362449518105, 34.0065578934837], - [-118.362465659034, 34.0065481195631], - [-118.362481941897, 34.0065385787262], - [-118.362498504136, 34.0065295025684], - [-118.36251520741, 34.0065204271543], - [-118.362535980951, 34.0065094892945], - [-118.362539911081, 34.0065076275944], - [-118.362543700174, 34.0065053004691], - [-118.362547490167, 34.0065032064287], - [-118.362551280159, 34.0065008793034], - [-118.362554929115, 34.0064985521779], - [-118.36255857897, 34.0064962250525], - [-118.362562228825, 34.0064938979269], - [-118.362565737645, 34.0064913377164], - [-118.362569246464, 34.0064887782505], - [-118.362572755284, 34.0064862180398], - [-118.362576123966, 34.0064836585738], - [-118.362579352511, 34.006481098363], - [-118.362582721194, 34.0064783058118], - [-118.362585949739, 34.0064755132605], - [-118.362589037248, 34.0064727207092], - [-118.362592125656, 34.0064699281578], - [-118.362595213166, 34.006466903266], - [-118.362598160538, 34.0064641107144], - [-118.362601108809, 34.0064610850776], - [-118.362603916044, 34.0064580601855], - [-118.36260672328, 34.0064550345486], - [-118.36260938948, 34.0064517765712], - [-118.362612056578, 34.0064487516788], - [-118.362614723676, 34.0064454937012], - [-118.362617249738, 34.0064422357235], - [-118.362619636562, 34.0064389777456], - [-118.362622022487, 34.0064357197677], - [-118.362624408413, 34.0064324617896], - [-118.362626654201, 34.0064292038113], - [-118.362628759852, 34.0064257127479], - [-118.362630865503, 34.006422222429], - [-118.362632970256, 34.0064187313653], - [-118.36263493577, 34.0064154733865], - [-118.362636760248, 34.0064117499821], - [-118.362638584726, 34.0064082596626], - [-118.362640269067, 34.0064047685983], - [-118.362641953409, 34.0064012782786], - [-118.36264363775, 34.0063975548735], - [-118.362645041816, 34.0063938314683], - [-118.36264658602, 34.0063903404034], - [-118.362647849052, 34.0063866169979], - [-118.362649112083, 34.0063828935922], - [-118.362650375114, 34.0063791701863], - [-118.362651498907, 34.0063754467803], - [-118.362652480765, 34.0063717233741], - [-118.362653463522, 34.0063679999677], - [-118.362654446279, 34.0063642765612], - [-118.362655288001, 34.0063603208139], - [-118.362655990483, 34.0063565974071], - [-118.362656551032, 34.0063528740001], - [-118.362657112479, 34.0063489175076], - [-118.362657674824, 34.0063451941002], - [-118.362658095236, 34.0063412383521], - [-118.362658376409, 34.0063375149444], - [-118.362658656683, 34.0063337915365], - [-118.36265879682, 34.0063298350432], - [-118.362658937856, 34.0063242499308], - [-118.365038911912, 34.0055595514969], - [-118.366242233082, 34.0063563643218], - [-118.366855333264, 34.0070812623905], - [-118.367185042821, 34.0070933633589], - [-118.366730972292, 34.0156266761455], - [-118.372661538407, 34.0158442390779], - [-118.372869975401, 34.0119257007136], - [-118.37318747326, 34.01191499662], - [-118.373210072177, 34.0119873652019], - [-118.373234073365, 34.0120595013971], - [-118.373259619655, 34.0121311721368], - [-118.373286709251, 34.0122026104915], - [-118.373315343051, 34.0122735826479], - [-118.373345380019, 34.0123440900966], - [-118.373367978936, 34.0123945848552], - [-118.373836084743, 34.013423096655], - [-118.373846893272, 34.0134470644614], - [-118.373876228656, 34.0135147778712], - [-118.373904301009, 34.0135827242925], - [-118.373930829157, 34.0136511360453], - [-118.373955954138, 34.0137200131284], - [-118.373979534914, 34.0137893555409], - [-118.374001852659, 34.0138586978966], - [-118.3740226262, 34.0139285055801], - [-118.374041855537, 34.0139987785902], - [-118.374059681705, 34.0140690515421], - [-118.37407596367, 34.0141397898191], - [-118.374090841567, 34.0142105280371], - [-118.37410417616, 34.0142817315786], - [-118.374116106685, 34.0143529350604], - [-118.374126493904, 34.0144243715457], - [-118.374135336022, 34.0144958072261], - [-118.374142775869, 34.0145674759093], - [-118.374148670614, 34.0146393775944], - [-118.374153022053, 34.0147112784741], - [-118.374155829288, 34.0147831800375], - [-118.374157233355, 34.0148550807953], - [-118.37415709232, 34.0149269822369], - [-118.374155407979, 34.014998882873], - [-118.374152179433, 34.0150707841928], - [-118.37414754772, 34.015142684707], - [-118.374141371802, 34.0152143528441], - [-118.374133792716, 34.0152860209206], - [-118.374124528391, 34.0153574566207], - [-118.374113860897, 34.0154288915161], - [-118.374101790234, 34.0155000940357], - [-118.374088034332, 34.0155712964957], - [-118.374072875262, 34.015642033521], - [-118.374056313023, 34.0157125381723], - [-118.374038205682, 34.0157828097057], - [-118.374018555035, 34.0158530811809], - [-118.37399750122, 34.0159226541655], - [-118.373975043337, 34.0159922278377], - [-118.373951041251, 34.0160613353356], - [-118.373925635997, 34.0161299781495], - [-118.373898826675, 34.0161983878497], - [-118.373870614186, 34.0162665644366], - [-118.373840857492, 34.0163340432844], - [-118.373809837767, 34.0164010567077], - [-118.373777273838, 34.0164678377652], - [-118.373743446877, 34.0165339203427], - [-118.373708074815, 34.0165995374991], - [-118.372906749734, 34.0180570629603], - [-118.372871518706, 34.0181229112477], - [-118.372837691746, 34.0181889925368], - [-118.372805127817, 34.0182555391352], - [-118.372774108092, 34.018322784094], - [-118.372755860614, 34.0183642011485], - [-118.375804237835, 34.0185217250068], - [-118.375926773429, 34.0180877768291], - [-118.37756676027, 34.0180598551306], - [-118.37757279605, 34.0180591567157], - [-118.377577708937, 34.0180589244072], - [-118.377985178461, 34.0180233238556], - [-118.378410896363, 34.0184072465699], - [-118.378599822847, 34.0185787318602], - [-118.378772046955, 34.0187341615816], - [-118.378909461345, 34.0188216488445], - [-118.379491962213, 34.0191946317224], - [-118.379559195722, 34.0192381428225], - [-118.379728752732, 34.0193468028335], - [-118.379821531633, 34.0194061359045], - [-118.380175945759, 34.0196332285173], - [-118.380252582832, 34.0196823228821], - [-118.38052502479, 34.0198682315036], - [-118.380902878656, 34.0201260360114], - [-118.381330841448, 34.0205753302521], - [-118.381393443243, 34.0208452316207], - [-118.379083509932, 34.0212095971071], - [-118.377961177274, 34.0213866608578], - [-118.377961878858, 34.0219099376147], - [-118.377963843473, 34.0220476783236], - [-118.377645924304, 34.0222656901379], - [-118.376967696265, 34.0237533647944], - [-118.376849792384, 34.0238475937966], - [-118.376488501655, 34.0259159603803], - [-118.376945238383, 34.0261811915936], - [-118.377099074875, 34.0262388909057], - [-118.377033666743, 34.0266127725629], - [-118.377023700833, 34.0266655854068], - [-118.377012752166, 34.0267183989624], - [-118.377000541367, 34.0267707464265], - [-118.376987346912, 34.0268230946029], - [-118.376973030461, 34.0268752097181], - [-118.376957590218, 34.0269270917724], - [-118.376941027979, 34.0269787415109], - [-118.376891199329, 34.0269743214113], - [-118.37688895354, 34.026981068075], - [-118.376871267509, 34.0270315548715], - [-118.376852599619, 34.0270820408936], - [-118.376833090008, 34.0271315970061], - [-118.376237955234, 34.0285994105781], - [-118.374971050409, 34.0295665396586], - [-118.373802959367, 34.0303293929563], - [-118.37365487838, 34.0304259418707], - [-118.371911023835, 34.0299329611641], - [-118.37023917465, 34.0336424577293], - [-118.369589299237, 34.0350845689129], - [-118.372826322668, 34.0337189952697], - [-118.375931265006, 34.0324217962692], - [-118.378917742016, 34.0316194123672], - [-118.382381730193, 34.0307041871753], - [-118.382390853483, 34.0307016276949], - [-118.382538935368, 34.0306625431241], - [-118.384217802192, 34.0302191181015], - [-118.385427580453, 34.0294958142378], - [-118.386408008042, 34.0289095354534], - [-118.38667399291, 34.0292117483414], - [-118.386810845853, 34.0291340431101], - [-118.387183646696, 34.0294046155818], - [-118.387660876691, 34.0291338108319], - [-118.387690493248, 34.0291170599942], - [-118.387773447274, 34.0290695992695], - [-118.388166319176, 34.0288441600926], - [-118.38896315268, 34.0283823463891], - [-118.389404731236, 34.0281289870002], - [-118.389828483624, 34.0278916803481], - [-118.389919578184, 34.0278395658572], - [-118.390187108153, 34.0276804303387], - [-118.390407335535, 34.0275543315142], - [-118.391598164734, 34.0268752097181], - [-118.391870466554, 34.0267200271899], - [-118.39216031337, 34.0265536775597], - [-118.392497602912, 34.0263596409209], - [-118.392900019413, 34.0261309373221], - [-118.393458660129, 34.0253754914251], - [-118.393814897834, 34.0255371901578], - [-118.393949363954, 34.0257112199295], - [-118.394208753391, 34.0255639464674], - [-118.39407442651, 34.0253896833607], - [-118.394085094004, 34.0253526906632], - [-118.39409604267, 34.0253145342725], - [-118.394298725056, 34.0251970400283], - [-118.394333674911, 34.0252424093407], - [-118.394606398041, 34.0250872238266], - [-118.394821291227, 34.0249336670352], - [-118.395558050795, 34.0245151074091], - [-118.395766347652, 34.024479974892], - [-118.397062307586, 34.0237435928533], - [-118.397017813132, 34.0236856588069], - [-118.397413211994, 34.0234609036971], - [-118.397576593087, 34.0233678372248], - [-118.39836219765, 34.022921582072], - [-118.397493077613, 34.0217943007666], - [-118.397610841357, 34.0216823857513], - [-118.399112992497, 34.0207917167624], - [-118.399132362869, 34.0208168454827], - [-118.399504040818, 34.0212989431944], - [-118.400261713166, 34.0208652415156], - [-118.400563491406, 34.0212561315393], - [-118.400486291987, 34.02130313129], - [-118.400620337696, 34.021476704401], - [-118.401281020739, 34.021101171781], - [-118.401297162567, 34.0210916325793], - [-118.400454149368, 34.0199975992065], - [-118.403058140798, 34.0184505250215], - [-118.403075966967, 34.0184735606897], - [-118.404537132472, 34.0176051955146], - [-118.405484855096, 34.0170423352638], - [-118.405652727765, 34.0168894623417], - [-118.406976339641, 34.0186073510538], - [-118.411205018704, 34.0162011800812], - [-118.41131885142, 34.0161364933614], - [-118.411327834573, 34.0161478946088], - [-118.411432685034, 34.0160883269464], - [-118.414675322936, 34.0142431048282], - [-118.414684446226, 34.0142547393935], - [-118.414847827318, 34.014161662827], - [-118.414838844165, 34.014150028249], - [-118.41525319209, 34.0139143117281], - [-118.4152440688, 34.0139029094362], - [-118.416978378745, 34.0129158231616], - [-118.417096844073, 34.0128483415969], - [-118.417150883128, 34.0128343798871], - [-118.41728605173, 34.0127557288794], - [-118.418811923783, 34.0118873052167], - [-118.419581667692, 34.011449366871], - [-118.419799369012, 34.0113251055262], - [-118.421380544049, 34.0104252528684], - [-118.420241368878, 34.0089824895512], - [-118.42006914477, 34.0087644436226], - [-118.419379687789, 34.0091416601266], - [-118.418880981771, 34.0085100955895], - [-118.418792414172, 34.0083979307259], - [-118.417360443015, 34.0065841903512], - [-118.418844067301, 34.0057094199005], - [-118.41901755444, 34.005606793025], - [-118.421089715374, 34.0043608278384], - [-118.419238062995, 34.0020161571991], - [-118.419146967537, 34.0019002601563], - [-118.41905615415, 34.0017857593081], - [-118.419023589322, 34.0017441018132], - [-118.418176225583, 34.0006709970488], - [-118.41919019896, 34.0000386716995], - [-118.420153080655, 33.9988582556961], - [-118.420158695125, 33.9988515060526], - [-118.420163889184, 33.9988454549814], - [-118.420169362619, 33.9988394039099], - [-118.42017497709, 33.9988335859438], - [-118.42018059156, 33.9988275348713], - [-118.420186346168, 33.9988217161597], - [-118.420192241811, 33.9988161305536], - [-118.420198277592, 33.9988105449472], - [-118.420204453509, 33.9988049593403], - [-118.420210769564, 33.9987993737332], - [-118.420217085619, 33.9987940212317], - [-118.420223542709, 33.9987889010911], - [-118.420230139038, 33.9987837809502], - [-118.420236877301, 33.998778660809], - [-118.420243754803, 33.9987737730289], - [-118.420250632305, 33.9987688859932], - [-118.420257649944, 33.9987639982125], - [-118.420264808618, 33.9987593435376], - [-118.420271967293, 33.9987546888625], - [-118.420279266104, 33.9987502672933], - [-118.420285582159, 33.9987465435528], - [-118.420294144901, 33.9987416557708], - [-118.420301723987, 33.9987376996685], - [-118.420309444108, 33.9987335104598], - [-118.42031716423, 33.9987295536124], - [-118.420324883453, 33.998725829871], - [-118.42033148068, 33.9987230370649], - [-118.420339902386, 33.9987190809617], - [-118.420348183955, 33.9987153572199], - [-118.420355482766, 33.9987125644134], - [-118.42036320199, 33.9987095385005], - [-118.420373308037, 33.9987055823968], - [-118.420383414084, 33.9987020910159], - [-118.420403345903, 33.9986955744662], - [-118.420414153534, 33.9986923161912], - [-118.420423277723, 33.9986897564903], - [-118.42043773431, 33.9986862651087], - [-118.420450507456, 33.9986832399397], - [-118.420459911918, 33.9986811449617], - [-118.420469456518, 33.9986792830899], - [-118.420479982977, 33.9986774212182], - [-118.42048672124, 33.9986764902823], - [-118.420496264941, 33.9986750938784], - [-118.420506652161, 33.9986736974745], - [-118.420515073867, 33.9986725341769], - [-118.422599025189, 33.9984456178635], - [-118.425900192822, 33.9980858093954], - [-118.428338417234, 34.0012891217286], - [-118.428339821301, 34.0012912166424], - [-118.428340944195, 34.0012930784569], - [-118.428341926952, 34.0012949402713], - [-118.428342908811, 34.0012970344401], - [-118.42834375143, 34.0012991293536], - [-118.428344312878, 34.0013012235224], - [-118.428344733289, 34.0013026198831], - [-118.428345154599, 34.0013047147964], - [-118.428345435772, 34.0013061111571], - [-118.428345575909, 34.0013082053257], - [-118.428345716046, 34.0013105325933], - [-118.428345716046, 34.0013126275065], - [-118.428345435772, 34.0013147216749], - [-118.428345154599, 34.0013168165879], - [-118.428344733289, 34.0013189107562], - [-118.428344312878, 34.0013203071167], - [-118.428343611293, 34.0013224020296], - [-118.428343189983, 34.00132379839], - [-118.428342207226, 34.0013258925581], - [-118.42834122447, 34.0013277543718], - [-118.428340101575, 34.0013298492846], - [-118.428338838544, 34.0013317110982], - [-118.428337855787, 34.0013328743593], - [-118.428336452619, 34.0013347361729], - [-118.428335469862, 34.0013359001787], - [-118.428333785521, 34.0013375288932], - [-118.42833210118, 34.0013391583523], - [-118.428330276701, 34.0013405547124], - [-118.42832859236, 34.0013424165258], - [-118.430697617749, 34.0000889414175], - [-118.430379558443, 33.9996714225793], - [-118.432178153627, 33.9987200118972], - [-118.434776109276, 33.9973457069789], - [-118.434943000086, 33.9972570342707], - [-118.435531115425, 33.996945862802], - [-118.435745869372, 33.9969595947577], - [-118.435953043334, 33.9968583529989], - [-118.442468776516, 33.9936034366945], - [-118.443362881397, 33.9953210986464], - [-118.443385479416, 33.9953646212355], - [-118.444357203228, 33.9946931552552], - [-118.444287583794, 33.9946233317597], - [-118.444698984346, 33.9943391491684], - [-118.448400464627, 33.9933390346976], - [-118.448387831619, 33.9933136647241], - [-118.447171035719, 33.9908695361773], - [-118.447227882008, 33.9908411400182], - [-118.446961757004, 33.9905574102893], - [-118.44517537262, 33.9914497929447], - [-118.442063130572, 33.9930050394293], - [-118.441957859698, 33.9928928540896], - [-118.441137304382, 33.9933024927873], - [-118.439714878723, 33.9940130719663], - [-118.439557533412, 33.994096627637], - [-118.438857830247, 33.994445979823], - [-118.438716766001, 33.9945176654273], - [-118.43806548742, 33.9948421118539], - [-118.437895650136, 33.9949265976862], - [-118.437104290066, 33.995322029619], - [-118.43706555022, 33.9952773429193], - [-118.435831209324, 33.9942756098975], - [-118.435716112678, 33.994177157896], - [-118.433622616756, 33.9924836789189], - [-118.432518390541, 33.9915885140808], - [-118.432514881721, 33.991585488659], - [-118.432500845545, 33.9915738509881], - [-118.432487089643, 33.9915619801897], - [-118.432473614914, 33.9915501101345], - [-118.432460421357, 33.9915377738257], - [-118.432447367938, 33.99152543826], - [-118.43243473493, 33.9915126364404], - [-118.432422242958, 33.9914998353638], - [-118.43241003126, 33.9914870335404], - [-118.432398100734, 33.9914737669524], - [-118.432386450483, 33.9914604996176], - [-118.432375081405, 33.9914469998993], - [-118.432363992601, 33.9914332677975], - [-118.43235318497, 33.9914195349487], - [-118.43234279775, 33.9914055697161], - [-118.432332551566, 33.9913913720997], - [-118.432301531841, 33.9913476143489], - [-118.43221787623, 33.9912291420804], - [-118.430880228178, 33.9893268196247], - [-118.429907100299, 33.9898016478598], - [-118.42975915945, 33.9898733373824], - [-118.430108238481, 33.9901561376213], - [-118.430311903624, 33.9903211627716], - [-118.430877000532, 33.9941594691751], - [-118.431204744575, 33.9963828663497], - [-118.430255899954, 33.9964166133084], - [-118.430147399638, 33.9964254574343], - [-118.429970122506, 33.9964394218418], - [-118.428548258295, 33.996553464417], - [-118.428449162441, 33.99656091209], - [-118.427367956453, 33.9966789114453], - [-118.426207445395, 33.9968052884999], - [-118.421981573568, 33.9972647142475], - [-118.4202872665, 33.9974588173882], - [-118.420252456783, 33.9974632397695], - [-118.420217928239, 33.9974681268802], - [-118.420183398796, 33.9974734802101], - [-118.420149010388, 33.9974792982693], - [-118.420114762118, 33.9974858149123], - [-118.420080795021, 33.9974925646643], - [-118.420046827025, 33.997500012255], - [-118.420013000064, 33.9975076922099], - [-118.419979313241, 33.9975160707479], - [-118.419945906692, 33.9975249147594], - [-118.419912641179, 33.9975342242441], - [-118.41987965594, 33.997543999202], - [-118.419846811737, 33.9975542396329], - [-118.419814107671, 33.997565178646], - [-118.419781683879, 33.9975763500223], - [-118.419749540361, 33.9975879868711], - [-118.419717678915, 33.9976000891921], - [-118.419686096844, 33.9976126569852], - [-118.419654655809, 33.9976259226145], - [-118.419623495947, 33.9976394213511], - [-118.419592756496, 33.997653385559], - [-118.419562298218, 33.9976678152381], - [-118.419531980077, 33.9976827103882], - [-118.419502083247, 33.9976978386447], - [-118.419472606827, 33.997713664736], - [-118.41944341158, 33.9977297231885], - [-118.419414497506, 33.9977464802199], - [-118.419385863707, 33.9977634703569], - [-118.419357791354, 33.9977809252186], - [-118.419329999276, 33.9977986131855], - [-118.41930248837, 33.99781699973], - [-118.419299962308, 33.997818628512], - [-118.419275538912, 33.9978356186345], - [-118.419248870626, 33.9978544698988], - [-118.419222622752, 33.9978740197398], - [-118.419196796187, 33.9978935695762], - [-118.419171390933, 33.9979138179888], - [-118.41914640609, 33.9979342987602], - [-118.419121842556, 33.9979552449985], - [-118.419097700333, 33.9979764235952], - [-118.41907397942, 33.9979980684031], - [-118.419050819953, 33.9980199455688], - [-118.419028080899, 33.9980420550922], - [-118.419005764052, 33.9980646308259], - [-118.418984007754, 33.9980876712801], - [-118.418962672766, 33.9981109448359], - [-118.418953689613, 33.9981209528353], - [-118.418941899225, 33.9981344514932], - [-118.418921546994, 33.998158190507], - [-118.418896983461, 33.9981886784661], - [-118.418760130517, 33.9983557822567], - [-118.418065620513, 33.9992073551971], - [-118.417543334617, 33.9995469108963], - [-118.417248855189, 33.9997384491531], - [-118.417121547744, 33.999820835911], - [-118.416070658998, 34.0005038978126], - [-118.416112065943, 34.0005215852125], - [-118.415793725464, 34.0010996818869], - [-118.415286878913, 34.0020068482055], - [-118.41458759616, 34.0032765855088], - [-118.414372562837, 34.0036666256348], - [-118.414243288979, 34.0034373961979], - [-118.412354019647, 34.0022267732786], - [-118.412223483657, 34.0021429925074], - [-118.411934338425, 34.0019577436198], - [-118.411672422926, 34.0017899483662], - [-118.411619928075, 34.0017564358953], - [-118.409743290853, 34.0005532363399], - [-118.409649529195, 34.0004931922793], - [-118.4090890649, 34.0012165109302], - [-118.405902431566, 33.9992417996035], - [-118.405253258636, 34.0001210581662], - [-118.405245117205, 34.0001322292064], - [-118.402766048194, 34.0034890602292], - [-118.399803853544, 34.0018304425843], - [-118.399827294183, 34.0016202917186], - [-118.40006296091, 34.0014166559296], - [-118.400445166215, 34.001440859479], - [-118.400814738513, 34.0014287577052], - [-118.400914535951, 34.0011122491622], - [-118.400934888182, 34.0009498053491], - [-118.401239052348, 34.00041778805], - [-118.401639364096, 33.9999227719585], - [-118.401088304263, 33.9998513232734], - [-118.401068372444, 33.9994249594545], - [-118.400996788394, 33.9991270620665], - [-118.402827947506, 33.9958387178779], - [-118.403137866279, 33.9957104767038], - [-118.404124328751, 33.9950988282566], - [-118.405062928086, 33.9946689497832], - [-118.404967060777, 33.9943070302338], - [-118.405510962834, 33.9933308881238], - [-118.405900045641, 33.9929871180873], - [-118.406229334786, 33.9929838595933], - [-118.406559464755, 33.9926486995489], - [-118.407129754823, 33.9923705618858], - [-118.407532593533, 33.9923284342711], - [-118.407885462556, 33.9921336207811], - [-118.407508591447, 33.9919071532521], - [-118.40516160263, 33.9905059715219], - [-118.405217326025, 33.9904480147962], - [-118.406152415642, 33.9894725270025], - [-118.40466205489, 33.9885666234066], - [-118.401352043342, 33.9865539090288], - [-118.40134909597, 33.986552280031], - [-118.401347131354, 33.9865513489622], - [-118.401344183982, 33.9865497192194], - [-118.401342078331, 33.9865490212903], - [-118.401340112817, 33.9865480902215], - [-118.401338008064, 33.9865473915474], - [-118.401335761378, 33.9865466936183], - [-118.401332533731, 33.9865457625494], - [-118.401330287943, 33.986545297015], - [-118.401326919261, 33.986544598341], - [-118.401324673472, 33.9865441328066], - [-118.401322427684, 33.9865439004118], - [-118.401320181896, 33.9865436672721], - [-118.401316672178, 33.9865434348774], - [-118.40131442639, 33.9865434348774], - [-118.401312040465, 33.9865434348774], - [-118.401309794676, 33.9865434348774], - [-118.401306285857, 33.9865436672721], - [-118.401304040069, 33.9865439004118], - [-118.40130179428, 33.9865441328066], - [-118.401299548492, 33.986544598341], - [-118.401297302704, 33.9865450638754], - [-118.401293934022, 33.9865457625494], - [-118.401291688233, 33.9865462280839], - [-118.401289582582, 33.986546926013], - [-118.401286354037, 33.9865480902215], - [-118.401284249285, 33.9865487881506], - [-118.401282283771, 33.9865497192194], - [-118.401279196261, 33.9865511158226], - [-118.401277230747, 33.9865520468914], - [-118.401275406269, 33.9865532110997], - [-118.401273160481, 33.9865546077028], - [-118.398779212673, 33.9881301937799], - [-118.394903403532, 33.9838595865411], - [-118.398474908371, 33.9831826754166], - [-118.39758164611, 33.9821945334113], - [-118.397662494486, 33.9820748848579], - [-118.39305146998, 33.9799109395424], - [-118.393061295753, 33.9799470216406], - [-118.392817767869, 33.97983272362], - [-118.390939446305, 33.9789511603901], - [-118.391020997164, 33.9789579108695], - [-118.387050022298, 33.9770937242382], - [-118.386042364976, 33.976620687362], - [-118.385961376463, 33.9767403435922], - [-118.385936672793, 33.9767291694766], - [-118.385909723334, 33.9767172973498], - [-118.385882493601, 33.9767056576429], - [-118.385854982696, 33.9766944835228], - [-118.385827330754, 33.9766837749897], - [-118.385799398539, 33.9766735320437], - [-118.385771326186, 33.976663754685], - [-118.385743113697, 33.976654209747], - [-118.385714479897, 33.9766451311415], - [-118.385685846097, 33.9766365173788], - [-118.385656932023, 33.976628136782], - [-118.385628017051, 33.9766204549401], - [-118.385598821804, 33.9766130055194], - [-118.38556948642, 33.9766060216869], - [-118.385540010899, 33.9765995034427], - [-118.385510394342, 33.976593450787], - [-118.385480637649, 33.9765878637197], - [-118.385450739919, 33.9765825090741], - [-118.385420843088, 33.9765776207622], - [-118.385390665085, 33.976573430461], - [-118.385360628117, 33.9765694725817], - [-118.385330309976, 33.9765659810362], - [-118.385300131972, 33.9765629543347], - [-118.385269673694, 33.9765601608001], - [-118.385239355553, 33.9765580660216], - [-118.385225599651, 33.9765569016763], - [-118.380532744912, 33.9762805744564], - [-118.380502286634, 33.9762789445167], - [-118.380471827457, 33.9762777809125], - [-118.380441229042, 33.9762770821541], - [-118.380410770764, 33.9762768497312] - ], - [ - [-118.412472906285, 33.9803022512319], - [-118.410193711526, 33.9812997257533], - [-118.409969975325, 33.9809335599223], - [-118.409969273741, 33.980932395637], - [-118.409043166378, 33.9813376695137], - [-118.410759791191, 33.9828088356692], - [-118.413448279953, 33.9851126024049], - [-118.414883198482, 33.9842490171203], - [-118.412472906285, 33.9803022512319] - ], - [ - [-118.452290309944, 34.0471330010106], - [-118.450057856326, 34.0484660272784], - [-118.448733122455, 34.0492563898339], - [-118.448554722429, 34.0493633836101], - [-118.448187676194, 34.0495827208014], - [-118.449106765019, 34.0520005049221], - [-118.448404394756, 34.0524117191275], - [-118.448325090585, 34.052458236446], - [-118.449099606344, 34.0533560156923], - [-118.445788331765, 34.0553348178257], - [-118.45283337028, 34.063507237103], - [-118.454151647959, 34.0650360478442], - [-118.454165824272, 34.0650525594078], - [-118.454199651233, 34.065093023441], - [-118.454232636472, 34.065134184738], - [-118.454264638954, 34.0651758118624], - [-118.454295938953, 34.0652176711458], - [-118.454326257094, 34.0652602276907], - [-118.454355733513, 34.0653030171375], - [-118.454384367313, 34.0653462716651], - [-118.454411878219, 34.0653899912727], - [-118.45443868754, 34.0654341759596], - [-118.454464514104, 34.0654785935463], - [-118.454489357912, 34.0655234754669], - [-118.454513218963, 34.0655688232091], - [-118.454536238292, 34.0656141701828], - [-118.454558134727, 34.0656602151552], - [-118.45457918944, 34.0657062601026], - [-118.454599261397, 34.0657527701251], - [-118.454618350597, 34.065799513044], - [-118.45463645704, 34.0658467202926], - [-118.454653580726, 34.0658939282591], - [-118.454669722553, 34.0659416005541], - [-118.454684881623, 34.0659892735665], - [-118.4546989178, 34.0660371787292], - [-118.454712112255, 34.066085316042], - [-118.454724182917, 34.0661336862484], - [-118.454735271721, 34.0661822893482], - [-118.454745237631, 34.066230891676], - [-118.454754360921, 34.0662797268966], - [-118.454762361317, 34.0663287950095], - [-118.454769379854, 34.0663778623499], - [-118.454775274599, 34.0664269304061], - [-118.454780187485, 34.0664762306098], - [-118.454787486297, 34.0665585525841], - [-118.454802785504, 34.0667292425246], - [-118.45889040122, 34.0656467272366], - [-118.459039184689, 34.0656071936696], - [-118.459533959679, 34.0654744076318], - [-118.459518239162, 34.0654560357435], - [-118.458201926996, 34.0639258372492], - [-118.456907931677, 34.0624221228745], - [-118.455082386137, 34.0603020867906], - [-118.458792709433, 34.0580782710225], - [-118.461112608654, 34.0600509162112], - [-118.462826566368, 34.063113518895], - [-118.46293366172, 34.0636577012101], - [-118.463211999321, 34.0650706984596], - [-118.465348445493, 34.0669938810169], - [-118.466560048232, 34.067617800989], - [-118.466743501281, 34.0677124470947], - [-118.467807583583, 34.0668090058624], - [-118.468477530054, 34.0662394963509], - [-118.468905352708, 34.065876254483], - [-118.468204947959, 34.0621790985456], - [-118.468180384426, 34.0620481668735], - [-118.467965631378, 34.0609153588664], - [-118.464286749117, 34.0566498072355], - [-118.461969516993, 34.0539628257194], - [-118.459233304333, 34.05078964427], - [-118.456587765822, 34.0477214798461], - [-118.454086098791, 34.0492170815891], - [-118.452290309944, 34.0471330010106] - ], - [ - [-118.40618020772, 34.0527501324089], - [-118.406114378278, 34.0527880435019], - [-118.40595127746, 34.0528438640332], - [-118.405870569222, 34.0528715420718], - [-118.405585355017, 34.0529685297538], - [-118.405630410919, 34.0530669135782], - [-118.405660447887, 34.0531329676523], - [-118.405730488631, 34.053285310107], - [-118.405825653457, 34.0534897517966], - [-118.405870850394, 34.0536167424009], - [-118.405898080127, 34.0536927967501], - [-118.405950435739, 34.053838161461], - [-118.405965734946, 34.0538807237201], - [-118.40601500215, 34.0539591044009], - [-118.406099359345, 34.0540940020919], - [-118.406134309199, 34.0541495888092], - [-118.405982297185, 34.0541493565995], - [-118.405936399562, 34.0541493565995], - [-118.405802353854, 34.0541491236455], - [-118.405613286334, 34.0541465656168], - [-118.405574266213, 34.0541463326627], - [-118.405574125178, 34.0543172805569], - [-118.405296770334, 34.0544068239873], - [-118.405374250027, 34.0545484661812], - [-118.405464081555, 34.0548664031579], - [-118.405725856917, 34.0548336097548], - [-118.405855410151, 34.0548352374427], - [-118.405938224041, 34.0548352374427], - [-118.405934855358, 34.0567167888172], - [-118.405934153774, 34.0571086768964], - [-118.40581554741, 34.0571086768964], - [-118.405551387021, 34.057108909098], - [-118.404660510686, 34.0571077466013], - [-118.404462460014, 34.0571063511587], - [-118.40404151576, 34.0571056530652], - [-118.403993091176, 34.0571056530652], - [-118.403993231313, 34.057028671482], - [-118.403473191205, 34.057028206334], - [-118.403473331343, 34.0569447118584], - [-118.403374235489, 34.0569451770068], - [-118.403273034882, 34.0568361000006], - [-118.402941359811, 34.0570491379905], - [-118.402933640588, 34.0570542546169], - [-118.402783031742, 34.0568919178663], - [-118.402482377295, 34.0570854195163], - [-118.402482096122, 34.0570865820133], - [-118.402477745581, 34.057103792475], - [-118.402419214052, 34.0571035602733], - [-118.402194775368, 34.0571033273274], - [-118.402383001166, 34.0570002974506], - [-118.402281659524, 34.0568914527176], - [-118.401894682368, 34.0571030951257], - [-118.401868294356, 34.0571030951257], - [-118.401630100669, 34.057103792475], - [-118.401568341493, 34.0571035602733], - [-118.401693964597, 34.0570323926657], - [-118.401601888179, 34.0569158726503], - [-118.401411276456, 34.0570279733879], - [-118.40128312639, 34.0571033273274], - [-118.401065143899, 34.0571030951257], - [-118.401006473131, 34.0571026299782], - [-118.40076322642, 34.0571023970322], - [-118.400918887391, 34.0570019250969], - [-118.40079747469, 34.0568716835221], - [-118.400440394365, 34.0571019318847], - [-118.400280943402, 34.0571019318847], - [-118.400055943271, 34.0568595896514], - [-118.399972006487, 34.0569047090832], - [-118.39972511082, 34.0570365789973], - [-118.399724969784, 34.0571012345354], - [-118.399107659199, 34.0571005364419], - [-118.399107237889, 34.0570396020866], - [-118.39894933113, 34.0570391369387], - [-118.398511823739, 34.0570386717908], - [-118.398511682703, 34.0570998390927], - [-118.398421009454, 34.0570998390927], - [-118.398090878587, 34.0570991409992], - [-118.39688222322, 34.0570972804088], - [-118.389865398094, 34.0570893728992], - [-118.387794782262, 34.0570954198184], - [-118.38677196587, 34.057098210704], - [-118.38553481684, 34.0571012345354], - [-118.384727175704, 34.0571040254209], - [-118.383579577929, 34.0571035602733], - [-118.383528205074, 34.059371124328], - [-118.383492272463, 34.0609316382975], - [-118.383466024589, 34.0620930513552], - [-118.383446935389, 34.06294189144], - [-118.37946473248, 34.062952821282], - [-118.377341760139, 34.0629588677828], - [-118.377326460931, 34.0620956091441], - [-118.377280282136, 34.0595269444508], - [-118.377181327318, 34.0595339214645], - [-118.376018710609, 34.0596162501831], - [-118.376013096138, 34.0599695181786], - [-118.373084728449, 34.0617118841351], - [-118.372957981553, 34.0617874668434], - [-118.372753474688, 34.0619088635622], - [-118.37225645391, 34.0622046801259], - [-118.372241575113, 34.0622923547476], - [-118.372039314037, 34.0638714193471], - [-118.372147393044, 34.0640474638316], - [-118.372351058187, 34.0643811788379], - [-118.375821081246, 34.0700564649453], - [-118.375885787795, 34.0700290254289], - [-118.376050713091, 34.0698139268452], - [-118.376042571659, 34.0695788300608], - [-118.376196969599, 34.0695811554563], - [-118.376191214991, 34.0694083780289], - [-118.377264701756, 34.0693974490196], - [-118.377268210575, 34.0693976811875], - [-118.377270737536, 34.0693976811875], - [-118.377272421877, 34.0693979140996], - [-118.377275088975, 34.0693983791797], - [-118.377276773316, 34.0693986113477], - [-118.377279159242, 34.0693993093399], - [-118.377280843583, 34.0693995415079], - [-118.377284072128, 34.0694007045801], - [-118.377287299775, 34.0694018669082], - [-118.377290248046, 34.0694032621484], - [-118.377293195418, 34.0694046573886], - [-118.377296002653, 34.0694065177088], - [-118.377298669752, 34.0694083780289], - [-118.377301055677, 34.069410238349], - [-118.377303441602, 34.0694123315812], - [-118.377305547253, 34.0694146569812], - [-118.377307512767, 34.069417214549], - [-118.37730919621, 34.0694195399489], - [-118.377310740414, 34.0694220982608], - [-118.377312004344, 34.0694248887405], - [-118.377312986202, 34.0694276792201], - [-118.377313407512, 34.0694290744598], - [-118.377313968959, 34.0694311669474], - [-118.377314530406, 34.0694339574268], - [-118.377314811579, 34.0694360506583], - [-118.377314811579, 34.0694374458979], - [-118.377324496316, 34.0697137028911], - [-118.377522687125, 34.0697137028911], - [-118.378381419943, 34.0697088191962], - [-118.378579610753, 34.0697076568723], - [-118.379438344469, 34.0697071917939], - [-118.37963779831, 34.0697046334906], - [-118.383767101244, 34.0696888208234], - [-118.38385791553, 34.0721283552982], - [-118.390715850241, 34.0721153334744], - [-118.390716832998, 34.0724587830347], - [-118.390720903265, 34.073652592076], - [-118.390730026555, 34.0765054349252], - [-118.390144858589, 34.0765068300484], - [-118.389849397302, 34.0765075272379], - [-118.389618221253, 34.0764784625389], - [-118.389608256242, 34.0764784625389], - [-118.388660532719, 34.0764768345226], - [-118.38689646608, 34.076473579234], - [-118.386714838407, 34.0764710218791], - [-118.384303423317, 34.0764682316315], - [-118.383852861608, 34.0764682316315], - [-118.383621405285, 34.0764042880603], - [-118.381705747942, 34.0764008006201], - [-118.38170490622, 34.0767160982673], - [-118.380678019561, 34.0767137730677], - [-118.380666509447, 34.0767135401757], - [-118.380655000232, 34.0767133080278], - [-118.380643490118, 34.0767128429878], - [-118.380631980903, 34.0767121450559], - [-118.380620470789, 34.076711214976], - [-118.380607698542, 34.0767102848961], - [-118.380597591597, 34.0767091226683], - [-118.380586082382, 34.0767077275485], - [-118.380574713303, 34.0767063324286], - [-118.380563343327, 34.0767047044166], - [-118.380552114386, 34.0767028442567], - [-118.380540886343, 34.0767007519488], - [-118.380529656504, 34.0766986588968], - [-118.380518428461, 34.0766963336967], - [-118.380507339657, 34.0766937763486], - [-118.380496250853, 34.0766912182563], - [-118.380485162049, 34.076688428016], - [-118.380474214281, 34.0766854056275], - [-118.380463265614, 34.0766823824949], - [-118.380452457983, 34.0766791272142], - [-118.380443614967, 34.0766761048254], - [-118.380430982858, 34.0766719194641], - [-118.380420455501, 34.0766681991428], - [-118.380409928144, 34.0766642459292], - [-118.380399400787, 34.0766602934595], - [-118.380389014466, 34.0766561080974], - [-118.380378768282, 34.0766516898429], - [-118.380368662235, 34.0766472723323], - [-118.380358556188, 34.0766426219293], - [-118.380348590278, 34.0766377386338], - [-118.380338624369, 34.0766328560821], - [-118.380328939631, 34.0766277406379], - [-118.380319254894, 34.0766226251934], - [-118.380309710294, 34.0766172768563], - [-118.380300305832, 34.0766116963707], - [-118.380290901369, 34.0766061158847], - [-118.380281637942, 34.0766003032501], - [-118.380258898887, 34.0765854219512], - [-118.380244862711, 34.0765765861788], - [-118.380230686397, 34.0765682154462], - [-118.380216510084, 34.0765598447127], - [-118.38020191246, 34.0765517061268], - [-118.380187314837, 34.0765438004326], - [-118.380172436041, 34.0765358947377], - [-118.380157558143, 34.0765284540829], - [-118.380142399073, 34.07652124632], - [-118.380127098967, 34.076514270705], - [-118.380111659622, 34.0765072950895], - [-118.380096079242, 34.0765007845144], - [-118.380080358724, 34.0764945060873], - [-118.38006449807, 34.0764882284039], - [-118.380048637415, 34.0764824150171], - [-118.380032495588, 34.076476602374], - [-118.380016213623, 34.0764712540277], - [-118.379999931659, 34.0764659064251], - [-118.379983509557, 34.0764610231195], - [-118.379967086557, 34.0764563727062], - [-118.379950384181, 34.0764517222927], - [-118.379933680906, 34.0764475369203], - [-118.379916837495, 34.0764435844405], - [-118.379899994083, 34.0764398641091], - [-118.379872343041, 34.076434050719], - [-118.379865745813, 34.0764326555947], - [-118.379859148586, 34.0764310283215], - [-118.379852411221, 34.0764291681556], - [-118.379845955029, 34.0764275401384], - [-118.379839357802, 34.0764256799723], - [-118.379832900711, 34.0764235876576], - [-118.379826444519, 34.0764214945987], - [-118.379819987429, 34.0764194022838], - [-118.379813671374, 34.0764170770761], - [-118.37980735532, 34.0764147518683], - [-118.379801039265, 34.0764121937676], - [-118.379794863347, 34.0764096364109], - [-118.37978868743, 34.07640707831], - [-118.379782651649, 34.0764045209532], - [-118.379776615869, 34.0764014978106], - [-118.379770580088, 34.0763987075607], - [-118.379765948375, 34.0763963823524], - [-118.378814155484, 34.0763942900369], - [-118.378659757544, 34.0766105341415], - [-118.377178940494, 34.0765972801144], - [-118.377164624043, 34.0775038722146], - [-118.377232137827, 34.0781774732112], - [-118.377505562541, 34.0786246005635], - [-118.377512721216, 34.0786369234705], - [-118.377520020027, 34.0786492471198], - [-118.377579673552, 34.0787473682448], - [-118.377226243082, 34.0788964094589], - [-118.377078862781, 34.0789584910383], - [-118.377049527397, 34.0789101281541], - [-118.376905375642, 34.0789708138968], - [-118.377239717811, 34.0801359327362], - [-118.376562191356, 34.0801605788527], - [-118.375178785818, 34.0801638339996], - [-118.375182014363, 34.0819408926633], - [-118.374323420784, 34.0819427527081], - [-118.374320052102, 34.0801656940835], - [-118.370335743543, 34.0801742965991], - [-118.370311321045, 34.0801742965991], - [-118.370313987245, 34.0831622362062], - [-118.372689891034, 34.0831268958555], - [-118.373070552136, 34.0831213157988], - [-118.376351227401, 34.0830722574124], - [-118.376516574007, 34.0830643523268], - [-118.376632512374, 34.0830380799092], - [-118.377045175958, 34.0830448221123], - [-118.376990715594, 34.0867515299967], - [-118.376989452563, 34.0868833527145], - [-118.376963485861, 34.0886349296317], - [-118.37629578428, 34.0889941220136], - [-118.375769989565, 34.0890020265456], - [-118.375770269839, 34.0891396582777], - [-118.37539550438, 34.0891452379377], - [-118.375197875018, 34.0894230576764], - [-118.37476892947, 34.089429567258], - [-118.374769631054, 34.0897043641397], - [-118.374340264196, 34.0897108736997], - [-118.374142634833, 34.0899619563458], - [-118.373712987701, 34.089968465886], - [-118.373713268874, 34.0900507650294], - [-118.373283902016, 34.0900572745628], - [-118.373086132516, 34.0902525603306], - [-118.372509947703, 34.0902611625653], - [-118.372501384962, 34.0867910536959], - [-118.372500402205, 34.0865994798287], - [-118.372499560483, 34.0860224306822], - [-118.372497033522, 34.0854430526429], - [-118.371838175855, 34.0854530495982], - [-118.371640125183, 34.0854560724177], - [-118.370992776731, 34.0854651393877], - [-118.370993338178, 34.0856025447458], - [-118.37031637317, 34.0856123095587], - [-118.370319601715, 34.0870872483608], - [-118.366441968096, 34.0871458361937], - [-118.366454740343, 34.0889292579545], - [-118.366022145838, 34.0889353026013], - [-118.365823955028, 34.0889380924381], - [-118.364958483947, 34.0889504145883], - [-118.363896226124, 34.0889659908013], - [-118.36369817635, 34.0889687806372], - [-118.363281301464, 34.0889752902537], - [-118.363279476986, 34.088728157332], - [-118.362822740258, 34.0887351319409], - [-118.362822599222, 34.0887065356686], - [-118.362812633313, 34.0872558041447], - [-118.362812493175, 34.0872009362467], - [-118.361511058908, 34.0872204655026], - [-118.360528526565, 34.0872109337383], - [-118.353056508915, 34.0871488582088], - [-118.352812419585, 34.0871469982783], - [-118.352811718001, 34.0872018662113], - [-118.352813402342, 34.0887660523284], - [-118.352813682616, 34.0889034523296], - [-118.352813822753, 34.0889671536049], - [-118.352378140739, 34.0889638987964], - [-118.351227454555, 34.0889548314572], - [-118.348576301573, 34.0889336755684], - [-118.344153501967, 34.0888985697411], - [-118.343400040921, 34.0888994996872], - [-118.343402426846, 34.0901811884563], - [-118.343412813168, 34.0901811884563], - [-118.343412813168, 34.0902223375627], - [-118.343391759352, 34.0902223375627], - [-118.343392460937, 34.0905852429196], - [-118.343392039627, 34.0905933794129], - [-118.343391057768, 34.09081330696], - [-118.3433923208, 34.0925031966626], - [-118.343392179764, 34.0926405898544], - [-118.343393583831, 34.0942916199425], - [-118.343394425552, 34.0944152947945], - [-118.3439993852, 34.0944141328097], - [-118.344165994838, 34.0944424943108], - [-118.344263966899, 34.0944427264101], - [-118.344641960903, 34.0944455160654], - [-118.346320407316, 34.0944583023565], - [-118.349434472944, 34.0944820144195], - [-118.349649086753, 34.0944836413457], - [-118.3507259422, 34.0944917782082], - [-118.351657663135, 34.0944989844415], - [-118.352830526028, 34.0945073534016], - [-118.35283038589, 34.0944806195924], - [-118.358723895601, 34.0945219994518], - [-118.361455195375, 34.0945412941759], - [-118.361653386184, 34.0945408292339], - [-118.36166447409, 34.0971830535115], - [-118.363117780235, 34.0971630616262], - [-118.365193028678, 34.097134236109], - [-118.365814690703, 34.0971254024807], - [-118.366128539605, 34.0971207532023], - [-118.366733359116, 34.0971123845006], - [-118.366732376359, 34.0968924733194], - [-118.367353336799, 34.0968838725024], - [-118.367354178521, 34.0971028538501], - [-118.368179926998, 34.0970914627426], - [-118.368182453959, 34.097728644557], - [-118.368214316304, 34.097984584742], - [-118.36875218258, 34.0979764489592], - [-118.369530067022, 34.097403428864], - [-118.369537927281, 34.097397617657], - [-118.369581720151, 34.0973662347519], - [-118.369626355641, 34.0973353175058], - [-118.369671411542, 34.0973048644313], - [-118.369717310063, 34.0972753419437], - [-118.369763628996, 34.0972462836292], - [-118.369810650411, 34.0972181559031], - [-118.369858373411, 34.0971904923513], - [-118.369906516822, 34.0971637593896], - [-118.369955222578, 34.0971374906033], - [-118.369967434276, 34.0971312144503], - [-118.370004489782, 34.0971121524086], - [-118.370054318432, 34.0970872783904], - [-118.370104707631, 34.0970633349651], - [-118.370155519039, 34.0970400885536], - [-118.370206750858, 34.0970175391564], - [-118.370258544124, 34.0969956875179], - [-118.371336102053, 34.0965516795989], - [-118.371372876386, 34.0965363368753], - [-118.371409230307, 34.096520297124], - [-118.371445162919, 34.0965037916947], - [-118.371480955393, 34.0964868220746], - [-118.371516185522, 34.0964691546821], - [-118.371551136274, 34.0964510223543], - [-118.371585805854, 34.0964326571849], - [-118.371619772952, 34.0964135949859], - [-118.371651354124, 34.0963952305523], - [-118.371682514885, 34.0963766332768], - [-118.371715359088, 34.096356176269], - [-118.371747923017, 34.0963352543241], - [-118.371780065636, 34.0963138674417], - [-118.371811787844, 34.0962920156215], - [-118.371842947706, 34.0962696988632], - [-118.371873687157, 34.0962469171663], - [-118.371903865161, 34.0962236705305], - [-118.371933621854, 34.0961999589555], - [-118.371962817101, 34.0961760145353], - [-118.371991591038, 34.0961513730805], - [-118.372019803528, 34.0961264995238], - [-118.372047314433, 34.0961013931212], - [-118.372068509284, 34.0960814009805], - [-118.372206344985, 34.0959495918413], - [-118.372220661435, 34.095936108746], - [-118.372237504847, 34.0959207659108], - [-118.372254769568, 34.0959054230727], - [-118.372272174427, 34.0958907780058], - [-118.372290000596, 34.0958761321924], - [-118.372308107038, 34.0958617192158], - [-118.372326494654, 34.0958475390762], - [-118.372345162544, 34.0958338231252], - [-118.372364111606, 34.0958203400114], - [-118.372383481081, 34.095807089735], - [-118.37240299159, 34.0957940715521], - [-118.372422782374, 34.0957812854628], - [-118.372442854331, 34.0957689650508], - [-118.372463206562, 34.0957568767327], - [-118.372483699829, 34.0957450205087], - [-118.372504613507, 34.0957333971227], - [-118.372525667322, 34.0957222386706], - [-118.372546862173, 34.0957113130568], - [-118.372568478334, 34.0957008516333], - [-118.372590234631, 34.0956906230484], - [-118.372612130168, 34.095680627302], - [-118.372634307776, 34.0956708636505], - [-118.372656625521, 34.0956615649336], - [-118.37267922354, 34.0956524983118], - [-118.372701821559, 34.0956438973688], - [-118.372724840889, 34.0956355285211], - [-118.372747860218, 34.0956276246085], - [-118.37277116072, 34.0956199527913], - [-118.37279460046, 34.0956125138135], - [-118.372819304131, 34.095605307675], - [-118.372841762911, 34.0955990306644], - [-118.372865623962, 34.0955927543973], - [-118.372889626048, 34.0955867102259], - [-118.372913627235, 34.0955811309904], - [-118.375186364904, 34.095070396542], - [-118.3752489667, 34.0950562162698], - [-118.376589421089, 34.0947549351369], - [-118.379043927465, 34.0931199544845], - [-118.379073403885, 34.0931004265895], - [-118.379119442543, 34.0930711347385], - [-118.379166182786, 34.0930423078276], - [-118.379213344338, 34.0930144108078], - [-118.379261207475, 34.0929869787292], - [-118.379309632058, 34.0929602444399], - [-118.379358477952, 34.0929342071963], - [-118.379481154582, 34.0928721358634], - [-118.379500103645, 34.0928633017903], - [-118.379551054291, 34.0928400542252], - [-118.381193146783, 34.092109147523], - [-118.381193427955, 34.0926312908046], - [-118.381195252434, 34.0933277868026], - [-118.382195049497, 34.0933294144949], - [-118.382223542261, 34.0933296465973], - [-118.383381527256, 34.093331506392], - [-118.383382510013, 34.0937288044668], - [-118.384390587747, 34.0937271767821], - [-118.384389886163, 34.0935623534619], - [-118.384555091733, 34.0935642132515], - [-118.384967334007, 34.0935651431463], - [-118.384970843725, 34.0935649103006], - [-118.384973369788, 34.0935646781989], - [-118.384976036886, 34.0935644453532], - [-118.384979405568, 34.0935639804059], - [-118.384982634113, 34.0935630505111], - [-118.384985160176, 34.0935623534619], - [-118.384987546101, 34.0935616556689], - [-118.384989932925, 34.0935607257741], - [-118.384992880297, 34.0935593309318], - [-118.384995126085, 34.0935581689353], - [-118.384997933321, 34.0935563091455], - [-118.385000600419, 34.0935546814576], - [-118.385002424897, 34.0935530545135], - [-118.385004810822, 34.093550961878], - [-118.385007056611, 34.0935486371406], - [-118.385009022125, 34.0935463124032], - [-118.385010706466, 34.0935439876657], - [-118.385012390807, 34.0935414308264], - [-118.385013372665, 34.0935393381906], - [-118.385014636595, 34.0935367813512], - [-118.385015478316, 34.0935339916659], - [-118.385016039764, 34.0935318990299], - [-118.385016601211, 34.0935288772426], - [-118.38501702252, 34.0935260875571], - [-118.38501702252, 34.0935239949209], - [-118.385015619352, 34.0931794680416], - [-118.385459302661, 34.0931801658379], - [-118.38545719701, 34.0927679867329], - [-118.385685144513, 34.0927684516846], - [-118.385688232921, 34.0927751938568], - [-118.385694689113, 34.0927884446079], - [-118.38570128634, 34.0928016961008], - [-118.385708303979, 34.0928149468477], - [-118.385715462654, 34.0928279654888], - [-118.38572304174, 34.0928409841279], - [-118.385730761861, 34.0928537706614], - [-118.385738762257, 34.0928665564489], - [-118.385747886446, 34.0928802728807], - [-118.385812311821, 34.0928437738361], - [-118.38581666326, 34.0928412169756], - [-118.385820874563, 34.0928386593711], - [-118.385825084966, 34.0928361025105], - [-118.385829156131, 34.0928333128021], - [-118.385833366535, 34.0928307551974], - [-118.385837296664, 34.0928279654888], - [-118.385841366931, 34.0928251757802], - [-118.385845156923, 34.0928221539677], - [-118.385849087052, 34.0928191314112], - [-118.385852877045, 34.0928161095985], - [-118.385856526001, 34.0928130870417], - [-118.385860175856, 34.0928100652288], - [-118.385863824813, 34.092806810568], - [-118.385867334531, 34.0928035559071], - [-118.38587084335, 34.0928003012461], - [-118.385874212033, 34.0927970465849], - [-118.385877439679, 34.0927935590759], - [-118.385880668225, 34.0927900723106], - [-118.38588389677, 34.0927868176491], - [-118.385886985178, 34.0927830980357], - [-118.385890072687, 34.0927796105261], - [-118.38589302006, 34.0927761237603], - [-118.385895827295, 34.0927724041465], - [-118.38589863453, 34.0927686845325], - [-118.385901441765, 34.0927649649183], - [-118.385903968726, 34.092761245304], - [-118.385906494789, 34.0927572928417], - [-118.38590902175, 34.092753573227], - [-118.385911407675, 34.0927496215083], - [-118.385913793601, 34.0927459018933], - [-118.385916039389, 34.0927419494302], - [-118.38591814504, 34.092737764863], - [-118.385920250691, 34.0927338131435], - [-118.385922215306, 34.0927298606798], - [-118.385924039785, 34.092725676112], - [-118.385925865161, 34.0927217243919], - [-118.38592768964, 34.0927175398237], - [-118.385929233844, 34.0927133552552], - [-118.385930778048, 34.0927091706866], - [-118.385932321353, 34.0927049861177], - [-118.38593372542, 34.0927008015486], - [-118.385934988451, 34.0926966169794], - [-118.385936251483, 34.0926924324099], - [-118.38593723424, 34.0926882478402], - [-118.385938357134, 34.0926838304222], - [-118.385939198855, 34.0926796458521], - [-118.385940041475, 34.0926752291776], - [-118.385940883196, 34.0926710446071], - [-118.385941725816, 34.0926652323312], - [-118.385949866349, 34.0926047885073], - [-118.385950428695, 34.0926015338386], - [-118.385950989243, 34.0925985120181], - [-118.38595155069, 34.0925952573492], - [-118.385952253173, 34.0925922347846], - [-118.385952954757, 34.0925892129638], - [-118.385953796479, 34.0925859582945], - [-118.385954779235, 34.0925829357295], - [-118.385955621855, 34.0925799139084], - [-118.385956604612, 34.0925768913432], - [-118.385957726608, 34.0925738695219], - [-118.385958849502, 34.0925708469564], - [-118.385960113432, 34.0925678251349], - [-118.385961376463, 34.0925648025693], - [-118.38596278053, 34.0925615478991], - [-118.385965446729, 34.0925559684641], - [-118.385966990933, 34.092552946642], - [-118.385968535137, 34.0925501569243], - [-118.385970219479, 34.0925473672065], - [-118.38597190382, 34.0925445774886], - [-118.385973728298, 34.0925417877707], - [-118.385975552776, 34.0925389980526], - [-118.385977377255, 34.0925362083344], - [-118.385979342769, 34.0925334186162], - [-118.385981307384, 34.0925306288978], - [-118.385983272898, 34.092528071284], - [-118.385985378549, 34.0925252815655], - [-118.385987483302, 34.0925227246954], - [-118.38598972909, 34.0925201670812], - [-118.385991974878, 34.092517610211], - [-118.385994361702, 34.0925150525968], - [-118.385996747627, 34.092512727831], - [-118.385999133553, 34.0925101709605], - [-118.386001519478, 34.0925078461946], - [-118.386004046439, 34.0925055214286], - [-118.386006713537, 34.0925031966626], - [-118.3860092396, 34.0925008718964], - [-118.386011906698, 34.0924985471303], - [-118.386014713933, 34.092496222364], - [-118.386017381031, 34.0924941297024], - [-118.386020188266, 34.0924920377846], - [-118.386022995501, 34.0924899451229], - [-118.386025942874, 34.092487853205], - [-118.386028890246, 34.0924857605432], - [-118.386031837619, 34.0924839007299], - [-118.38603478589, 34.0924818088119], - [-118.386037873399, 34.0924799489986], - [-118.386040961807, 34.0924780891852], - [-118.38604390918, 34.0924759965231], - [-118.386056120878, 34.0924699521291], - [-118.386059629697, 34.0924683251642], - [-118.386052471023, 34.0910409061687], - [-118.388736889518, 34.091056481997], - [-118.389653592418, 34.0910560170359], - [-118.391917908381, 34.0910541571912], - [-118.392691582419, 34.0910534601214], - [-118.395856319317, 34.0910595046165], - [-118.395860670757, 34.0933863703168], - [-118.395861933788, 34.0938050556558], - [-118.39586488116, 34.0943548522202], - [-118.395926078889, 34.0950183233452], - [-118.395954432414, 34.0950115820956], - [-118.395986434896, 34.0950092573983], - [-118.396015348971, 34.0950113492539], - [-118.39604356146, 34.0950178584058], - [-118.396069949472, 34.095028087072], - [-118.396089179707, 34.0950390135179], - [-118.396108409044, 34.0950538915738], - [-118.396124691009, 34.0950708614811], - [-118.396137464154, 34.0950899239826], - [-118.396146587444, 34.0951103812964], - [-118.396166097953, 34.0951678016024], - [-118.396247928187, 34.0951406023192], - [-118.39646071662, 34.0950699316029], - [-118.396516299879, 34.0950710943226], - [-118.396517844083, 34.0959626100003], - [-118.396518265392, 34.0961669487124], - [-118.396518265392, 34.0961871729275], - [-118.39542386495, 34.0961783392005], - [-118.392744779752, 34.0983607066991], - [-118.392635016404, 34.0984509013328], - [-118.392569608272, 34.0985041350903], - [-118.392601189444, 34.0986703435452], - [-118.392115818916, 34.099065291974], - [-118.392096168269, 34.0990592480506], - [-118.392081289473, 34.1035074589323], - [-118.39207581514, 34.1050913337556], - [-118.392083956571, 34.1050913337556], - [-118.392112028924, 34.1060852500419], - [-118.392117362222, 34.1060859469878], - [-118.392121853798, 34.1060866446776], - [-118.392126345375, 34.1060875744348], - [-118.392130836951, 34.1060882713808], - [-118.392135328527, 34.106089201138], - [-118.392139820104, 34.1060901308951], - [-118.39214431168, 34.1060910606523], - [-118.392148663119, 34.1060922232206], - [-118.392153154696, 34.1060933850451], - [-118.392157506135, 34.1060945476134], - [-118.392161857574, 34.1060957094378], - [-118.392166209014, 34.1060971040734], - [-118.392170559555, 34.106098498709], - [-118.392174770857, 34.1060998933446], - [-118.39217898126, 34.1061015207913], - [-118.392183192562, 34.1061031474942], - [-118.392187402966, 34.1061047749408], - [-118.392191473233, 34.1061064016436], - [-118.392195684535, 34.1061082611575], - [-118.392199614664, 34.1061098886041], - [-118.392203684931, 34.1061117481179], - [-118.392207755197, 34.1061138396991], - [-118.392211686225, 34.1061156992128], - [-118.392215475319, 34.1061177915376], - [-118.392219405448, 34.1061198831186], - [-118.39222319544, 34.1061222075106], - [-118.392226985432, 34.1061242998353], - [-118.392230634389, 34.1061266242272], - [-118.392234284244, 34.106128948619], - [-118.392237933201, 34.1061312730108], - [-118.392241583056, 34.1061335974025], - [-118.392245091875, 34.1061361538614], - [-118.392248600695, 34.106138711064], - [-118.392251969377, 34.1061412675228], - [-118.392255338059, 34.1061438247253], - [-118.392258566605, 34.1061466139949], - [-118.39226179515, 34.1061494032644], - [-118.392265022796, 34.1061521925339], - [-118.392268111204, 34.1061549818032], - [-118.392271198714, 34.1061577710725], - [-118.392274287122, 34.1061607924088], - [-118.392277234494, 34.1061635816779], - [-118.39228004173, 34.1061666037579], - [-118.392282848965, 34.1061696250939], - [-118.3922856562, 34.1061726471737], - [-118.392288323298, 34.1061759013204], - [-118.392290990396, 34.1061789226561], - [-118.392293516459, 34.1061821768027], - [-118.39229604342, 34.1061854309491], - [-118.392298429345, 34.1061886850954], - [-118.392300815271, 34.1061919392415], - [-118.392303061059, 34.1061954261985], - [-118.392305306847, 34.1061986803444], - [-118.392307552635, 34.1062021665572], - [-118.392309518149, 34.1062054207029], - [-118.3923116238, 34.1062089076593], - [-118.392313448279, 34.1062123938717], - [-118.392315412894, 34.1062158808278], - [-118.392317097235, 34.1062195998508], - [-118.392318781576, 34.1062230860628], - [-118.392320465918, 34.1062265730184], - [-118.392322010122, 34.1062302920409], - [-118.392323554325, 34.1062337782525], - [-118.392324957494, 34.1062374972747], - [-118.392326220525, 34.1062412162967], - [-118.392327484455, 34.1062449353186], - [-118.392328607349, 34.1062486543403], - [-118.392329730243, 34.1062523733618], - [-118.392330853137, 34.1062560923832], - [-118.392331694859, 34.1062598114044], - [-118.392332537478, 34.1062635304255], - [-118.3923333792, 34.1062674822571], - [-118.392334080784, 34.1062712012778], - [-118.392334642231, 34.1062749202983], - [-118.392335203678, 34.1062788713856], - [-118.392335624988, 34.1062825904058], - [-118.392336046298, 34.1062865422365], - [-118.392336326572, 34.1062902612564], - [-118.392336607745, 34.1062939802761], - [-118.392336747882, 34.1062979313625], - [-118.392336747882, 34.1063016503818], - [-118.392336747882, 34.1063056022116], - [-118.392336607745, 34.1063095532975], - [-118.392336467608, 34.1063132723163], - [-118.392336186435, 34.106316991335], - [-118.392335906161, 34.1063209431641], - [-118.392335484851, 34.1063246621825], - [-118.392334923404, 34.1063286132674], - [-118.392334361957, 34.1063323322854], - [-118.392333660372, 34.1063362841138], - [-118.391906819577, 34.1066324103856], - [-118.391328108701, 34.1074229272685], - [-118.391373024465, 34.1076704712192], - [-118.39113216368, 34.1082641079864], - [-118.390803857291, 34.1088412375651], - [-118.390586015835, 34.1092077819788], - [-118.390851440154, 34.1107169408196], - [-118.391152515911, 34.1124278159075], - [-118.392313026969, 34.1124364151446], - [-118.392845138637, 34.1124331612386], - [-118.394844451592, 34.1124187510821], - [-118.394983129014, 34.1124182862383], - [-118.396254104105, 34.1124189838759], - [-118.39659602536, 34.1124189838759], - [-118.396591814956, 34.1116952189884], - [-118.396591533784, 34.1116412966295], - [-118.397693233936, 34.1115650615116], - [-118.398090878587, 34.1114665135745], - [-118.398523192817, 34.1114790644974], - [-118.399201560994, 34.1114683729706], - [-118.399738725686, 34.111039083439], - [-118.400063943667, 34.1109161305316], - [-118.398684187984, 34.1087566323736], - [-118.399281567648, 34.1087545408578], - [-118.401050125864, 34.1087491945505], - [-118.401049283244, 34.1086780703343], - [-118.400992015645, 34.1032301495721], - [-118.400992296817, 34.1029856148234], - [-118.400972364998, 34.1011199680881], - [-118.400971101966, 34.1009412119599], - [-118.400968294731, 34.1006664515576], - [-118.400946959743, 34.0985241266588], - [-118.400946819606, 34.0985190125371], - [-118.400943871335, 34.0982772537032], - [-118.400940923963, 34.0979934182806], - [-118.400923940414, 34.0963036389308], - [-118.400911728716, 34.0951436247928], - [-118.400900078465, 34.0939987050984], - [-118.401238070489, 34.0938794469935], - [-118.401285652453, 34.0938908377894], - [-118.401286495073, 34.0938887459062], - [-118.401287476931, 34.0938861883333], - [-118.401289161273, 34.0938827016131], - [-118.401290425202, 34.0938803768849], - [-118.401291688233, 34.0938780521566], - [-118.401293934022, 34.0938745646921], - [-118.401296319947, 34.0938713100722], - [-118.401298004288, 34.0938692181885], - [-118.401299828767, 34.0938671255608], - [-118.401302777037, 34.0938641037855], - [-118.401305864547, 34.0938610812662], - [-118.401309093092, 34.0938582915916], - [-118.401312461774, 34.0938557347617], - [-118.4013148477, 34.0938541070795], - [-118.401317233625, 34.0938524801412], - [-118.401321023617, 34.0938501554121], - [-118.401323690715, 34.0938487605747], - [-118.401326217676, 34.0938473657372], - [-118.40133042808, 34.0938455059538], - [-118.401334498347, 34.0938438782714], - [-118.401338849786, 34.0938424834338], - [-118.401343201225, 34.0938410885962], - [-118.401346148598, 34.0938403915494], - [-118.401350640174, 34.0938394616576], - [-118.401353727684, 34.0938387638669], - [-118.401356675954, 34.093838298921], - [-118.401361307668, 34.0938378339751], - [-118.401365939382, 34.0938376018741], - [-118.40136902779, 34.0938373690292], - [-118.401373659503, 34.0938373690292], - [-118.401376747013, 34.0938376018741], - [-118.401379835421, 34.0938378339751], - [-118.401384467134, 34.093838298921], - [-118.401387555542, 34.0938387638669], - [-118.401390643052, 34.0938392288128], - [-118.401393590424, 34.0938399266035], - [-118.401398082001, 34.0938408564953], - [-118.40140243344, 34.0938422513329], - [-118.401406784879, 34.0938436461704], - [-118.401410996181, 34.0938452731089], - [-118.401415066448, 34.0938471328923], - [-118.401418996577, 34.0938492255205], - [-118.401422785671, 34.0938513174047], - [-118.401426575663, 34.0938536421337], - [-118.401430084483, 34.0938561997075], - [-118.401432330271, 34.0938580594907], - [-118.401434576059, 34.0938596864289], - [-118.401437664467, 34.0938627089483], - [-118.40144061184, 34.0938654986227], - [-118.401443419075, 34.0938687532428], - [-118.401446086173, 34.0938720078627], - [-118.401448472098, 34.0938752624825], - [-118.401450577749, 34.0938787492031], - [-118.401452542365, 34.0938822366674], - [-118.401454226706, 34.0938857233877], - [-118.40145577091, 34.0938894429526], - [-118.401551778356, 34.094147255268], - [-118.401553602834, 34.0941521368098], - [-118.401554866764, 34.0941549264747], - [-118.401556269932, 34.0941579489836], - [-118.401556971517, 34.0941593438159], - [-118.401558515721, 34.0941623655807], - [-118.401560200062, 34.0941651552453], - [-118.40156202454, 34.0941679449097], - [-118.401563990054, 34.0941707345741], - [-118.401565954669, 34.0941732921384], - [-118.40156806032, 34.0941760818026], - [-118.401570446246, 34.0941786386227], - [-118.401571570038, 34.0941798013547], - [-118.401573955964, 34.0941821260747], - [-118.401576482026, 34.0941846828947], - [-118.401579008987, 34.0941870076145], - [-118.401581816222, 34.0941891002343], - [-118.401584623458, 34.0941911921101], - [-118.401586026626, 34.0941923548419], - [-118.401588973999, 34.0941944467177], - [-118.401591922269, 34.0941963064934], - [-118.401595009779, 34.094198166269], - [-118.401598238324, 34.0941997939446], - [-118.401599782528, 34.0942007238324], - [-118.401603010175, 34.0942023507641], - [-118.401606379756, 34.0942039784396], - [-118.401608064097, 34.0942046754834], - [-118.401611431881, 34.094206070315], - [-118.401614941599, 34.0942072330466], - [-118.401618450418, 34.0942083950343], - [-118.401620274896, 34.094209092822], - [-118.401623924751, 34.0942100227097], - [-118.401627573708, 34.0942109525974], - [-118.401631223563, 34.0942116496412], - [-118.40163487252, 34.0942123474289], - [-118.401638662512, 34.0942130444727], - [-118.401642452504, 34.0942135094165], - [-118.401646241598, 34.0942139743603], - [-118.40165003159, 34.0942142072042], - [-118.401651856967, 34.0942142072042], - [-118.401655646061, 34.0942142072042], - [-118.40165957619, 34.0942142072042], - [-118.401663366182, 34.0942142072042], - [-118.401665190661, 34.0942139743603], - [-118.401668980653, 34.0942137422604], - [-118.401672770645, 34.0942132773166], - [-118.401676559739, 34.0942128123727], - [-118.401678385115, 34.0942125795289], - [-118.401682175108, 34.0942118824851], - [-118.401685824064, 34.0942111846974], - [-118.401689473021, 34.0942102548097], - [-118.401693122876, 34.094209324922], - [-118.401696631696, 34.0942081629344], - [-118.401699860241, 34.0942070002028], - [-118.401704632091, 34.0942053732712], - [-118.401709404841, 34.0942035134957], - [-118.401714176691, 34.0942016537202], - [-118.40171894944, 34.0941995611007], - [-118.401723581154, 34.0941977013251], - [-118.401728212868, 34.0941956094494], - [-118.40173284548, 34.0941932847298], - [-118.401737477193, 34.0941911921101], - [-118.40174196877, 34.0941888673904], - [-118.401746320209, 34.0941865426706], - [-118.401750811785, 34.0941839858507], - [-118.401755162326, 34.0941814282867], - [-118.401759373628, 34.0941788714667], - [-118.401763584032, 34.0941763139026], - [-118.401767795334, 34.0941737570824], - [-118.401772005738, 34.0941709674181], - [-118.401776076903, 34.0941681777537], - [-118.401780007032, 34.0941653880893], - [-118.401783937161, 34.0941623655807], - [-118.401787867291, 34.0941593438159], - [-118.401791656385, 34.0941563213071], - [-118.401795446377, 34.0941532995422], - [-118.401799236369, 34.0941502770332], - [-118.401802745188, 34.0941470224239], - [-118.401806395043, 34.0941437678146], - [-118.401809903863, 34.0941405132051], - [-118.401813272545, 34.0941370264953], - [-118.401816641228, 34.0941337718855], - [-118.401819869773, 34.0941302844315], - [-118.401823098318, 34.0941267977214], - [-118.401826325965, 34.0941233102671], - [-118.401830115957, 34.0941186608238], - [-118.401833063329, 34.0941151741131], - [-118.401836010702, 34.0941114545581], - [-118.401838817937, 34.094107735003], - [-118.401841485035, 34.0941040154477], - [-118.401844152133, 34.0941002958922], - [-118.401846678196, 34.0940963434924], - [-118.401849205157, 34.0940923918363], - [-118.401851591082, 34.0940886722803], - [-118.401853977007, 34.09408471988], - [-118.401857626862, 34.094077978556], - [-118.401918403281, 34.0939645320059], - [-118.401921210517, 34.0939594176087], - [-118.401925140646, 34.0939524434301], - [-118.401929211811, 34.093945469251], - [-118.401933562352, 34.0939384950713], - [-118.401937913791, 34.0939317529918], - [-118.401942545505, 34.0939250116556], - [-118.401947178116, 34.093918269575], - [-118.401952090104, 34.0939115282378], - [-118.401957002991, 34.0939050190008], - [-118.401962196151, 34.0938985097633], - [-118.401967389312, 34.0938920005253], - [-118.401972863645, 34.0938857233877], - [-118.401978337979, 34.0938794469935], - [-118.401983952449, 34.0938734026998], - [-118.401989707057, 34.0938671255608], - [-118.4019956027, 34.0938610812662], - [-118.402001778618, 34.093855269816], - [-118.402007814398, 34.0938494576214], - [-118.402014130453, 34.0938436461704], - [-118.402020586645, 34.09383806682], - [-118.402027043735, 34.0938324874692], - [-118.402033640963, 34.0938269081181], - [-118.402040377429, 34.0938215608676], - [-118.402047255829, 34.0938162143607], - [-118.402054273468, 34.0938110999545], - [-118.402061292005, 34.093805985548], - [-118.40206845068, 34.0938011032422], - [-118.402075748593, 34.0937962216801], - [-118.40208318844, 34.0937915722189], - [-118.402090627389, 34.0937869227574], - [-118.402098206475, 34.0937822732956], - [-118.402105926597, 34.0937778559347], - [-118.402113646718, 34.0937734393175], - [-118.402121506977, 34.0937692548013], - [-118.402129507373, 34.093765302386], - [-118.402137507769, 34.0937613507144], - [-118.4021456492, 34.0937573982988], - [-118.402153789733, 34.0937536787281], - [-118.402162071302, 34.0937499591572], - [-118.402170352871, 34.0937464724314], - [-118.402178774577, 34.0937432178066], - [-118.402187336419, 34.0937399631817], - [-118.402195758125, 34.0937367085567], - [-118.402204461004, 34.0937336860328], - [-118.402213162984, 34.093730896354], - [-118.402221865862, 34.0937281066751], - [-118.402230708878, 34.0937255498414], - [-118.402239410858, 34.0937229922636], - [-118.402248394011, 34.093720667531], - [-118.402257377164, 34.0937183427983], - [-118.402266360317, 34.0937162509108], - [-118.402270711756, 34.0937153210177], - [-118.402276326226, 34.0937143911246], - [-118.402281940697, 34.0937134612315], - [-118.402284747932, 34.0937129962849], - [-118.402287555168, 34.0937127634396], - [-118.402293169638, 34.0937120663918], - [-118.402298924246, 34.0937118335465], - [-118.402301731481, 34.0937116014452], - [-118.402307486089, 34.0937116014452], - [-118.402313240696, 34.0937116014452], - [-118.402313802143, 34.0937095088136], - [-118.402314504626, 34.0937071840806], - [-118.40231562752, 34.0937041622996], - [-118.402317030689, 34.0937009076732], - [-118.402318574893, 34.093697885148], - [-118.402320399371, 34.0936950954681], - [-118.402321663301, 34.09369300358], - [-118.402322926332, 34.0936911437933], - [-118.40232517212, 34.0936883541131], - [-118.402327417908, 34.0936857965342], - [-118.402329943971, 34.0936830068538], - [-118.402332611069, 34.0936806821201], - [-118.402334575684, 34.0936790551784], - [-118.402336401061, 34.0936776603382], - [-118.402339488571, 34.0936753356043], - [-118.402342576979, 34.0936732429718], - [-118.402345805524, 34.0936713831846], - [-118.402349173308, 34.0936697562428], - [-118.402351560132, 34.0936685935038], - [-118.40235380592, 34.0936676636101], - [-118.402357454876, 34.0936662687697], - [-118.402361104731, 34.0936648739292], - [-118.402364893825, 34.0936639440355], - [-118.402368683817, 34.0936630141418], - [-118.402372613947, 34.0936623170935], - [-118.402376544076, 34.0936616193013], - [-118.402380474206, 34.0936613871998], - [-118.402384404335, 34.0936611543544], - [-118.402388474601, 34.093660922253], - [-118.402392404731, 34.0936611543544], - [-118.40239633486, 34.0936613871998], - [-118.402400265888, 34.0936618521467], - [-118.402402932088, 34.0936623170935], - [-118.40240672208, 34.0936630141418], - [-118.402410652209, 34.0936639440355], - [-118.402414301166, 34.0936651067746], - [-118.402416828127, 34.0936658038229], - [-118.402420477083, 34.0936671986633], - [-118.402422863907, 34.093668128557], - [-118.402425109695, 34.093669291296], - [-118.402427495621, 34.093670453291], - [-118.402430724166, 34.0936720809768], - [-118.402432969954, 34.0936734758172], - [-118.402436057464, 34.0936755677057], - [-118.402437461531, 34.0936758005511], - [-118.402443777585, 34.0936816120136], - [-118.402464410989, 34.0936999777799], - [-118.403061089069, 34.0942421038286], - [-118.403088458939, 34.0942653510087], - [-118.40316860573, 34.0943390441552], - [-118.403177027436, 34.0943467160885], - [-118.403236119514, 34.0944011144125], - [-118.403777214335, 34.0948927896139], - [-118.404309184968, 34.0953760938298], - [-118.405332001361, 34.096305265822], - [-118.405422675509, 34.1014656252768], - [-118.406114238141, 34.1014756203399], - [-118.408706438284, 34.1015123477422], - [-118.408709526692, 34.1013682274646], - [-118.408678225794, 34.1013677625601], - [-118.408636398438, 34.100986539995], - [-118.408682437096, 34.1007589682732], - [-118.408762582989, 34.1006157765298], - [-118.408801884283, 34.1005253513768], - [-118.408804551381, 34.1005193075577], - [-118.408807358616, 34.1005132637381], - [-118.408810305989, 34.1005072199181], - [-118.408813394397, 34.1005014089243], - [-118.408816622942, 34.1004953651034], - [-118.408819850589, 34.1004895533649], - [-118.408823360307, 34.1004839744525], - [-118.408826869126, 34.1004781634571], - [-118.408830518083, 34.100472584544], - [-118.408834308075, 34.1004670056306], - [-118.408838238204, 34.1004614267168], - [-118.408842308471, 34.1004558478026], - [-118.408846379636, 34.1004505009709], - [-118.408850730177, 34.100444922056], - [-118.408855081616, 34.1004398080504], - [-118.408859573192, 34.1004344619615], - [-118.408864064769, 34.1004293479552], - [-118.408868837518, 34.1004242339487], - [-118.408873609369, 34.1004191199418], - [-118.408878522255, 34.1004142380175], - [-118.408883435141, 34.1004093568368], - [-118.408888628302, 34.100404474912], - [-118.408893821462, 34.1003995937308], - [-118.408899155659, 34.1003949446323], - [-118.408904488956, 34.1003902955335], - [-118.40890996329, 34.1003858785175], - [-118.40891557776, 34.1003814622451], - [-118.408921332368, 34.1003770452286], - [-118.408927086976, 34.1003728610389], - [-118.408932982619, 34.1003686768489], - [-118.409120365797, 34.1002371072141], - [-118.409123874617, 34.100234550577], - [-118.409127243299, 34.1002317611125], - [-118.409130471844, 34.1002292037313], - [-118.409132156185, 34.100227808999], - [-118.409135243695, 34.1002247874508], - [-118.409136787899, 34.1002233927184], - [-118.409139595134, 34.1002203704262], - [-118.40914240237, 34.1002173488777], - [-118.409145069468, 34.1002140945017], - [-118.40914759553, 34.1002108401257], - [-118.409149981456, 34.1002075857495], - [-118.409151105248, 34.1002059581894], - [-118.409152227244, 34.1002040985458], - [-118.409154332895, 34.1002008441693], - [-118.409156298409, 34.1001971248818], - [-118.40915798275, 34.1001936384215], - [-118.409159667091, 34.1001901512172], - [-118.409161210397, 34.1001864319292], - [-118.409161912879, 34.1001845722852], - [-118.409163175911, 34.1001808529969], - [-118.409164298805, 34.1001771337085], - [-118.409165281562, 34.1001734144199], - [-118.409165701973, 34.100171322692], - [-118.409166404456, 34.1001676034032], - [-118.409166965903, 34.1001638841142], - [-118.409167246177, 34.1001599319975], - [-118.409167386314, 34.1001580723529], - [-118.40916752735, 34.1001559806245], - [-118.40916752735, 34.100152261335], - [-118.40916752735, 34.1001483092178], - [-118.409167386314, 34.1001464495729], - [-118.409167246177, 34.100144589928], - [-118.409166824867, 34.1001406385543], - [-118.409166264318, 34.1001369192641], - [-118.409165843009, 34.1001348267914], - [-118.409165561836, 34.1001329671462], - [-118.409164579977, 34.1001292478557], - [-118.40916359722, 34.100125528565], - [-118.409162333291, 34.1001218092742], - [-118.409160930122, 34.1001180899831], - [-118.409160228538, 34.1001162303376], - [-118.409159526056, 34.100114370692], - [-118.409157841714, 34.1001108842283], - [-118.409156859856, 34.1001090245826], - [-118.409155034479, 34.1001055373748], - [-118.409112926848, 34.1000318492491], - [-118.409104504244, 34.1000167392404], - [-118.409096363711, 34.100001397145], - [-118.409088503453, 34.0999858229626], - [-118.409081064504, 34.0999702480334], - [-118.409073905829, 34.0999544410172], - [-118.409067028327, 34.0999386339981], - [-118.409060571237, 34.099922826976], - [-118.40905439532, 34.0999067878666], - [-118.409048500575, 34.0998907480103], - [-118.409043026241, 34.0998747088948], - [-118.409037833081, 34.099858436948], - [-118.409033060332, 34.099842164998], - [-118.409028568755, 34.0998256602164], - [-118.409024498489, 34.0998091561756], - [-118.409020708496, 34.0997928842161], - [-118.409017339814, 34.0997761473402], - [-118.409014252304, 34.0997596425458], - [-118.409011445069, 34.0997429056634], - [-118.409009058245, 34.0997264016064], - [-118.40900709363, 34.0997096647174], - [-118.409005409289, 34.0996929278251], - [-118.40900400612, 34.0996761909295], - [-118.409003023363, 34.0996594540305], - [-118.409002461916, 34.0996424842993], - [-118.409002180744, 34.0996257473938], - [-118.409002180744, 34.0996090104848], - [-118.409002602053, 34.0995922735726], - [-118.409003443775, 34.0995755366571], - [-118.409004566669, 34.0995587997383], - [-118.409005970736, 34.0995418307308], - [-118.409007935351, 34.0995250938053], - [-118.409010041002, 34.0995085889619], - [-118.409012567963, 34.0994918520298], - [-118.409015515336, 34.0994753479238], - [-118.409018743881, 34.0994586109852], - [-118.4090222527, 34.0994421061288], - [-118.409093135166, 34.0991308452695], - [-118.40909425806, 34.0991252662676], - [-118.409095380954, 34.0991196872653], - [-118.409096363711, 34.0991141082627], - [-118.409097205433, 34.0991085292597], - [-118.409097907915, 34.0991027174259], - [-118.409098609499, 34.0990971384222], - [-118.409099030809, 34.0990915594181], - [-118.409099452119, 34.099085748327], - [-118.409099732394, 34.0990801693221], - [-118.409099872531, 34.0990745903169], - [-118.409099872531, 34.0990687784808], - [-118.409099732394, 34.0990631994748], - [-118.409099592256, 34.0990576204684], - [-118.409099311084, 34.099051809375], - [-118.409098749637, 34.0990462303679], - [-118.40909818819, 34.0990406513605], - [-118.409097486605, 34.099034839522], - [-118.409096785021, 34.0990292605138], - [-118.409095802264, 34.0990236815052], - [-118.409094819507, 34.0990181024963], - [-118.409093696613, 34.0990125234869], - [-118.409092293445, 34.0990069444772], - [-118.409091029515, 34.0990013654672], - [-118.409089486209, 34.0989957864567], - [-118.409087801868, 34.0989904402767], - [-118.409086117527, 34.0989848612655], - [-118.409084293049, 34.0989795143409], - [-118.409082327535, 34.098973935329], - [-118.409080221884, 34.0989685891476], - [-118.409077976096, 34.0989632422219], - [-118.409075730308, 34.0989578960398], - [-118.409073204245, 34.0989525491134], - [-118.409070677284, 34.0989474350176], - [-118.409068151221, 34.0989420888345], - [-118.409065343986, 34.098936974738], - [-118.409062536751, 34.0989318606413], - [-118.40905958848, 34.0989267465442], - [-118.409056500971, 34.0989216324468], - [-118.409053272425, 34.0989165183491], - [-118.40905004388, 34.0989116363382], - [-118.409047096508, 34.0989074520759], - [-118.408942948529, 34.0987582132547], - [-118.40893775447, 34.0987505424652], - [-118.408933965376, 34.0987447306068], - [-118.408930455658, 34.0987389194919], - [-118.408926946839, 34.0987331076327], - [-118.408923578156, 34.098727296517], - [-118.408920349611, 34.0987212525695], - [-118.408917262102, 34.0987152086215], - [-118.408914314729, 34.0987091646731], - [-118.408911367357, 34.0987031207242], - [-118.408908700259, 34.098697076775], - [-118.40890603316, 34.0986907999937], - [-118.408903647235, 34.0986847560435], - [-118.408901260411, 34.0986784800052], - [-118.408899155659, 34.0986722032226], - [-118.408897050008, 34.0986659271834], - [-118.408895084494, 34.0986596503998], - [-118.408893260015, 34.098653141528], - [-118.408891575674, 34.0986468654873], - [-118.40889003147, 34.0986403566145], - [-118.408888628302, 34.098634079829], - [-118.408887365271, 34.0986275709553], - [-118.408886242376, 34.098621062081], - [-118.40888525962, 34.098614786038], - [-118.408884417898, 34.0986082771627], - [-118.408883715416, 34.0986017682869], - [-118.408883153968, 34.0985952594107], - [-118.408882733557, 34.0985887505339], - [-118.408882312247, 34.0985822416566], - [-118.408882171212, 34.0985757327788], - [-118.408882171212, 34.0985689910687], - [-118.408882312247, 34.0985624821899], - [-118.408882452384, 34.0985559733106], - [-118.408882873694, 34.0985494644308], - [-118.408908700259, 34.0981661373922], - [-118.408908980533, 34.0981612553379], - [-118.40890954198, 34.0981559091052], - [-118.408910243564, 34.0981505621282], - [-118.408910946047, 34.0981452158947], - [-118.408911787768, 34.098139868917], - [-118.408912770525, 34.0981345226829], - [-118.408913893419, 34.0981291757045], - [-118.408915016313, 34.0981238294697], - [-118.408916279345, 34.0981184824907], - [-118.408917823549, 34.0981133683444], - [-118.408919227615, 34.0981080221086], - [-118.408920911058, 34.0981029079618], - [-118.408922595399, 34.0980975609814], - [-118.408924560913, 34.0980924468339], - [-118.408926526427, 34.0980873326861], - [-118.408928491043, 34.098082218538], - [-118.408930736831, 34.0980771043896], - [-118.408932982619, 34.0980719902408], - [-118.408935368544, 34.0980671089251], - [-118.408937895505, 34.0980619947757], - [-118.408940421568, 34.0980571127155], - [-118.408943228803, 34.0980522313989], - [-118.408946036038, 34.0980473493381], - [-118.408948843274, 34.0980424680209], - [-118.40912934895, 34.0977491012331], - [-118.409130752119, 34.0977467766111], - [-118.409133419217, 34.0977418945326], - [-118.409135946178, 34.0977370131978], - [-118.409138332103, 34.0977318990286], - [-118.409139454997, 34.0977295744061], - [-118.409141419613, 34.0977244602363], - [-118.409143103954, 34.0977193460663], - [-118.409143946574, 34.0977167886092], - [-118.409145489879, 34.0977114423483], - [-118.409146051326, 34.097708884891], - [-118.409146612773, 34.0977060953432], - [-118.409147735667, 34.0977009811721], - [-118.40914843815, 34.0976956349102], - [-118.409148858562, 34.097692845362], - [-118.409149279871, 34.0976874983558], - [-118.409149420907, 34.0976849416416], - [-118.409149561044, 34.0976821520931], - [-118.409149561044, 34.0976768050861], - [-118.409149279871, 34.0976714588227], - [-118.409148858562, 34.0976661118151], - [-118.409148578287, 34.0976635551003], - [-118.409147735667, 34.0976582080922], - [-118.409146753809, 34.0976528618276], - [-118.409145630915, 34.0976477476533], - [-118.40914492933, 34.097645190194], - [-118.409143385127, 34.0976398439286], - [-118.40914155975, 34.0976347297535], - [-118.409139595134, 34.097629615578], - [-118.40913847224, 34.0976270581182], - [-118.409136226452, 34.0976221767768], - [-118.409133699491, 34.0976172946912], - [-118.40913243646, 34.0976147379749], - [-118.409129629225, 34.0976098558889], - [-118.409126541715, 34.0976052066371], - [-118.40912331317, 34.0976005573851], - [-118.409119944487, 34.0975959081328], - [-118.409118260146, 34.0975938163411], - [-118.409114470154, 34.0975893991791], - [-118.409110680162, 34.0975852148516], - [-118.409106609895, 34.0975810305238], - [-118.409102399492, 34.0975768461957], - [-118.409098048052, 34.0975728947021], - [-118.409095802264, 34.0975710350007], - [-118.409091170551, 34.0975673155976], - [-118.408874732263, 34.0974004072149], - [-118.40886308291, 34.0973913407794], - [-118.408851432659, 34.0973818101603], - [-118.408839922545, 34.097372046705], - [-118.408828553467, 34.0973622832485], - [-118.408817464663, 34.0973522869557], - [-118.408806657032, 34.0973420585703], - [-118.408795989538, 34.0973318301837], - [-118.408785462181, 34.0973213697044], - [-118.408775215997, 34.0973106763885], - [-118.408765250087, 34.0972997502359], - [-118.408755425213, 34.0972888248257], - [-118.408745880613, 34.0972778986703], - [-118.408736616288, 34.0972667404218], - [-118.408727492998, 34.0972553500803], - [-118.408718649982, 34.0972439589933], - [-118.408710088139, 34.0972323358132], - [-118.408701666433, 34.0972204805397], - [-118.408653522124, 34.0971516712708], - [-118.408904068545, 34.0971242405331], - [-118.409066607017, 34.096575158982], - [-118.409386350665, 34.0962327363473], - [-118.409390842241, 34.0962320393202], - [-118.40939645761, 34.0962308766164], - [-118.409401931045, 34.0962297146565], - [-118.409404598143, 34.0962290168854], - [-118.409407265241, 34.0962283198582], - [-118.409412738676, 34.0962266922216], - [-118.409418072872, 34.0962250653289], - [-118.409423266033, 34.0962232055978], - [-118.409425792994, 34.0962222757322], - [-118.409430986155, 34.0962201831627], - [-118.409436039178, 34.096218091337], - [-118.409438425103, 34.096216928633], - [-118.409440952064, 34.0962157666729], - [-118.409445723915, 34.0962132091704], - [-118.409450496664, 34.0962106524117], - [-118.409455128378, 34.0962078628146], - [-118.409459619954, 34.0962048403789], - [-118.409461725605, 34.0962034455803], - [-118.409463971394, 34.0962018186871], - [-118.409468181797, 34.0961987962512], - [-118.409472252962, 34.0961955417208], - [-118.409474357715, 34.0961939148274], - [-118.409478147707, 34.0961904274583], - [-118.409481937699, 34.0961867079945], - [-118.409485586656, 34.0961832213691], - [-118.409487270997, 34.096181361637], - [-118.409490639679, 34.0961774093344], - [-118.409492183883, 34.0961755496022], - [-118.409495412428, 34.0961715980432], - [-118.409496815597, 34.0961695054724], - [-118.409499762969, 34.0961653210747], - [-118.409502008758, 34.0961620665429], - [-118.409504114409, 34.0961583470779], - [-118.409506079922, 34.0961546276127], - [-118.409508044538, 34.0961509081473], - [-118.409508887158, 34.0961490484146], - [-118.409510430463, 34.096145328949], - [-118.409511273083, 34.0961434692161], - [-118.409511974667, 34.0961413773886], - [-118.409513238597, 34.0961376579226], - [-118.409513799146, 34.096135565351], - [-118.40951492204, 34.0961316137901], - [-118.40951576466, 34.0961276614852], - [-118.409516185969, 34.0961258017519], - [-118.409516466244, 34.0961237099239], - [-118.409517027691, 34.0961197576186], - [-118.409517308864, 34.0961176657904], - [-118.409517589138, 34.0961137134848], - [-118.409517589138, 34.0961116216565], - [-118.409517730173, 34.0961076693506], - [-118.409517589138, 34.0961058096169], - [-118.409517449001, 34.096101625216], - [-118.409517308864, 34.0960997654822], - [-118.409516747416, 34.0960955810809], - [-118.409516466244, 34.096093721347], - [-118.40951576466, 34.0960897697841], - [-118.40951492204, 34.0960858174772], - [-118.409514360593, 34.0960837256482], - [-118.409513238597, 34.096079773341], - [-118.409512676252, 34.0960779136067], - [-118.409511273083, 34.0960739620432], - [-118.409510571499, 34.0960721023087], - [-118.409509728879, 34.0960702425743], - [-118.409508044538, 34.0960665231052], - [-118.40950622006, 34.096062803636], - [-118.409504254546, 34.0960590841666], - [-118.409503131652, 34.0960572244318], - [-118.409500886762, 34.0960537370571], - [-118.409498499938, 34.0960502504261], - [-118.409492885468, 34.0960425786473], - [-118.409484323625, 34.0960307232071], - [-118.409476042056, 34.0960186349265], - [-118.409467901523, 34.096006546644], - [-118.409460181401, 34.0959944583599], - [-118.409452602315, 34.095982137235], - [-118.409445443641, 34.0959698168523], - [-118.409438425103, 34.0959572636286], - [-118.409431687739, 34.0959447104031], - [-118.40942509141, 34.0959319243367], - [-118.409276448078, 34.095633435937], - [-118.409274342427, 34.0956292515127], - [-118.409272096639, 34.0956250670882], - [-118.409269710713, 34.0956208826635], - [-118.40926732389, 34.0956166982386], - [-118.409264797827, 34.0956127466535], - [-118.409262130729, 34.0956085622282], - [-118.409259463631, 34.0956046098988], - [-118.409256656396, 34.0956006583131], - [-118.409253850059, 34.0955967059833], - [-118.409250901788, 34.0955927543973], - [-118.409247954415, 34.0955888020672], - [-118.409244866906, 34.095585082577], - [-118.409241638361, 34.0955813630866], - [-118.409238409816, 34.095577643596], - [-118.409235041133, 34.0955739241053], - [-118.409231672451, 34.0955702046145], - [-118.409228163631, 34.0955667179636], - [-118.409224653914, 34.0955632305686], - [-118.409221004957, 34.0955597439175], - [-118.409217214965, 34.0955562565222], - [-118.409213566008, 34.0955527698708], - [-118.409209635879, 34.0955495153155], - [-118.409205705749, 34.09554626076], - [-118.409202337067, 34.0955432383008], - [-118.40945203997, 34.0948641961655], - [-118.409638581427, 34.0947084410394], - [-118.411483496442, 34.0935005154354], - [-118.411654316483, 34.0931104233845], - [-118.411779800348, 34.092261420645], - [-118.411952023558, 34.0922609556905], - [-118.412039890471, 34.0924120657832], - [-118.412044802459, 34.0924204349497], - [-118.412049855482, 34.0924285712665], - [-118.412055189678, 34.0924367083266], - [-118.412060522976, 34.0924446125371], - [-118.412066137447, 34.0924525167468], - [-118.412071892953, 34.0924604209559], - [-118.412077787698, 34.0924683251642], - [-118.412083963615, 34.0924759965231], - [-118.412090139533, 34.0924836686252], - [-118.412096596623, 34.0924911078781], - [-118.412103052815, 34.0924985471303], - [-118.41210979018, 34.0925057535332], - [-118.412116667681, 34.0925129606795], - [-118.412123686219, 34.0925201670812], - [-118.412130844893, 34.0925271413778], - [-118.412138143705, 34.0925341156738], - [-118.412145582654, 34.0925408578647], - [-118.41215316174, 34.092547599311], - [-118.412160881861, 34.0925541086524], - [-118.41216874212, 34.0925606179933], - [-118.412176742516, 34.0925671273336], - [-118.412184883947, 34.0925731717206], - [-118.412193165516, 34.0925794489556], - [-118.412201587222, 34.0925854933418], - [-118.412210149065, 34.0925913048791], - [-118.412218710908, 34.09259711716], - [-118.412227553923, 34.0926026965923], - [-118.412236396939, 34.0926080431758], - [-118.412245380092, 34.0926136226073], - [-118.412254503382, 34.0926187370859], - [-118.412263626672, 34.0926238515641], - [-118.412273031135, 34.092628966042], - [-118.412280329946, 34.0926326856622], - [-118.412350229655, 34.0926682548933], - [-118.412373530157, 34.0926803436524], - [-118.412396549486, 34.0926928973621], - [-118.412419288541, 34.0927056831739], - [-118.412441745525, 34.0927189346798], - [-118.412463923133, 34.0927324182876], - [-118.412485819568, 34.0927463668452], - [-118.412507435728, 34.0927603154004], - [-118.412528769818, 34.092774961009], - [-118.412549684394, 34.092789607359], - [-118.412570457935, 34.092804717914], - [-118.412590810167, 34.0928200613141], - [-118.412610882123, 34.0928356375591], - [-118.41263053277, 34.0928516780086], - [-118.412649902244, 34.0928679513026], - [-118.412668991444, 34.092884457441], - [-118.412687660232, 34.0929011956797], - [-118.412706046949, 34.0929181660186], - [-118.412724013255, 34.0929356020489], - [-118.412741558251, 34.0929530373317], - [-118.412758822972, 34.0929709383054], - [-118.412775666384, 34.0929890713787], - [-118.413128536306, 34.0933745145047], - [-118.413140185658, 34.0933868352652], - [-118.413152117082, 34.0933991567679], - [-118.413164327882, 34.0934114775248], - [-118.413176820752, 34.0934233340757], - [-118.413189452862, 34.0934351898811], - [-118.413202366144, 34.0934468135827], - [-118.413215560599, 34.0934582051807], - [-118.413228894293, 34.0934693639312], - [-118.413237877445, 34.0934765702515], - [-118.41343368233, 34.0936341874325], - [-118.413446595612, 34.0936446491114], - [-118.413484914149, 34.0936764975993], - [-118.413522672137, 34.0937088117657], - [-118.413559727642, 34.0937418229674], - [-118.413596080665, 34.0937752991024], - [-118.413631733002, 34.0938092401703], - [-118.413846345913, 34.0940173028965], - [-118.413855610239, 34.0940266017939], - [-118.413864593392, 34.0940359006904], - [-118.413873435509, 34.0940454324301], - [-118.413882138387, 34.0940549634249], - [-118.413890560093, 34.0940647272629], - [-118.413898700626, 34.0940744910997], - [-118.413906701921, 34.0940844877797], - [-118.413914562179, 34.0940944837145], - [-118.413922141265, 34.0941047124924], - [-118.413929580214, 34.094114941269], - [-118.413936738889, 34.0941254028884], - [-118.413943617289, 34.0941358637627], - [-118.413950353755, 34.0941465574797], - [-118.413956389536, 34.0941565541512], - [-118.413957232155, 34.0942688377132], - [-118.417339529337, 34.0942718602179], - [-118.418881684253, 34.0942737199919], - [-118.418885052936, 34.0942737199919], - [-118.418894176226, 34.0942741849354], - [-118.418913265426, 34.0942737199919], - [-118.419004500122, 34.0942732550484], - [-118.420988513869, 34.0942755797659], - [-118.421639231003, 34.0942765096528], - [-118.422702050273, 34.0942776716395], - [-118.422691804089, 34.0931971361208], - [-118.422691663952, 34.0931039140859], - [-118.42266836345, 34.090647780149], - [-118.422818269813, 34.0906473151856], - [-118.422951754075, 34.0906459202953], - [-118.42295764882, 34.0906452232222], - [-118.422963544463, 34.0906445254051], - [-118.422966491835, 34.0906440604416], - [-118.422972246443, 34.0906428984051], - [-118.422978142086, 34.0906417356245], - [-118.422983756557, 34.0906403407341], - [-118.422989371027, 34.090638713734], - [-118.422992178262, 34.0906380159169], - [-118.422994985498, 34.0906370859899], - [-118.423000459831, 34.0906352261361], - [-118.423005933266, 34.0906331341724], - [-118.423011267462, 34.0906310414647], - [-118.423015899176, 34.0906289495009], - [-118.423019127721, 34.0906273217566], - [-118.423024180744, 34.0906247648292], - [-118.423029233768, 34.0906222071577], - [-118.423031619693, 34.0906208122671], - [-118.423036392442, 34.0906177903759], - [-118.423038778368, 34.0906161626314], - [-118.423043410081, 34.0906131407401], - [-118.423047901658, 34.0906098859948], - [-118.42305211296, 34.0906063983957], - [-118.423056323364, 34.0906029115403], - [-118.42306039363, 34.0905994239409], - [-118.423062359144, 34.0905975640862], - [-118.423066149136, 34.0905936122667], - [-118.423067973615, 34.0905917524119], - [-118.423071622571, 34.0905877998483], - [-118.423073307811, 34.0905857078834], - [-118.423076535458, 34.0905815232096], - [-118.423079764003, 34.0905773385355], - [-118.423082711375, 34.0905731538613], - [-118.423085378473, 34.0905687363329], - [-118.423086781642, 34.0905664115137], - [-118.42360415555, 34.0905094537944], - [-118.423643316706, 34.0904606325428], - [-118.423646825526, 34.0904564478628], - [-118.423650054071, 34.0904527281471], - [-118.423653422753, 34.0904492405414], - [-118.423656931573, 34.0904459857899], - [-118.423660721565, 34.0904424989279], - [-118.423664510659, 34.0904394762864], - [-118.423666476173, 34.0904378492825], - [-118.423670406302, 34.0904348266408], - [-118.423674616706, 34.090431804743], - [-118.423678828008, 34.0904290149554], - [-118.423683319584, 34.090426457278], - [-118.423687811161, 34.0904239003445], - [-118.423690056949, 34.0904227375609], - [-118.423694688662, 34.0904204127377], - [-118.423699320376, 34.0904183207687], - [-118.423704233262, 34.0904162280557], - [-118.423709146149, 34.0904141360866], - [-118.423711672211, 34.0904134382676], - [-118.423716585098, 34.0904115784088], - [-118.423719112058, 34.0904108813337], - [-118.423724305219, 34.0904092535852], - [-118.42372949838, 34.0904080915454], - [-118.42373469154, 34.0904069287616], - [-118.423737358638, 34.0904062316865], - [-118.423742551799, 34.090405301757], - [-118.423748026132, 34.0904046039379], - [-118.423750693231, 34.0904041389732], - [-118.423756026528, 34.0904036740084], - [-118.423761360725, 34.0904032090437], - [-118.423766835058, 34.0904029769333], - [-118.423772308493, 34.0904029769333], - [-118.423778344273, 34.0904029769333], - [-118.423784520191, 34.0904032090437], - [-118.423790555971, 34.0904032090437], - [-118.423796591752, 34.0904036740084], - [-118.423802626634, 34.0904039068628], - [-118.423808662414, 34.0904043718275], - [-118.423814558057, 34.0904050689027], - [-118.423820592939, 34.0904057667218], - [-118.42382662872, 34.0904064637969], - [-118.423832524363, 34.090407161616], - [-118.423838560143, 34.0904080915454], - [-118.423844454888, 34.0904092535852], - [-118.423850349633, 34.0904101835147], - [-118.423856245276, 34.0904113462984], - [-118.423862140021, 34.0904127411925], - [-118.423868035665, 34.0904139032323], - [-118.423873790272, 34.0904152981263], - [-118.423879545778, 34.0904169258747], - [-118.423885300386, 34.0904185528791], - [-118.423891054994, 34.0904201806274], - [-118.423896669464, 34.0904218076317], - [-118.423902283935, 34.0904236674902], - [-118.423907898405, 34.0904257602031], - [-118.423913512876, 34.0904276200615], - [-118.423918987209, 34.0904297120303], - [-118.423924460644, 34.090431804743], - [-118.42392979484, 34.0904341295659], - [-118.423935269174, 34.0904364543888], - [-118.423940602471, 34.0904387792116], - [-118.423945795632, 34.0904413361446], - [-118.423950989691, 34.0904438938215], - [-118.423956182852, 34.0904464507544], - [-118.423961376012, 34.0904492405414], - [-118.423966429036, 34.0904520303284], - [-118.423971341922, 34.0904548201152], - [-118.423976254808, 34.0904578427562], - [-118.423981166796, 34.0904606325428], - [-118.423985939546, 34.0904638872938], - [-118.423988325471, 34.0904652821871], - [-118.423990711396, 34.0904669091905], - [-118.424110440654, 34.090453658076], - [-118.424115914987, 34.0904673741549], - [-118.424120265528, 34.0904787661539], - [-118.42412447683, 34.0904903902614], - [-118.424128265924, 34.0905017815133], - [-118.424131915779, 34.0905134056178], - [-118.424135284462, 34.0905250297206], - [-118.424138371971, 34.0905368866758], - [-118.424141320242, 34.0905485107754], - [-118.424143986442, 34.0905603669834], - [-118.424146373265, 34.0905722239336], - [-118.424148478018, 34.0905840801383], - [-118.424150303395, 34.0905959370852], - [-118.424157461171, 34.0906433633685], - [-118.425290742496, 34.0906401086244], - [-118.427053124794, 34.0906354589898], - [-118.427011857986, 34.0866655077639], - [-118.427004419037, 34.0859468695949], - [-118.426974100897, 34.0830490071586], - [-118.425824538506, 34.0830624923066], - [-118.425101253666, 34.0830713274023], - [-118.425093954855, 34.0830710952718], - [-118.425086656043, 34.0830710952718], - [-118.424326737906, 34.0830799303667], - [-118.42425894295, 34.083082022517], - [-118.424256978335, 34.0830815575121], - [-118.424254311237, 34.0830813253816], - [-118.424252346621, 34.0830810925071], - [-118.424249679523, 34.0830808603766], - [-118.422790758908, 34.0830980655585], - [-118.422789776151, 34.0830599351507], - [-118.422800023233, 34.0799389956846], - [-118.423006354576, 34.0799359734122], - [-118.423241039444, 34.0799324853733], - [-118.423840243586, 34.0797453140822], - [-118.423618331863, 34.0792561081792], - [-118.423489339178, 34.0788173546721], - [-118.423643737118, 34.0787687595651], - [-118.423468706673, 34.0783832499438], - [-118.423452564846, 34.0783472096803], - [-118.423446108654, 34.078332561576], - [-118.42344021301, 34.078317912725], - [-118.423434458403, 34.078303031728], - [-118.423428984069, 34.0782881507283], - [-118.423423931046, 34.0782730375824], - [-118.423419158297, 34.0782579236897], - [-118.42341466672, 34.0782428105383], - [-118.423410596454, 34.0782276966402], - [-118.423406666324, 34.0782123505956], - [-118.423403157505, 34.0781970045483], - [-118.423400069097, 34.0781816584981], - [-118.423397121725, 34.0781663124452], - [-118.423394595662, 34.0781509663894], - [-118.423392490011, 34.0781353881869], - [-118.423390524497, 34.0781198092375], - [-118.423388981191, 34.0781042310292], - [-118.423387717262, 34.0780888849622], - [-118.423368207651, 34.0778052147387], - [-118.423367224894, 34.0777921937876], - [-118.423365961862, 34.0777789399454], - [-118.423364277521, 34.0777659189903], - [-118.423362452144, 34.077752665888], - [-118.423360347392, 34.0777396449289], - [-118.423357820431, 34.0777266239677], - [-118.423355154231, 34.0777136030045], - [-118.42335220596, 34.0777008141845], - [-118.423348978314, 34.0776877932174], - [-118.423345468596, 34.0776750051376], - [-118.423341679502, 34.0776622163118], - [-118.423337608337, 34.0776494282281], - [-118.423333397933, 34.0776366393985], - [-118.423328765321, 34.0776240834564], - [-118.423323993471, 34.0776115275125], - [-118.42331880031, 34.0775989715667], - [-118.423313466114, 34.0775866485086], - [-118.423307851643, 34.0775743247046], - [-118.423301956898, 34.077562001643], - [-118.423295780981, 34.0775499107252], - [-118.423289464028, 34.0775378198056], - [-118.423282867699, 34.0775257288844], - [-118.423275989298, 34.0775138701071], - [-118.423268831522, 34.077502244218], - [-118.423261392573, 34.0774903861815], - [-118.423253812589, 34.077478992435], - [-118.42324595233, 34.0774673665411], - [-118.423237810899, 34.0774562056815], - [-118.423229390091, 34.0774450448204], - [-118.42322082735, 34.0774338839579], - [-118.423211984334, 34.0774229559839], - [-118.423203983938, 34.0774131902267], - [-118.423016460623, 34.0771918327612], - [-118.423014496007, 34.0771895075746], - [-118.423012250219, 34.077187182388], - [-118.423010004431, 34.077185089348], - [-118.423007758643, 34.0771827641613], - [-118.423004108788, 34.0771797417905], - [-118.423000318796, 34.0771769515662], - [-118.422996248529, 34.0771741613419], - [-118.422992038125, 34.0771716032642], - [-118.422987686686, 34.0771692780771], - [-118.42298319511, 34.0771669528899], - [-118.422978703533, 34.0771650927401], - [-118.422973930784, 34.0771632325903], - [-118.422969018796, 34.0771616053313], - [-118.42296410591, 34.0771604423656], - [-118.422959193024, 34.0771592801439], - [-118.42295414, 34.0771583500689], - [-118.422948946839, 34.0771576521407], - [-118.422943893816, 34.0771571871032], - [-118.422938700655, 34.0771569549565], - [-118.422933506596, 34.0771569549565], - [-118.422928313436, 34.0771569549565], - [-118.422923120275, 34.077157419994], - [-118.422917927114, 34.0771581171782], - [-118.422912874091, 34.0771590472531], - [-118.422907961205, 34.0771599773281], - [-118.422904592522, 34.077160907403], - [-118.422901363977, 34.077161837478], - [-118.422896451091, 34.0771634654811], - [-118.422891819377, 34.0771653256309], - [-118.422887187664, 34.0771671857807], - [-118.422882696087, 34.0771695109678], - [-118.422878484785, 34.0771718361549], - [-118.422874274381, 34.0771743934886], - [-118.422870204115, 34.0771771837129], - [-118.422866414123, 34.0771802068278], - [-118.422862765166, 34.0771832291986], - [-118.422780372587, 34.0770544137555], - [-118.42277938983, 34.0770527864943], - [-118.42278542561, 34.0770460430701], - [-118.422791320355, 34.0770390674983], - [-118.422797215998, 34.077032091926], - [-118.422802830469, 34.077025116353], - [-118.422808304802, 34.0770181407795], - [-118.4228136381, 34.0770109330583], - [-118.42281883126, 34.0770037245924], - [-118.422823884284, 34.0769962839788], - [-118.42282879717, 34.0769888433645], - [-118.422833569021, 34.0769814027495], - [-118.422838201633, 34.0769739621339], - [-118.422842552174, 34.0769662893704], - [-118.422846763476, 34.0769586158621], - [-118.42285097388, 34.0769509430972], - [-118.422854904009, 34.0769430374403], - [-118.422858694001, 34.0769351317826], - [-118.422862202821, 34.0769272261242], - [-118.422865712538, 34.076919320465], - [-118.422868940185, 34.0769114148051], - [-118.422872028593, 34.076903276253], - [-118.422874554656, 34.0768963006694], - [-118.423301395451, 34.0756937014783], - [-118.422558179691, 34.0752779497584], - [-118.421851036679, 34.0750972788644], - [-118.420942756383, 34.0754321133385], - [-118.420397451158, 34.0742573965681], - [-118.419623214774, 34.0725896986204], - [-118.418453861599, 34.0723148461977], - [-118.418345081008, 34.072023947747], - [-118.417689872298, 34.0707419848933], - [-118.417393708529, 34.0702111023594], - [-118.416725025089, 34.0692779233519], - [-118.416383385007, 34.06884493207], - [-118.416944832059, 34.0685421624154], - [-118.41710961632, 34.0685651837371], - [-118.417264997016, 34.0683877541258], - [-118.414384352327, 34.0643676907157], - [-118.414078504719, 34.0639407207718], - [-118.413526180956, 34.0631697977409], - [-118.413610959461, 34.063127239402], - [-118.414178582431, 34.0628453795414], - [-118.413628925767, 34.0620783996961], - [-118.413527023576, 34.0619360727756], - [-118.412216184845, 34.0601058016977], - [-118.411563080888, 34.0604299974477], - [-118.41112150323, 34.0598118383386], - [-118.410798249865, 34.0593608913547], - [-118.410630377196, 34.0591248345507], - [-118.41071431398, 34.0590827400614], - [-118.410681047568, 34.0590352961042], - [-118.410677819921, 34.0590308769308], - [-118.409002321779, 34.0566909733776], - [-118.408696473273, 34.0562639647575], - [-118.408693244728, 34.0562595454396], - [-118.407017886723, 34.0539195653819], - [-118.406712038216, 34.053492542801], - [-118.406708669534, 34.053487891127], - [-118.40618020772, 34.0527501324089] - ], - [ - [-118.358481771784, 34.0757099780702], - [-118.357557068489, 34.0757139313283], - [-118.357557770073, 34.0759887727222], - [-118.358482473369, 34.0759848194769], - [-118.358481771784, 34.0757099780702] - ], - [ - [-118.409763222673, 34.1051571144528], - [-118.409746940708, 34.1088591351782], - [-118.411821206395, 34.1088881887612], - [-118.411800994301, 34.1107018334627], - [-118.413981093206, 34.1107322809721], - [-118.413967478339, 34.1119903968098], - [-118.4150585101, 34.111978310809], - [-118.415880889895, 34.1119692459353], - [-118.415880047275, 34.1119743592441], - [-118.415879626863, 34.1119778459627], - [-118.415879345691, 34.1119813319375], - [-118.415879065416, 34.111985050707], - [-118.415878925279, 34.1119885374251], - [-118.415878784244, 34.1119920233994], - [-118.415878644106, 34.1119957421685], - [-118.415878784244, 34.1119992288862], - [-118.415878784244, 34.11200271486], - [-118.415878925279, 34.1120064336286], - [-118.415879205553, 34.1120099203459], - [-118.415879485828, 34.1120134063193], - [-118.415879907138, 34.1120171250874], - [-118.415880468585, 34.1120206118042], - [-118.415881030032, 34.1120240977772], - [-118.415881591479, 34.1120275844937], - [-118.415883135683, 34.1120347892337], - [-118.415883977404, 34.1120382759499], - [-118.415884820024, 34.1120417619221], - [-118.415885802781, 34.1120452486379], - [-118.415886925675, 34.1120487346098], - [-118.415888048569, 34.1120519885306], - [-118.415889171463, 34.112055475246], - [-118.415890574632, 34.1120589612175], - [-118.415891837663, 34.1120622151378], - [-118.41589324173, 34.1120657018528], - [-118.415894785934, 34.1120689557729], - [-118.415896329239, 34.1120724417439], - [-118.415898013581, 34.1120756956637], - [-118.415899697922, 34.1120789495834], - [-118.415901383161, 34.112082203503], - [-118.415903347777, 34.1120854574225], - [-118.415905172255, 34.1120887113418], - [-118.415907137769, 34.1120917332101], - [-118.41590924342, 34.1120949871293], - [-118.415911348173, 34.1120980082536], - [-118.415913453824, 34.1121010301215], - [-118.415915699612, 34.1121042840403], - [-118.4159179454, 34.1121070731134], - [-118.415920331325, 34.1121100942373], - [-118.415922718149, 34.1121131161048], - [-118.415925244212, 34.1121161372285], - [-118.415927770274, 34.1121189263012], - [-118.415930437372, 34.1121217153738], - [-118.41593310447, 34.1121245044463], - [-118.415935771569, 34.1121272935188], - [-118.415938578804, 34.1121300825912], - [-118.415941386039, 34.1121326396126], - [-118.415944333412, 34.1121354286848], - [-118.415946860372, 34.1121382177569], - [-118.41596524709, 34.1121521631159], - [-118.415974230242, 34.1121589030001], - [-118.415978020235, 34.1121626217618], - [-118.415982230638, 34.1121663405233], - [-118.415986301803, 34.1121700592847], - [-118.415990231933, 34.112173778046], - [-118.415994162062, 34.112177496807], - [-118.415998092191, 34.1121812155679], - [-118.416001741148, 34.1121851671231], - [-118.41600553114, 34.1121891179343], - [-118.41600903996, 34.1121930694891], - [-118.416012548779, 34.1121972530944], - [-118.416015917462, 34.112201203905], - [-118.416019286144, 34.1122053875099], - [-118.416022514689, 34.1122095711145], - [-118.416025743234, 34.1122139875132], - [-118.416028831642, 34.1122181711174], - [-118.416031779015, 34.112222586772], - [-118.41603458625, 34.1122267703758], - [-118.416037393485, 34.1122311867736], - [-118.416040059685, 34.1122358352218], - [-118.416042726783, 34.1122402508754], - [-118.416045253744, 34.1122446672726], - [-118.416047639669, 34.11224931572], - [-118.416050025595, 34.1122539641672], - [-118.416052131246, 34.1122586126141], - [-118.416054377034, 34.1122632610607], - [-118.416056341649, 34.1122679095071], - [-118.416058307163, 34.1122725579533], - [-118.416060833226, 34.112279530622], - [-118.416061815983, 34.1122820868952], - [-118.416063500324, 34.1122869681347], - [-118.416064904391, 34.1122918486301], - [-118.416066447696, 34.1122967298691], - [-118.416067711626, 34.1123013783136], - [-118.416068974657, 34.1123062588082], - [-118.416070097551, 34.1123113720967], - [-118.416071080308, 34.1123162533345], - [-118.41607192203, 34.1123211338282], - [-118.416072764649, 34.1123260150655], - [-118.416073466234, 34.1123311283527], - [-118.416074167818, 34.1123360088456], - [-118.416074589128, 34.112340890082], - [-118.416075010438, 34.1123460033683], - [-118.416075290712, 34.1123508838604], - [-118.416075430849, 34.1123559971461], - [-118.416075571885, 34.1123608783814], - [-118.416075571885, 34.1123657588726], - [-118.416075430849, 34.1123708721574], - [-118.416075150575, 34.1123757533918], - [-118.416074869402, 34.112380866676], - [-118.416074308853, 34.112385747166], - [-118.416073746508, 34.1123908604496], - [-118.416073185061, 34.1123957416828], - [-118.41607234334, 34.112400622172], - [-118.41607150072, 34.1124055034047], - [-118.416070518861, 34.1124106166871], - [-118.416069536104, 34.1124154971754], - [-118.416068273073, 34.1124203784073], - [-118.416067009143, 34.112425258895], - [-118.416065746112, 34.1124301401262], - [-118.41606279874, 34.1124396690504], - [-118.416061394673, 34.1124445502808], - [-118.416060131642, 34.1124494307672], - [-118.41605886861, 34.112454311997], - [-118.416057885853, 34.1124591924828], - [-118.416056903096, 34.1124640737121], - [-118.416056061375, 34.112469186991], - [-118.416055218755, 34.1124740674759], - [-118.416054658207, 34.1124789487044], - [-118.416054095861, 34.1124840619823], - [-118.41605367545, 34.1124889424664], - [-118.41605325414, 34.112493823694], - [-118.416053114003, 34.1124989369711], - [-118.416052973865, 34.1125038174543], - [-118.41605283283, 34.1125089307308], - [-118.416052973865, 34.1125138119572], - [-118.416053114003, 34.1125168330665], - [-118.416673933407, 34.1125100932109], - [-118.416678987329, 34.1125100932109], - [-118.416869316981, 34.1125091635243], - [-118.417503191602, 34.1125014932376], - [-118.417538702904, 34.1093063325473], - [-118.417566775256, 34.1067625754519], - [-118.41756144106, 34.1067397965797], - [-118.417555686452, 34.1067174825766], - [-118.417553861974, 34.1067107415151], - [-118.417483681093, 34.1064576169816], - [-118.417481575441, 34.1064497140798], - [-118.417479049379, 34.1064392539838], - [-118.417476662555, 34.1064290266967], - [-118.417474416767, 34.106418567342], - [-118.417472592289, 34.1064081072421], - [-118.417470907947, 34.1063976478848], - [-118.417469363744, 34.1063871877823], - [-118.417468100712, 34.1063767284224], - [-118.417467117955, 34.1063662683174], - [-118.417466276234, 34.1063555761444], - [-118.417465573751, 34.1063451167806], - [-118.417449011512, 34.1059885552376], - [-118.417448731238, 34.1059827438764], - [-118.417448168893, 34.1059601976058], - [-118.417447888618, 34.1059376505855], - [-118.417448168893, 34.1059151043029], - [-118.417452380195, 34.1057226439563], - [-118.41738626958, 34.1057219462635], - [-118.417389498125, 34.1055764385115], - [-118.417390059572, 34.1055548218957], - [-118.417391322603, 34.105531345004], - [-118.417393006944, 34.1055078688497], - [-118.417395112595, 34.1054843919449], - [-118.417397779693, 34.1054611478466], - [-118.417400867203, 34.1054376716728], - [-118.41743567692, 34.1052033707403], - [-118.417369004858, 34.1052038356237], - [-118.416432371037, 34.105208484458], - [-118.41404116804, 34.1052170844289], - [-118.409763222673, 34.1051571144528] - ], - [ - [-118.348787124492, 34.1313910816837], - [-118.34841685061, 34.1319929172058], - [-118.348669641921, 34.132469038474], - [-118.348465696504, 34.1328896213198], - [-118.347473759699, 34.1331491729638], - [-118.347786205433, 34.1334698359471], - [-118.347689075093, 34.1336129720745], - [-118.346726193398, 34.1338955257961], - [-118.346729702217, 34.1341978297205], - [-118.346501193267, 34.1343556030256], - [-118.346576287035, 34.1346509344809], - [-118.346169658333, 34.1346929914021], - [-118.345872934015, 34.1346816061], - [-118.34570056977, 34.1349632268093], - [-118.345662531507, 34.1350208520533], - [-118.345587859049, 34.1351142606315], - [-118.344225086916, 34.1360662318468], - [-118.344388889318, 34.1366324840905], - [-118.344435909835, 34.1368055882732], - [-118.344040370835, 34.1378541996614], - [-118.343829968328, 34.138052628024], - [-118.343242414436, 34.1382187587685], - [-118.344849134865, 34.1419811348484], - [-118.345049852636, 34.1424511582793], - [-118.347508850589, 34.1421402876636], - [-118.35098659377, 34.1425334063674], - [-118.352682163869, 34.1426488792024], - [-118.353565881529, 34.1429060774923], - [-118.353850254012, 34.1429894871987], - [-118.356903964531, 34.1431667604318], - [-118.359094590793, 34.1431154142276], - [-118.361147942802, 34.1427171867824], - [-118.360794231159, 34.1420657067982], - [-118.360818654554, 34.1411128730526], - [-118.362243326001, 34.1386379189025], - [-118.361861401868, 34.1385972580441], - [-118.358617500936, 34.139500861767], - [-118.356571869049, 34.1378140023866], - [-118.352148507996, 34.1341641368465], - [-118.352142051804, 34.134177613849], - [-118.352139244569, 34.1341766844006], - [-118.35213447182, 34.1341755229619], - [-118.352129840106, 34.1341743607796], - [-118.352125068255, 34.1341734313312], - [-118.352120295506, 34.134172966607], - [-118.352115523655, 34.1341725018828], - [-118.352110610769, 34.1341722698925], - [-118.35210583802, 34.1341720371586], - [-118.352100926032, 34.1341722698925], - [-118.352096153283, 34.1341727346167], - [-118.352091381432, 34.1341734313312], - [-118.352086608683, 34.1341741287893], - [-118.352081976969, 34.134175290228], - [-118.352077345256, 34.1341764524103], - [-118.352072853679, 34.1341778465829], - [-118.352069765271, 34.1341790080215], - [-118.352066958036, 34.1341799374699], - [-118.352064010664, 34.1341813316424], - [-118.352061203428, 34.1341824938246], - [-118.352058396193, 34.1341838879971], - [-118.352054466064, 34.1341862116178], - [-118.352051798966, 34.1341878377805], - [-118.352049272903, 34.1341894646867], - [-118.352046745942, 34.1341910908494], - [-118.352044360017, 34.1341929497458], - [-118.352042114229, 34.1341948086422], - [-118.35203986844, 34.1341969002724], - [-118.352037762789, 34.1341987591687], - [-118.352035658037, 34.1342008500552], - [-118.352033692523, 34.1342029416852], - [-118.352031727009, 34.1342052653054], - [-118.352029341084, 34.1342080536496], - [-118.352009831472, 34.1341978297205], - [-118.35162664341, 34.1341267269069], - [-118.351604185528, 34.1341148760603], - [-118.351606712489, 34.1341116229885], - [-118.351607834484, 34.1341097640903], - [-118.351610221308, 34.1341062790278], - [-118.351612326061, 34.1341025612311], - [-118.351614291575, 34.1340988434342], - [-118.351616116053, 34.1340951256372], - [-118.351616958673, 34.1340932667386], - [-118.351618501978, 34.1340895489413], - [-118.351620046182, 34.1340855984097], - [-118.351621309214, 34.1340818806121], - [-118.351621871559, 34.1340797897226], - [-118.351622853418, 34.1340758391906], - [-118.351623696037, 34.1340718894019], - [-118.351624397622, 34.1340679388695], - [-118.351624959069, 34.1340639890804], - [-118.351625239343, 34.134059806557], - [-118.351625380378, 34.1340579476577], - [-118.351625380378, 34.1340537651339], - [-118.351625380378, 34.1340498146007], - [-118.351625099206, 34.1340458648108], - [-118.351624677896, 34.1340416822865], - [-118.351624116449, 34.1340377317527], - [-118.351623414865, 34.1340337819622], - [-118.351622432108, 34.134029831428], - [-118.351621450249, 34.1340258816372], - [-118.351620186319, 34.1340219311026], - [-118.351619484735, 34.1340200722024], - [-118.351617940531, 34.1340163544019], - [-118.351616397226, 34.1340124046105], - [-118.351614571849, 34.1340086868096], - [-118.35161358999, 34.1340068279091], - [-118.351611484339, 34.1340033420988], - [-118.351609378688, 34.1339996242976], - [-118.351606992763, 34.1339961392305], - [-118.351604606837, 34.1339926534198], - [-118.351601939739, 34.1339891683525], - [-118.351599132504, 34.1339859152758], - [-118.351596185132, 34.1339826621991], - [-118.351594781065, 34.1339812680233], - [-118.351591693555, 34.1339780149464], - [-118.351588324873, 34.1339749938602], - [-118.351586780669, 34.1339735996842], - [-118.35158327185, 34.1339708113323], - [-118.351579762132, 34.1339680229803], - [-118.351576113175, 34.1339654673627], - [-118.351572323183, 34.1339629110014], - [-118.351568533191, 34.1339603553837], - [-118.351564603061, 34.1339580317567], - [-118.351560532795, 34.1339559401206], - [-118.351556322391, 34.1339538492281], - [-118.351537373328, 34.1339447867101], - [-118.35151000256, 34.133931542405], - [-118.351482912964, 34.1339178326287], - [-118.351456103643, 34.133903658868], - [-118.351429575494, 34.1338892523702], - [-118.351403467757, 34.1338741484094], - [-118.351377641193, 34.1338590451895], - [-118.351352095801, 34.1338434764973], - [-118.351326830684, 34.1338274438197], - [-118.351301986876, 34.1338109456693], - [-118.351277423343, 34.1337942155243], - [-118.35125328112, 34.1337772533846], - [-118.351229420069, 34.1337598257714], - [-118.35120597943, 34.1337419341716], - [-118.351182960101, 34.1337238098329], - [-118.351160221046, 34.1337054527551], - [-118.351138044337, 34.13368663169], - [-118.351116147902, 34.1336675778855], - [-118.351094531741, 34.1336480593496], - [-118.351073477926, 34.1336285408091], - [-118.351052844522, 34.1336083248014], - [-118.351032632428, 34.1335881095324], - [-118.351012841644, 34.1335674287875], - [-118.350993471272, 34.1335467487811], - [-118.350974522209, 34.1335253713062], - [-118.350955994456, 34.1335039938258], - [-118.350938028151, 34.1334823836043], - [-118.350920483155, 34.1334603093927], - [-118.350903359469, 34.1334382344318], - [-118.350886656194, 34.1334156954803], - [-118.350870514367, 34.1333929237867], - [-118.350856056881, 34.133372242994], - [-118.350843705046, 34.1333550480334], - [-118.350830932799, 34.1333378530694], - [-118.350817878481, 34.1333208908381], - [-118.350804544788, 34.1333041605961], - [-118.350790788886, 34.1332874303507], - [-118.350776612572, 34.1332711648313], - [-118.350762155086, 34.1332548993087], - [-118.350747417325, 34.1332390985123], - [-118.350732258255, 34.133223297713], - [-118.350716818012, 34.1332077289037], - [-118.350700957357, 34.1331926255647], - [-118.350684956565, 34.1331775214794], - [-118.350668533565, 34.1331626501281], - [-118.350651831189, 34.1331480115109], - [-118.350634706605, 34.1331338368776], - [-118.350617442782, 34.1331196629854], - [-118.350599896888, 34.1331057210841], - [-118.350581930582, 34.1330922439106], - [-118.350563824139, 34.1330789987284], - [-118.350545296386, 34.133065986281], - [-118.350526628497, 34.1330532065685], - [-118.350507539297, 34.1330406588476], - [-118.350490836022, 34.1330299700468], - [-118.350193409222, 34.1328436129115], - [-118.350171934096, 34.1328301356963], - [-118.350135299901, 34.1328062016438], - [-118.350099226254, 34.1327818035964], - [-118.350063714953, 34.132757172804], - [-118.350028765098, 34.1327316125403], - [-118.349994235655, 34.1327058195308], - [-118.349960408695, 34.1326795625247], - [-118.349927003044, 34.1326526080397], - [-118.34989429808, 34.1326251888135], - [-118.349862015323, 34.1325975368401], - [-118.349830434151, 34.1325691881299], - [-118.349799413528, 34.1325406074154], - [-118.34976895525, 34.1325113292194], - [-118.349739198556, 34.1324818182747], - [-118.349710003309, 34.132451610591], - [-118.349681509647, 34.1324211709014], - [-118.34965357833, 34.1323902657232], - [-118.349626347699, 34.1323591285384], - [-118.349599679413, 34.1323275266078], - [-118.349573852848, 34.1322954599308], - [-118.349548587731, 34.132262928507], - [-118.349524024198, 34.132230165075], - [-118.349500162249, 34.1321971688909], - [-118.349454826073, 34.1321316412196], - [-118.349432087018, 34.1321000392039], - [-118.349408506242, 34.1320686691726], - [-118.349384364019, 34.1320377646091], - [-118.349042442764, 34.1316083474969], - [-118.349028406588, 34.1315902227007], - [-118.348787124492, 34.1313910816837] - ], - [ - [-118.443819758262, 34.2733849274111], - [-118.440481815397, 34.2758188125558], - [-118.440082906817, 34.2761094725611], - [-118.435599893138, 34.279377882786], - [-118.433809858899, 34.2776873256413], - [-118.433719325786, 34.2777545959906], - [-118.433064117076, 34.278232445888], - [-118.432968390803, 34.2783034272429], - [-118.432874207835, 34.2783760326636], - [-118.432781569071, 34.2784497974739], - [-118.432690474511, 34.2785247216708], - [-118.432601063395, 34.2786012699222], - [-118.43251319738, 34.2786789782954], - [-118.432434173483, 34.2787511187216], - [-118.430616629236, 34.2804520930071], - [-118.430664492373, 34.2804975569951], - [-118.430207053162, 34.2808311134609], - [-118.428947587286, 34.2817482709389], - [-118.42738157132, 34.2828913404021], - [-118.427232366541, 34.2830003579844], - [-118.424147776434, 34.2852495675387], - [-118.422432415551, 34.2865002107676], - [-118.422382446763, 34.2865363937887], - [-118.42082106251, 34.2876745277761], - [-118.420666945743, 34.2877870178668], - [-118.419099805984, 34.2889295413246], - [-118.41868237055, 34.2892329127874], - [-118.417378830631, 34.2901829129737], - [-118.417135022473, 34.2903605729492], - [-118.417353425376, 34.2905669920312], - [-118.417418833509, 34.290629149588], - [-118.417423886532, 34.2906254386904], - [-118.418207386343, 34.2913560184998], - [-118.418644612562, 34.2917686197088], - [-118.415803550339, 34.2938455084676], - [-118.418574432578, 34.2964559843905], - [-118.418961690009, 34.2961725837691], - [-118.418971655919, 34.2961653947502], - [-118.419219534344, 34.2959826448906], - [-118.421412967841, 34.2943800878496], - [-118.428543065134, 34.3011001860111], - [-118.428652827584, 34.3012033827927], - [-118.429202344111, 34.3017184349704], - [-118.429333441549, 34.3018422697363], - [-118.430354153189, 34.3027431973176], - [-118.431349036467, 34.3036086348956], - [-118.431453045208, 34.3037081175332], - [-118.431934766779, 34.3041308614026], - [-118.432163837176, 34.3043240283617], - [-118.43243557755, 34.3045628781076], - [-118.432559937623, 34.3046732589256], - [-118.432580992336, 34.3046915787881], - [-118.432806272742, 34.3044893684836], - [-118.435784609218, 34.3018346174024], - [-118.435917390997, 34.3017107826253], - [-118.436233205413, 34.3014343567712], - [-118.437333921911, 34.3004531764128], - [-118.437628260303, 34.3001922844067], - [-118.438253432046, 34.2996352474813], - [-118.438252309151, 34.2996340875791], - [-118.441728649164, 34.2965346039135], - [-118.441864238178, 34.2964135443039], - [-118.442484777308, 34.2958606568189], - [-118.442559590802, 34.2959274487889], - [-118.442785011344, 34.2957263765976], - [-118.44291049521, 34.2956203908213], - [-118.443485275956, 34.295161193277], - [-118.443449624517, 34.2951294200537], - [-118.443589284696, 34.2950053438429], - [-118.4435839505, 34.2948801074811], - [-118.44470965184, 34.2938761221948], - [-118.444977743257, 34.294114306259], - [-118.445225341407, 34.2943339352382], - [-118.44541216224, 34.2944997588672], - [-118.445721379429, 34.2942237731241], - [-118.445857109479, 34.2941036378704], - [-118.447096082986, 34.2930008435048], - [-118.449186350363, 34.2911484420791], - [-118.45001434373, 34.2904088147576], - [-118.449453317988, 34.2897037394176], - [-118.449699793244, 34.2895232948435], - [-118.452012253516, 34.2878334056423], - [-118.456018318373, 34.2849093025198], - [-118.455760614176, 34.2846664534103], - [-118.454671828204, 34.2836405434871], - [-118.45236161372, 34.2814573981031], - [-118.448090405717, 34.2774210273164], - [-118.447817401414, 34.2771628474087], - [-118.44742424834, 34.2767912334319], - [-118.446993057003, 34.2763838943767], - [-118.446135727354, 34.2755736174542], - [-118.443819758262, 34.2733849274111] - ] - ] - } - } - ] -} diff --git a/packages/ladot-service-areas/package.json b/packages/ladot-service-areas/package.json deleted file mode 100644 index 2c389aadf..000000000 --- a/packages/ladot-service-areas/package.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "name": "ladot-service-areas", - "version": "0.1.9", - "private": true, - "description": "Service Areas for LADOT", - "main": "ladot-service-areas.js", - "keywords": [ - "mds", - "enums" - ], - "author": "City of Los Angeles", - "license": "Apache-2.0", - "scripts": {} -} diff --git a/packages/mds-cache/index.ts b/packages/mds-agency-cache/index.ts similarity index 62% rename from packages/mds-cache/index.ts rename to packages/mds-agency-cache/index.ts index 05116e71e..ec4e285fe 100644 --- a/packages/mds-cache/index.ts +++ b/packages/mds-agency-cache/index.ts @@ -14,10 +14,10 @@ limitations under the License. */ -import log from '@mds-core/mds-logger' +import logger from '@mds-core/mds-logger' import flatten from 'flat' -import { nullKeys, stripNulls, now, isInsideBoundingBox, routeDistance } from '@mds-core/mds-utils' +import { NotFoundError, nullKeys, stripNulls, now, isInsideBoundingBox, routeDistance } from '@mds-core/mds-utils' import { UUID, Timestamp, @@ -52,6 +52,9 @@ declare module 'redis' { flushdbAsync: () => Promise<'OK'> hdelAsync: (...args: (string | number)[]) => Promise hgetallAsync: (arg1: string) => Promise<{ [key: string]: string }> + hgetAsync: (key: string, field: string) => Promise + hscanAsync: (key: string, cursor: number, condition: string, pattern: string) => Promise<[string, string[]]> + hsetAsync: (key: string, field: string, value: string) => Promise hmsetAsync: (...args: unknown[]) => Promise<'OK'> infoAsync: () => Promise keysAsync: (arg1: string) => Promise @@ -59,30 +62,40 @@ declare module 'redis' { zrangebyscoreAsync: (key: string, min: number | string, max: number | string) => Promise georadiusAsync: (key: string, longitude: number, latitude: number, radius: number, unit: string) => Promise } + interface Multi { + hgetallAsync: (arg1: string) => Promise<{ [key: string]: string }> + execAsync: () => Promise + } } bluebird.promisifyAll(redis.RedisClient.prototype) +bluebird.promisifyAll(redis.Multi.prototype) let cachedClient: redis.RedisClient | null +// optionally prefix a 'tenantId' key given the redis is a shared service across deployments +function decorateKey(key: string): string { + return env.TENANT_ID ? `${env.TENANT_ID}:${key}` : key +} + async function getClient() { if (!cachedClient) { const port = Number(env.REDIS_PORT) || 6379 const host = env.REDIS_HOST || 'localhost' - log.info(`connecting to redis on ${host}:${port}`) + logger.info(`connecting to redis on ${host}:${port}`) cachedClient = redis.createClient({ port, host, password: env.REDIS_PASS }) - log.info('redis client created') + logger.info('redis client created') cachedClient.on('connect', () => { - log.info('redis cache connected') + logger.info('redis cache connected') }) cachedClient.on('error', async err => { - await log.error(`redis cache error ${err}`) + logger.error(`redis cache error ${err}`) }) try { const size = await cachedClient.dbsizeAsync() - log.info(`redis cache has ${size} keys`) + logger.info(`redis cache has ${size} keys`) } catch (err) { - await log.error('redis failed to get dbsize', err) + logger.error('redis failed to get dbsize', err) } } return cachedClient @@ -109,30 +122,31 @@ async function info() { // so that we can trivially get a list of "updated since ___" device_ids async function updateVehicleList(device_id: UUID, timestamp?: Timestamp) { const when = timestamp || now() - // log.info('redis zadd', device_id, when) - return (await getClient()).zaddAsync('device-ids', when, device_id) + // logger.info('redis zadd', device_id, when) + return (await getClient()).zaddAsync(decorateKey('device-ids'), when, device_id) } async function hread(suffix: string, device_id: UUID): Promise { if (!device_id) { throw new Error(`hread: tried to read ${suffix} for device_id ${device_id}`) } - const key = `device:${device_id}:${suffix}` + const key = decorateKey(`device:${device_id}:${suffix}`) const flat = await (await getClient()).hgetallAsync(key) if (flat) { return unflatten({ ...flat, device_id }) } - throw new Error(`${suffix} for ${device_id} not found`) + throw new NotFoundError(`${suffix} for ${device_id} not found`) } /* Store latest known lat/lng for a given device in a redis geo-spatial analysis compatible manner. */ async function addGeospatialHash(device_id: UUID, coordinates: [number, number]) { const client = await getClient() const [lat, lng] = coordinates - const res = await client.geoadd('locations', lng, lat, device_id) + const res = await client.geoadd(decorateKey('locations'), lng, lat, device_id) return res } async function getEventsInBBox(bbox: BoundingBox) { + const start = now() const client = await getClient() const [pt1, pt2] = bbox const points = bbox.map(pt => { @@ -140,49 +154,49 @@ async function getEventsInBBox(bbox: BoundingBox) { }) const [lng, lat] = [(pt1[0] + pt2[0]) / 2, (pt1[1] + pt2[1]) / 2] const radius = routeDistance(points) - return client.georadiusAsync('locations', lng, lat, radius, 'm') + const events = client.georadiusAsync(decorateKey('locations'), lng, lat, radius, 'm') + const finish = now() + const timeElapsed = finish - start + logger.info(`mds-agency-cache getEventsInBBox ${JSON.stringify(bbox)} time elapsed: ${timeElapsed}ms`) + return events } -async function hreads(suffixes: string[], device_ids: UUID[]): Promise { +async function hreads( + suffixes: string[], + ids: UUID[], + prefix: 'device' | 'provider' = 'device' +): Promise { if (suffixes === undefined) { throw new Error('hreads: no suffixes') } - if (device_ids === undefined) { - throw new Error('hreads: no device_ids') + if (ids === undefined) { + throw new Error('hreads: no ids') } // bleah const multi = (await getClient()).multi() - suffixes.map(suffix => device_ids.map(device_id => multi.hgetall(`device:${device_id}:${suffix}`))) - - /* eslint-reason external lib weirdness */ - /* eslint-disable-next-line promise/avoid-new */ - return new Promise((resolve, reject) => { - /* eslint-reason external lib weirdness */ - /* eslint-disable-next-line promise/prefer-await-to-callbacks */ - multi.exec(async (err, replies) => { - if (err) { - await log.error('hreads', err) - reject(err) - } else { - resolve( - replies.map((flat, index) => { - if (flat) { - const flattened = { ...flat, device_id: device_ids[index % device_ids.length] } - return unflatten(flattened) - } - return unflatten(null) - }) - ) - } - }) + await Promise.all( + suffixes.map(suffix => + ids.map(id => { + return multi.hgetallAsync(decorateKey(`${prefix}:${id}:${suffix}`)) + }) + ) + ) + + const replies = await multi.execAsync() + return replies.map((flat, index) => { + if (flat) { + const flattened = { ...flat, [`${prefix}_id`]: ids[index % ids.length] } + return unflatten(flattened) + } + return unflatten(null) }) } // anything with a device_id, e.g. device, telemetry, etc. async function hwrite(suffix: string, item: CacheReadDeviceResult | Telemetry | VehicleEvent) { if (typeof item.device_id !== 'string') { - await log.error(`hwrite: invalid device_id ${item.device_id}`) + logger.error(`hwrite: invalid device_id ${item.device_id}`) throw new Error(`hwrite: invalid device_id ${item.device_id}`) } const { device_id } = item @@ -195,10 +209,17 @@ async function hwrite(suffix: string, item: CacheReadDeviceResult | Telemetry | if (nulls.length > 0) { // redis doesn't store null keys, so we have to delete them // TODO unit-test - await (await getClient()).hdelAsync(key, ...nulls) + await (await getClient()).hdelAsync(decorateKey(key), ...nulls) } - await (await getClient()).hmsetAsync(key, hmap) + const client = await getClient() + + await Promise.all( + (suffix === 'event' ? [`provider:${item.provider_id}:latest_event`, key] : [key]).map(k => + client.hmsetAsync(k, hmap) + ) + ) + return updateVehicleList(device_id) } @@ -211,22 +232,38 @@ async function writeDevice(device: Device) { } async function readKeys(pattern: string) { - return (await getClient()).keysAsync(pattern) + return (await getClient()).keysAsync(decorateKey(pattern)) +} + +async function getMostRecentEventByProvider(): Promise<{ provider_id: string; max: number }[]> { + const provider_ids = (await readKeys('provider:*:latest_event')).map(key => { + const [, provider_id] = key.split(':') + return provider_id + }) + const result = await hreads(['latest_event'], provider_ids, 'provider') + return result.map(elem => { + const max = parseInt(elem.timestamp || '0') + return { provider_id: elem.provider_id, max } + }) } async function wipeDevice(device_id: UUID) { - const keys = [`device:${device_id}:event`, `device:${device_id}:telemetry`, `device:${device_id}:device`] + const keys = [ + decorateKey(`device:${device_id}:event`), + decorateKey(`device:${device_id}:telemetry`), + decorateKey(`device:${device_id}:device`) + ] if (keys.length > 0) { - log.info('del', ...keys) + logger.info('del', ...keys) return (await getClient()).delAsync(...keys) } - log.info('no keys found for', device_id) + logger.info('no keys found for', device_id) return 0 } async function writeEvent(event: VehicleEvent) { // FIXME cope with out-of-order -- check timestamp - // log.info('redis write event', event.device_id) + // logger.info('redis write event', event.device_id) try { if (event.event_type === 'deregister') { return await wipeDevice(event.device_id) @@ -240,7 +277,7 @@ async function writeEvent(event: VehicleEvent) { } return hwrite('event', event) } catch (err) { - await log.error('hwrites', err.stack) + logger.error('hwrites', err.stack) throw err } } else { @@ -254,17 +291,16 @@ async function writeEvent(event: VehicleEvent) { } return hwrite('event', event) } catch (err) { - await log.error('hwrites', err.stack) + logger.error('hwrites', err.stack) throw err } } } async function readEvent(device_id: UUID): Promise { - // log.info('redis read event', device_id) - log.info('redis read event for', device_id) - const event = await hread('event', device_id) - return parseEvent(event as StringifiedEventWithTelemetry) + const rawEvent = await hread('event', device_id) + const event = parseEvent(rawEvent as StringifiedEventWithTelemetry) + return event } async function readEvents(device_ids: UUID[]): Promise { @@ -278,88 +314,127 @@ async function readEvents(device_ids: UUID[]): Promise { async function readAllEvents(): Promise> { // FIXME wildcard searching is slow + let start = now() const keys = await readKeys('device:*:event') + let finish = now() + let timeElapsed = finish - start + logger.info(`MDS-DAILY /admin/events -> cache.readAllEvents() readKeys() time elapsed: ${timeElapsed}ms`) const device_ids = keys.map(key => { const [, device_id] = key.split(':') return device_id }) - return (await hreads(['event'], device_ids)).map(event => { + + start = now() + const result = (await hreads(['event'], device_ids)).map(event => { return parseEvent(event as StringifiedEventWithTelemetry) }) + finish = now() + timeElapsed = finish - start + logger.info(`MDS-DAILY /admin/events -> cache.readAllEvents() hreads() time elapsed: ${timeElapsed}ms`) + + return result } async function readDevice(device_id: UUID) { if (!device_id) { throw new Error('null device not legal to read') } - // log.info('redis read device', device_id) - return parseDevice((await hread('device', device_id)) as StringifiedCacheReadDeviceResult) + // logger.info('redis read device', device_id) + const rawDevice = await hread('device', device_id) + const device = parseDevice(rawDevice as StringifiedCacheReadDeviceResult) + return device } async function readDevices(device_ids: UUID[]) { - // log.info('redis read device', device_id) + // logger.info('redis read device', device_id) return ((await hreads(['device'], device_ids)) as StringifiedCacheReadDeviceResult[]).map(device => { return parseDevice(device) }) } async function readDeviceStatus(device_id: UUID) { - let event: VehicleEvent - let device: Device + // Read event and device in parallel, catching NotFoundErrors + const promises = [readEvent(device_id), readDevice(device_id)].map((p: Promise<{}>) => + /* eslint-disable-next-line promise/prefer-await-to-callbacks */ + p.catch((err: Error) => { + if (err.name !== 'NotFoundError') { + throw err + } + }) + ) try { - event = await readEvent(device_id) - device = await readDevice(device_id) - } catch { - return null + const results = await Promise.all(promises) + const deviceStatusMap: { [device_id: string]: CachedItem | {} } = {} + results + .filter((item): item is CachedItem => item !== undefined) + .map(item => { + deviceStatusMap[item.device_id] = deviceStatusMap[item.device_id] || {} + Object.assign(deviceStatusMap[item.device_id], item) + }) + const statuses = Object.values(deviceStatusMap) + return statuses.find((status: any) => status.telemetry) || statuses[0] || null + } catch (err) { + logger.error('Error reading device status', err) + throw err } - const deviceStatusMap: { [device_id: string]: CachedItem | {} } = {} - const all = [device, event] - all.map(item => { - deviceStatusMap[item.device_id] = deviceStatusMap[item.device_id] || {} - Object.assign(deviceStatusMap[item.device_id], item) - }) - const values = Object.values(deviceStatusMap) - - return values.filter((item: any) => item.telemetry)[0] } /* eslint-reason redis external lib weirdness */ /* eslint-disable promise/prefer-await-to-then */ /* eslint-disable promise/catch-or-return */ -async function readDevicesStatus(query: { since?: number; skip?: number; take?: number; bbox: BoundingBox }) { - log.info('readDevicesStatus', JSON.stringify(query), 'start') +async function readDevicesStatus(query: { + since?: number + skip?: number + take?: number + bbox: BoundingBox + strict?: boolean +}) { + logger.info('readDevicesStatus', JSON.stringify(query), 'start') const start = query.since || 0 const stop = now() + const strictChecking = query.strict - log.info('redis zrangebyscore device-ids', start, stop) + logger.info('redis zrangebyscore device-ids', start, stop) const client = await getClient() + const geoStart = now() const { bbox } = query const deviceIdsInBbox = await getEventsInBBox(bbox) const deviceIdsRes = - deviceIdsInBbox.length === 0 ? await client.zrangebyscoreAsync('device-ids', start, stop) : deviceIdsInBbox + deviceIdsInBbox.length === 0 + ? await client.zrangebyscoreAsync(decorateKey('device-ids'), start, stop) + : deviceIdsInBbox const skip = query.skip || 0 const take = query.take || 100000000000 const deviceIds = deviceIdsRes.slice(skip, skip + take) + const geoFinish = now() + const timeElapsed = geoFinish - geoStart + logger.info(`mds-agency-cache readDevicesStatus bbox fetch ${JSON.stringify(bbox)} time elapsed: ${timeElapsed}ms`) - const deviceStatusMap: { [device_id: string]: CachedItem | {} } = {} - + const eventsStart = now() const events = ((await hreads(['event'], deviceIds)) as StringifiedEvent[]) .reduce((acc: VehicleEvent[], item: StringifiedEventWithTelemetry) => { try { const parsedItem = parseEvent(item) if ( - EVENT_STATUS_MAP[parsedItem.event_type] !== VEHICLE_STATUSES.removed && - (parsedItem.telemetry && isInsideBoundingBox(parsedItem.telemetry, query.bbox)) + EVENT_STATUS_MAP[parsedItem.event_type] === VEHICLE_STATUSES.removed || + !parsedItem.telemetry || + (strictChecking && !isInsideBoundingBox(parsedItem.telemetry, query.bbox)) ) - return [...acc, parsedItem] - return acc + return acc + return [...acc, parsedItem] } catch (err) { return acc } }, []) .filter(item => Boolean(item)) + const eventsFinish = now() + const eventsTimeElapsed = eventsFinish - eventsStart + logger.info( + `mds-agency-cache readDevicesStatus bbox check ${JSON.stringify(bbox)} time elapsed: ${eventsTimeElapsed}ms` + ) + const devicesStart = now() const eventDeviceIds = events.map(event => event.device_id) const devices = (await hreads(['device'], eventDeviceIds)) .reduce((acc: (Device | Telemetry | VehicleEvent)[], item: CachedItem) => { @@ -372,17 +447,24 @@ async function readDevicesStatus(query: { since?: number; skip?: number; take?: }, []) .filter(item => Boolean(item)) const all = [...devices, ...events] + const deviceStatusMap: { [device_id: string]: CachedItem | {} } = {} all.map(item => { deviceStatusMap[item.device_id] = deviceStatusMap[item.device_id] || {} Object.assign(deviceStatusMap[item.device_id], item) }) const values = Object.values(deviceStatusMap) + const valuesWithTelemetry = values.filter((item: any) => item.telemetry) + const devicesFinish = now() + const devicesTimeElapsed = devicesFinish - devicesStart + logger.info( + `mds-agency-cache readDevicesStatus device processing ${JSON.stringify(bbox)} time elapsed: ${devicesTimeElapsed}ms` + ) - return values.filter((item: any) => item.telemetry) + return valuesWithTelemetry } async function readTelemetry(device_id: UUID): Promise { - // log.info('redis read telemetry for', device_id) + // logger.info('redis read telemetry for', device_id) const telemetry = await hread('telemetry', device_id) return parseTelemetry(telemetry as StringifiedTelemetry) } @@ -396,19 +478,19 @@ async function writeOneTelemetry(telemetry: Telemetry) { await addGeospatialHash(telemetry.device_id, [lat, lng]) return hwrite('telemetry', telemetry) } catch (err) { - await log.error('hwrite', err.stack) + logger.error('hwrite', err.stack) return Promise.reject(err) } } else { return Promise.resolve() } } catch (err) { - log.info('writeOneTelemetry: no prior telemetry found:', err.message) + logger.info('writeOneTelemetry: no prior telemetry found:', err.message) try { await addGeospatialHash(telemetry.device_id, [lat, lng]) return hwrite('telemetry', telemetry) } catch (err2) { - await log.error('writeOneTelemetry hwrite2', err.stack) + logger.error('writeOneTelemetry hwrite2', err.stack) return Promise.reject(err2) } } @@ -429,34 +511,34 @@ async function readAllTelemetry() { try { return [...acc, parseTelemetry(telemetry)] } catch (err) { - log.info(JSON.parse(err)) + logger.info(JSON.parse(err)) return acc } }, []) } async function seed(dataParam: { devices: Device[]; events: VehicleEvent[]; telemetry: Telemetry[] }) { - log.info('cache seed') + logger.info('cache seed') const data = dataParam || { devices: [], events: [], telemetry: [] } - // log.info('cache seed redis', Object.keys(data).map(key => `${key} (${data[key].length})`)) - // log.info('cache seed redis', Object.keys(data).forEach(key => `${key} (${data[key].length})`)) + // logger.info('cache seed redis', Object.keys(data).map(key => `${key} (${data[key].length})`)) + // logger.info('cache seed redis', Object.keys(data).forEach(key => `${key} (${data[key].length})`)) await data.devices.map(writeDevice) await data.events.map(writeEvent) if (data.telemetry.length !== 0) { await writeTelemetry(data.telemetry.sort((a, b) => a.timestamp - b.timestamp)) } - log.info('cache seed redis done') + logger.info('cache seed redis done') } async function reset() { - log.info('cache reset') + logger.info('cache reset') await (await getClient()).flushdbAsync() - return log.info('redis flushed') + return logger.info('redis flushed') } async function initialize() { @@ -485,7 +567,7 @@ async function health() { async function cleanup() { try { const keys = await readKeys('device:*') - await log.warn('cleanup: read', keys.length) + logger.warn('cleanup: read', keys.length) const report: { telemetry: number; device: number; event: number; [suffix: string]: number } = { telemetry: 0, device: 0, @@ -508,16 +590,16 @@ async function cleanup() { report.deleted = result return report } catch (ex) { - await log.error('cleanup: exception', ex) + logger.error('cleanup: exception', ex) throw ex } } catch (ex) { - await log.error('cleanup: exception', ex) + logger.error('cleanup: exception', ex) return Promise.reject(ex) } } -export = { +export default { initialize, health, info, @@ -541,5 +623,6 @@ export = { readKeys, wipeDevice, updateVehicleList, - cleanup + cleanup, + getMostRecentEventByProvider } diff --git a/packages/mds-cache/package.json b/packages/mds-agency-cache/package.json similarity index 51% rename from packages/mds-cache/package.json rename to packages/mds-agency-cache/package.json index ac1c1f941..7caa9e80b 100644 --- a/packages/mds-cache/package.json +++ b/packages/mds-agency-cache/package.json @@ -1,6 +1,6 @@ { - "name": "@mds-core/mds-cache", - "version": "0.1.18", + "name": "@mds-core/mds-agency-cache", + "version": "0.1.26", "description": "Mobility Data Specification cache interface", "main": "dist/index.js", "types": "dist/index.d.ts", @@ -14,17 +14,21 @@ "author": "City of Los Angeles", "license": "Apache-2.0", "dependencies": { - "@mds-core/mds-logger": "0.1.16", - "@mds-core/mds-types": "0.1.15", - "@mds-core/mds-utils": "0.1.18", - "bluebird": "3.7.1", + "@mds-core/mds-logger": "0.1.24", + "@mds-core/mds-schema-validators": "0.1.2", + "@mds-core/mds-types": "0.1.23", + "@mds-core/mds-utils": "0.1.26", + "@types/bluebird": "3.5.30", + "@types/flat": "5.0.0", + "@types/redis": "2.8.20", + "bluebird": "3.7.2", "flat": "5.0.0", - "redis": "2.8.0" + "redis": "3.0.2" }, "scripts": { "build": "tsc --build tsconfig.build.json", "test": "yarn test:eslint && yarn test:unit", - "test:eslint": "eslint --fix --ignore-path ../../.gitignore '**/*.ts'", + "test:eslint": "eslint --ignore-path ../../.gitignore '**/*.ts'", "test:unit": "exit 0" } } diff --git a/packages/mds-agency-cache/tsconfig.build.json b/packages/mds-agency-cache/tsconfig.build.json new file mode 100644 index 000000000..1e1dd66dc --- /dev/null +++ b/packages/mds-agency-cache/tsconfig.build.json @@ -0,0 +1,12 @@ +{ + "extends": "../../tsconfig.build.json", + "compilerOptions": { + "outDir": "dist" + }, + "references": [ + { "path": "../../packages/mds-logger/tsconfig.build.json" }, + { "path": "../../packages/mds-schema-validators/tsconfig.build.json" }, + { "path": "../../packages/mds-types/tsconfig.build.json" }, + { "path": "../../packages/mds-utils/tsconfig.build.json" } + ] +} diff --git a/packages/mds-agency-cache/tsconfig.eslint.json b/packages/mds-agency-cache/tsconfig.eslint.json new file mode 100644 index 000000000..2a400942a --- /dev/null +++ b/packages/mds-agency-cache/tsconfig.eslint.json @@ -0,0 +1 @@ +{ "extends": "../../tsconfig.eslint.json" } diff --git a/packages/mds-cache/types.ts b/packages/mds-agency-cache/types.ts similarity index 100% rename from packages/mds-cache/types.ts rename to packages/mds-agency-cache/types.ts diff --git a/packages/mds-cache/unflatteners.ts b/packages/mds-agency-cache/unflatteners.ts similarity index 74% rename from packages/mds-cache/unflatteners.ts rename to packages/mds-agency-cache/unflatteners.ts index 7d180e017..a4df92d99 100644 --- a/packages/mds-cache/unflatteners.ts +++ b/packages/mds-agency-cache/unflatteners.ts @@ -1,11 +1,14 @@ import { VEHICLE_TYPE, VEHICLE_EVENT, VEHICLE_STATUS, Device, Telemetry, VehicleEvent } from '@mds-core/mds-types' -import { - isStringifiedTelemetry, - isStringifiedEventWithTelemetry, - isStringifiedCacheReadDeviceResult -} from '@mds-core/mds-utils' -import { StringifiedEvent, StringifiedTelemetry, StringifiedCacheReadDeviceResult, CachedItem } from './types' +import { ParseError } from '@mds-core/mds-utils' +import { HasPropertyAssertion } from '@mds-core/mds-schema-validators' +import { + StringifiedEvent, + StringifiedEventWithTelemetry, + StringifiedTelemetry, + StringifiedCacheReadDeviceResult, + CachedItem +} from './types' function parseTelemetry(telemetry: StringifiedTelemetry): Telemetry { try { @@ -26,7 +29,7 @@ function parseTelemetry(telemetry: StringifiedTelemetry): Telemetry { timestamp: Number(telemetry.timestamp) } } catch (err) { - throw new Error(`unable to parse telemetry: ${telemetry}`) + throw new ParseError(`unable to parse telemetry: ${telemetry}`) } } @@ -71,6 +74,15 @@ function parseDevice(device: StringifiedCacheReadDeviceResult): Device { return device } +const isStringifiedTelemetry = (telemetry: unknown): telemetry is StringifiedTelemetry => + HasPropertyAssertion(telemetry, 'gps') + +const isStringifiedEventWithTelemetry = (event: unknown): event is StringifiedEventWithTelemetry => + HasPropertyAssertion(event, 'event_type', 'telemetry') + +const isStringifiedCacheReadDeviceResult = (device: unknown): device is StringifiedCacheReadDeviceResult => + HasPropertyAssertion(device, 'device_id', 'provider_id', 'type', 'propulsion') + function parseCachedItem(item: CachedItem): Device | Telemetry | VehicleEvent { if (isStringifiedTelemetry(item)) { return parseTelemetry(item) @@ -82,7 +94,7 @@ function parseCachedItem(item: CachedItem): Device | Telemetry | VehicleEvent { return parseDevice(item) } - throw new Error(`unable to parse ${JSON.stringify(item)}`) + throw new ParseError(`unable to parse ${JSON.stringify(item)}`) } export { parseEvent, parseTelemetry, parseDevice, parseCachedItem } diff --git a/packages/mds-agency/README.md b/packages/mds-agency/README.md index adf942eec..de57ec454 100644 --- a/packages/mds-agency/README.md +++ b/packages/mds-agency/README.md @@ -1,18 +1,13 @@ # MDS Agency API +Implementation of the [MDS-Agency](https://github.com/openmobilityfoundation/mobility-data-specification/blob/dev/agency/README.md) API +## Package Content +### Stable Content +This package contains an MDS-Agency 0.4.0 implementation. +### Experimental Content +This package implements the `/stops` endpoint which is currently up for PR and not approved in the specification. -## Quick Start - -1. Make sure node/git are installed -2. Clone this repo -3. `cd` into the repo dir -4. Host the API locally at `http://localhost:4001` by running `yarn start` -5. Interact with the API using your favorite REST client - -## Run Tests - -1. Make sure you can run the API as per [Quick Start](#quick-start) above. -2. `yarn test` +## Quick Start and Tests +See [Quick Start](../../README.md#Installation) ## Build Deployment Package -1. Make sure the API passes all tests as per [Run Tests](#run-tests) above. -2. `yarn build` \ No newline at end of file +See [Build](../../README.md#Build) \ No newline at end of file diff --git a/packages/mds-agency/agency-candidate-request-handlers.ts b/packages/mds-agency/agency-candidate-request-handlers.ts index 548aa97c5..dc510075f 100644 --- a/packages/mds-agency/agency-candidate-request-handlers.ts +++ b/packages/mds-agency/agency-candidate-request-handlers.ts @@ -1,12 +1,13 @@ -import { AgencyApiRequest, AgencyApiResponse } from '@mds-core/mds-agency/types' -import log from '@mds-core/mds-logger' +import logger from '@mds-core/mds-logger' import { isUUID } from '@mds-core/mds-utils' import db from '@mds-core/mds-db' import { providerName } from '@mds-core/mds-providers' +import { parseRequest } from '@mds-core/mds-api-helpers' +import { AgencyApiRequest, AgencyApiResponse } from './types' export const readAllVehicleIds = async (req: AgencyApiRequest, res: AgencyApiResponse) => { // read all the devices - const query_provider_id = req.query.provider_id + const { provider_id: query_provider_id } = parseRequest(req).query('provider_id') if (query_provider_id && !isUUID(query_provider_id)) { return res.status(400).send({ @@ -15,7 +16,7 @@ export const readAllVehicleIds = async (req: AgencyApiRequest, res: AgencyApiRes }) } - await log.info(query_provider_id ? providerName(query_provider_id) : null, 'get /vehicles') + logger.info(query_provider_id ? providerName(query_provider_id) : null, 'get /vehicles') const items = await db.readDeviceIds(query_provider_id) const data: { [s: string]: string[] } = {} diff --git a/packages/mds-agency/api.ts b/packages/mds-agency/api.ts index ee63f69f8..f6aa89ef3 100644 --- a/packages/mds-agency/api.ts +++ b/packages/mds-agency/api.ts @@ -16,29 +16,37 @@ import express from 'express' -import log from '@mds-core/mds-logger' +import logger from '@mds-core/mds-logger' import { isProviderId } from '@mds-core/mds-providers' import { isUUID, pathsFor } from '@mds-core/mds-utils' -import { AgencyApiRequest, AgencyApiResponse } from '@mds-core/mds-agency/types' -import { checkAccess } from '@mds-core/mds-api-server' +import { checkAccess, AccessTokenScopeValidator } from '@mds-core/mds-api-server' +import { AgencyApiRequest, AgencyApiResponse, AgencyApiAccessTokenScopes } from './types' import { - getAllServiceAreas, - getServiceAreaById, registerVehicle, getVehicleById, getVehiclesByProvider, updateVehicle, submitVehicleEvent, - submitVehicleTelemetry + submitVehicleTelemetry, + registerStop, + readStop, + readStops } from './request-handlers' import { readAllVehicleIds } from './agency-candidate-request-handlers' import { getCacheInfo, wipeDevice, refreshCache } from './sandbox-admin-request-handlers' import { validateDeviceId } from './utils' +import { AgencyApiVersionMiddleware } from './middleware/agency-api-version' + +const checkAgencyApiAccess = (validator: AccessTokenScopeValidator) => + checkAccess(validator) + function api(app: express.Express): express.Express { /** * Agency-specific middleware to extract provider_id into locals, do some logging, etc. */ + app.use(AgencyApiVersionMiddleware) + app.use(async (req: AgencyApiRequest, res: AgencyApiResponse, next) => { try { // verify presence of provider_id @@ -47,47 +55,37 @@ function api(app: express.Express): express.Express { const { provider_id } = res.locals.claims if (!isUUID(provider_id)) { - await log.warn(req.originalUrl, 'invalid provider_id is not a UUID', provider_id) + logger.warn(req.originalUrl, 'invalid provider_id is not a UUID', provider_id) return res.status(400).send({ - result: `invalid provider_id ${provider_id} is not a UUID` + error: 'authentication_error', + error_description: `invalid provider_id ${provider_id} is not a UUID` }) } if (!isProviderId(provider_id)) { return res.status(400).send({ - result: `invalid provider_id ${provider_id} is not a known provider` + error: 'authentication_error', + error_description: `invalid provider_id ${provider_id} is not a known provider` }) } // stash provider_id res.locals.provider_id = provider_id - // log.info(providerName(provider_id), req.method, req.originalUrl) + // logger.info(providerName(provider_id), req.method, req.originalUrl) } else { - return res.status(401).send('Unauthorized') + return res.status(401).send({ error: 'authentication_error', error_description: 'Unauthorized' }) } } } catch (err) { /* istanbul ignore next */ - await log.error(req.originalUrl, 'request validation fail:', err.stack) + logger.error(req.originalUrl, 'request validation fail:', err.stack) } next() }) // / ////////// gets //////////////// - /** - * Get all service areas - * See {@link https://github.com/CityOfLosAngeles/mobility-data-specification/tree/dev/agency#service_areas Service Areas} - */ - app.get(pathsFor('/service_areas'), getAllServiceAreas) - - /** - * Get a particular service area - * See {@link https://github.com/CityOfLosAngeles/mobility-data-specification/tree/dev/agency#service_areas Service Areas} - */ - app.get(pathsFor('/service_areas/:service_area_id'), getServiceAreaById) - /** * Endpoint to register vehicles * See {@link https://github.com/CityOfLosAngeles/mobility-data-specification/tree/dev/agency#vehicle---register Register} @@ -124,21 +122,43 @@ function api(app: express.Express): express.Express { /** * Not currently in Agency spec. Ability to read back all vehicle IDs. */ - app.get(pathsFor('/admin/vehicle_ids'), checkAccess(scopes => scopes.includes('admin:all')), readAllVehicleIds) + app.get( + pathsFor('/admin/vehicle_ids'), + checkAgencyApiAccess(scopes => scopes.includes('admin:all')), + readAllVehicleIds + ) // /////////////////// end Agency candidate endpoints //////////////////// - app.get(pathsFor('/admin/cache/info'), checkAccess(scopes => scopes.includes('admin:all')), getCacheInfo) + app.get( + pathsFor('/admin/cache/info'), + checkAgencyApiAccess(scopes => scopes.includes('admin:all')), + getCacheInfo + ) // wipe a device -- sandbox or admin use only app.get( pathsFor('/admin/wipe/:device_id'), - checkAccess(scopes => scopes.includes('admin:all')), + checkAgencyApiAccess(scopes => scopes.includes('admin:all')), validateDeviceId, wipeDevice ) - app.get(pathsFor('/admin/cache/refresh'), checkAccess(scopes => scopes.includes('admin:all')), refreshCache) + app.get( + pathsFor('/admin/cache/refresh'), + checkAgencyApiAccess(scopes => scopes.includes('admin:all')), + refreshCache + ) + + app.post( + pathsFor('/stops'), + checkAgencyApiAccess(scopes => scopes.includes('admin:all')), + registerStop + ) + + app.get(pathsFor('/stops/:stop_id'), readStop) + + app.get(pathsFor('/stops'), readStops) return app } diff --git a/packages/mds-agency/middleware/agency-api-version.ts b/packages/mds-agency/middleware/agency-api-version.ts new file mode 100644 index 000000000..ebad0d356 --- /dev/null +++ b/packages/mds-agency/middleware/agency-api-version.ts @@ -0,0 +1,23 @@ +/* + Copyright 2019-2020 City of Los Angeles. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +import { ApiVersionMiddleware } from '@mds-core/mds-api-server' +import { AGENCY_API_SUPPORTED_VERSIONS, AGENCY_API_DEFAULT_VERSION } from '../types' + +export const AgencyApiVersionMiddleware = ApiVersionMiddleware( + 'application/vnd.mds.agency+json', + AGENCY_API_SUPPORTED_VERSIONS +).withDefaultVersion(AGENCY_API_DEFAULT_VERSION) diff --git a/packages/mds-agency/middleware/index.ts b/packages/mds-agency/middleware/index.ts new file mode 100644 index 000000000..527a2c8ed --- /dev/null +++ b/packages/mds-agency/middleware/index.ts @@ -0,0 +1,17 @@ +/* + Copyright 2019-2020 City of Los Angeles. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +export * from './agency-api-version' diff --git a/packages/mds-agency/package.json b/packages/mds-agency/package.json index d966e3277..9a651f99c 100644 --- a/packages/mds-agency/package.json +++ b/packages/mds-agency/package.json @@ -1,25 +1,22 @@ { "name": "@mds-core/mds-agency", "description": "MDS Agency API", - "version": "0.0.20", + "version": "0.0.28", "author": "City of Los Angeles", "license": "Apache-2.0", "dependencies": { - "@mds-core/mds-api-helpers": "0.1.18", - "@mds-core/mds-api-server": "0.1.18", - "@mds-core/mds-cache": "0.1.18", - "@mds-core/mds-db": "0.1.18", - "@mds-core/mds-logger": "0.1.16", - "@mds-core/mds-providers": "0.1.18", - "@mds-core/mds-stream": "0.1.18", - "@mds-core/mds-types": "0.1.15", - "@mds-core/mds-utils": "0.1.18", - "express": "4.17.1", - "ladot-service-areas": "0.1.9", - "uuid": "3.3.3" - }, - "devDependencies": { - "@mds-core/mds-test-data": "0.1.18" + "@mds-core/mds-api-helpers": "0.1.26", + "@mds-core/mds-api-server": "0.1.26", + "@mds-core/mds-agency-cache": "0.1.26", + "@mds-core/mds-db": "0.1.26", + "@mds-core/mds-logger": "0.1.24", + "@mds-core/mds-providers": "0.1.26", + "@mds-core/mds-schema-validators": "0.1.2", + "@mds-core/mds-stream": "0.1.26", + "@mds-core/mds-types": "0.1.23", + "@mds-core/mds-utils": "0.1.26", + "@types/express": "4.17.6", + "express": "4.17.1" }, "main": "dist/index.js", "types": "dist/index.d.ts", @@ -30,9 +27,9 @@ "build": "tsc --build tsconfig.build.json", "start": "PATH_PREFIX=/agency yarn watch server", "test": "yarn test:eslint && yarn test:unit", - "test:eslint": "eslint --fix --ignore-path ../../.gitignore '**/*.ts'", - "test:unit": "PATH_PREFIX=/agency DOTENV_CONFIG_PATH=../../.env nyc --check-coverage --exclude tests --extension .ts --lines 80 --reporter=text --reporter=html ts-mocha --project ../../tsconfig.json --require tsconfig-paths/register --require source-map-support/register --require dotenv/config --recursive --timeout 5000 tests/**/*.ts", - "watch": "nodemon --watch '../../packages' --ext 'ts' --ignore '*.d.ts' --exec yarn watch:exec --", - "watch:exec": "yarn build && DOTENV_CONFIG_PATH=../../.env ts-node -r dotenv/config" + "test:eslint": "eslint --ignore-path ../../.gitignore '**/*.ts'", + "test:unit": "PATH_PREFIX=/agency DOTENV_CONFIG_PATH=../../.env nyc ts-mocha --project ../../tsconfig.json --exit", + "ts-node": "DOTENV_CONFIG_PATH=../../.env ts-node -r dotenv/config", + "watch": "nodemon --watch '../../packages' --ext 'ts' --ignore '*.d.ts' --exec yarn ts-node --" } } diff --git a/packages/mds-agency/request-handlers.ts b/packages/mds-agency/request-handlers.ts index 12546c503..3cc5f160b 100644 --- a/packages/mds-agency/request-handlers.ts +++ b/packages/mds-agency/request-handlers.ts @@ -1,9 +1,8 @@ -import { AgencyApiRequest, AgencyApiResponse } from '@mds-core/mds-agency/types' -import areas from 'ladot-service-areas' -import log from '@mds-core/mds-logger' -import { isUUID, now, ServerError } from '@mds-core/mds-utils' +import logger from '@mds-core/mds-logger' +import { isUUID, now, ValidationError, normalizeToArray, NotFoundError, ServerError } from '@mds-core/mds-utils' +import { isValidStop, isValidDevice, validateEvent, isValidTelemetry } from '@mds-core/mds-schema-validators' import db from '@mds-core/mds-db' -import cache from '@mds-core/mds-cache' +import cache from '@mds-core/mds-agency-cache' import stream from '@mds-core/mds-stream' import { providerName } from '@mds-core/mds-providers' import { @@ -19,6 +18,19 @@ import { UUID } from '@mds-core/mds-types' import urls from 'url' +import { parseRequest } from '@mds-core/mds-api-helpers' +import { + AgencyApiRequest, + AgencyRegisterVehicleResponse, + AgencyGetVehicleByIdResponse, + AgencyGetVehiclesByProviderResponse, + AgencyUpdateVehicleResponse, + AgencySubmitVehicleEventResponse, + AgencySubmitVehicleTelemetryResponse, + AgencyRegisterStopResponse, + AgencyReadStopsResponse, + AgencyReadStopResponse +} from './types' import { badDevice, getVehicles, @@ -26,71 +38,41 @@ import { writeTelemetry, badEvent, badTelemetry, - getServiceArea, writeRegisterEvent, readPayload, computeCompositeVehicleData } from './utils' -export const getAllServiceAreas = async (req: AgencyApiRequest, res: AgencyApiResponse) => { - try { - const serviceAreas = await areas.readServiceAreas() - await log.info('readServiceAreas (all)', serviceAreas.length) - return res.status(200).send({ - service_areas: serviceAreas - }) - } catch (err) { - /* istanbul ignore next */ - await log.error('failed to read service areas', err) - return res.status(404).send({ - result: 'not found' - }) - } -} +// eslint-disable-next-line @typescript-eslint/no-floating-promises +stream.initialize() +const agencyServerError = { error: 'server_error', error_description: 'Unknown server error' } -export const getServiceAreaById = async (req: AgencyApiRequest, res: AgencyApiResponse) => { - const { service_area_id } = req.params +export const registerVehicle = async (req: AgencyApiRequest, res: AgencyRegisterVehicleResponse) => { + const { body } = req + const recorded = now() - if (!isUUID(service_area_id)) { - return res.status(400).send({ - result: `invalid service_area_id ${service_area_id} is not a UUID` - }) + const { provider_id } = res.locals + const { device_id, vehicle_id, type, propulsion, year, mfgr, model } = body + + const status = VEHICLE_STATUSES.removed + + const device = { + provider_id, + device_id, + vehicle_id, + type, + propulsion, + year, + mfgr, + model, + recorded, + status } try { - const serviceAreas = await areas.readServiceAreas(undefined, service_area_id) - - if (serviceAreas && serviceAreas.length > 0) { - await log.info('readServiceAreas (one)') - return res.status(200).send({ - service_areas: serviceAreas - }) - } - } catch { - return res.status(404).send({ - result: `${service_area_id} not found` - }) - } - - return res.status(404).send({ - result: `${service_area_id} not found` - }) -} - -export const registerVehicle = async (req: AgencyApiRequest, res: AgencyApiResponse) => { - const { body } = req - const recorded = now() - const device: Device = { - provider_id: res.locals.provider_id, - device_id: body.device_id, - vehicle_id: body.vehicle_id, - type: body.type, - propulsion: body.propulsion, - year: parseInt(body.year) || body.year, - mfgr: body.mfgr, - model: body.model, - recorded, - status: VEHICLE_STATUSES.removed + isValidDevice(device) + } catch (err) { + logger.info(`Device ValidationError for ${providerName(provider_id)}. Error: ${err}`) } const failure = badDevice(device) @@ -105,15 +87,15 @@ export const registerVehicle = async (req: AgencyApiRequest, res: AgencyApiRespo try { await Promise.all([cache.writeDevice(device), stream.writeDevice(device)]) } catch (err) { - await log.error('failed to write device stream/cache', err) + logger.error('failed to write device stream/cache', err) } - await log.info('new', providerName(res.locals.provider_id), 'vehicle added', device) + logger.info('new', providerName(res.locals.provider_id), 'vehicle added', device) try { await writeRegisterEvent(device, recorded) } catch (err) { - await log.error('writeRegisterEvent failure', err) + logger.error('writeRegisterEvent failure', err) } - res.status(201).send({ result: 'register device success', recorded, device }) + res.status(201).send({}) } catch (err) { if (String(err).includes('duplicate')) { res.status(409).send({ @@ -121,41 +103,36 @@ export const registerVehicle = async (req: AgencyApiRequest, res: AgencyApiRespo error_description: 'A vehicle with this device_id is already registered' }) } else if (String(err).includes('db')) { - await log.error(providerName(res.locals.provider_id), 'register vehicle failed:', err) - res.status(500).send(new ServerError()) + logger.error(providerName(res.locals.provider_id), 'register vehicle failed:', err) + res.status(500).send(agencyServerError) } else { - await log.error(providerName(res.locals.provider_id), 'register vehicle failed:', err) - res.status(500).send(new ServerError()) + logger.error(providerName(res.locals.provider_id), 'register vehicle failed:', err) + res.status(500).send(agencyServerError) } } } -export const getVehicleById = async (req: AgencyApiRequest, res: AgencyApiResponse) => { +export const getVehicleById = async (req: AgencyApiRequest, res: AgencyGetVehicleByIdResponse) => { const { device_id } = req.params - const { cached } = req.query + const { provider_id } = res.locals.scopes.includes('vehicles:read') + ? parseRequest(req).query('provider_id') + : res.locals - const { provider_id } = res.locals.scopes.includes('vehicles:read') ? req.query : res.locals + const payload = await readPayload(device_id) - log.info(`/vehicles/${device_id}`, cached) - const store = cached ? cache : db - const payload = await readPayload(store, device_id) if (!payload.device || (provider_id && payload.device.provider_id !== provider_id)) { - res.status(404).send({ - error: 'not_found' - }) + res.status(404).send({}) return } const compositeData = computeCompositeVehicleData(payload) - res.status(200).send(compositeData) + res.status(200).send({ ...compositeData }) } -export const getVehiclesByProvider = async (req: AgencyApiRequest, res: AgencyApiResponse) => { - let { skip, take } = req.query +export const getVehiclesByProvider = async (req: AgencyApiRequest, res: AgencyGetVehiclesByProviderResponse) => { const PAGE_SIZE = 1000 - skip = parseInt(skip) || 0 - take = parseInt(take) || PAGE_SIZE + const { skip = 0, take = PAGE_SIZE } = parseRequest(req, { parser: Number }).query('skip', 'take') const url = urls.format({ protocol: req.get('x-forwarded-proto') || req.protocol, @@ -164,43 +141,42 @@ export const getVehiclesByProvider = async (req: AgencyApiRequest, res: AgencyAp }) // TODO: Replace with express middleware - const { provider_id } = res.locals.scopes.includes('vehicles:read') ? req.query : res.locals + const { provider_id } = res.locals.scopes.includes('vehicles:read') + ? parseRequest(req).query('provider_id') + : res.locals try { - const response = await getVehicles(skip, take, url, provider_id, req.query) - return res.status(200).send(response) + const response = await getVehicles(skip, take, url, req.query, provider_id) + return res.status(200).send({ ...response }) } catch (err) { - await log.error('getVehicles fail', err) - res.status(500).send(new ServerError()) + logger.error('getVehicles fail', err) + return res.status(500).send(agencyServerError) } } export async function updateVehicleFail( req: AgencyApiRequest, - res: AgencyApiResponse, + res: AgencyUpdateVehicleResponse, provider_id: UUID, device_id: UUID, err: Error | string ) { if (String(err).includes('not found')) { - res.status(404).send({ - error: 'not_found' - }) + res.status(404).send({}) } else if (String(err).includes('invalid')) { res.status(400).send({ - error: 'invalid_data' + error: 'bad_param', + error_description: 'Invalid parameters for vehicle were sent' }) } else if (!provider_id) { - res.status(404).send({ - error: 'not_found' - }) + res.status(404).send({}) } else { - await log.error(providerName(provider_id), `fail PUT /vehicles/${device_id}`, req.body, err) - res.status(500).send(new ServerError()) + logger.error(providerName(provider_id), `fail PUT /vehicles/${device_id}`, req.body, err) + res.status(500).send(agencyServerError) } } -export const updateVehicle = async (req: AgencyApiRequest, res: AgencyApiResponse) => { +export const updateVehicle = async (req: AgencyApiRequest, res: AgencyUpdateVehicleResponse) => { const { device_id } = req.params const { vehicle_id } = req.body @@ -218,18 +194,19 @@ export const updateVehicle = async (req: AgencyApiRequest, res: AgencyApiRespons } else { const device = await db.updateDevice(device_id, provider_id, update) // TODO should we warn instead of fail if the cache/stream doesn't work? - await Promise.all([cache.writeDevice(device), stream.writeDevice(device)]) - return res.status(201).send({ - result: 'success', - vehicle: device - }) + try { + await Promise.all([cache.writeDevice(device), stream.writeDevice(device)]) + } catch (error) { + logger.warn(`Error writing to cache/stream ${error}`) + } + return res.status(201).send({}) } } catch (err) { await updateVehicleFail(req, res, provider_id, device_id, 'not found') } } -export const submitVehicleEvent = async (req: AgencyApiRequest, res: AgencyApiResponse) => { +export const submitVehicleEvent = async (req: AgencyApiRequest, res: AgencySubmitVehicleEventResponse) => { const { device_id } = req.params const { provider_id } = res.locals @@ -250,6 +227,12 @@ export const submitVehicleEvent = async (req: AgencyApiRequest, res: AgencyApiRe service_area_id: null // added for diagnostic purposes } + try { + validateEvent(event) + } catch (err) { + logger.info(`Event ValidationError for ${providerName(provider_id)}. Error: ${err}`) + } + if (event.telemetry) { event.telemetry_timestamp = event.telemetry.timestamp } @@ -257,8 +240,6 @@ export const submitVehicleEvent = async (req: AgencyApiRequest, res: AgencyApiRe async function success() { function fin() { res.status(201).send({ - result: 'success', - recorded, device_id, status: EVENT_STATUS_MAP[event.event_type] }) @@ -266,7 +247,7 @@ export const submitVehicleEvent = async (req: AgencyApiRequest, res: AgencyApiRe const delta = now() - recorded if (delta > 100) { - await log.info(name, 'post event took', delta, 'ms') + logger.info(name, 'post event took', delta, 'ms') fin() } else { fin() @@ -277,30 +258,20 @@ export const submitVehicleEvent = async (req: AgencyApiRequest, res: AgencyApiRe async function fail(err: Error | Partial<{ message: string }>): Promise { const message = err.message || String(err) if (message.includes('duplicate')) { - await log.info(name, 'duplicate event', event.event_type) - res.status(409).send({ - error: 'duplicate_event', - error_description: 'an event with this device_id and timestamp has already been received' + logger.info(name, 'duplicate event', event.event_type) + res.status(400).send({ + error: 'bad_param', + error_description: 'An event with this device_id and timestamp has already been received' }) } else if (message.includes('not found') || message.includes('unregistered')) { - await log.info(name, 'event for unregistered', event.device_id, event.event_type) + logger.info(name, 'event for unregistered', event.device_id, event.event_type) res.status(400).send({ error: 'unregistered', - error_description: 'the specified device_id has not been registered' + error_description: 'The specified device_id has not been registered' }) } else { - await log.error('post event fail:', event, message) - res.status(500).send(new ServerError()) - } - } - - async function finish() { - if (event.telemetry) { - event.telemetry.recorded = recorded - await writeTelemetry(event.telemetry) - await success() - } else { - await success() + logger.error('post event fail:', event, message) + res.status(500).send(agencyServerError) } } @@ -310,8 +281,12 @@ export const submitVehicleEvent = async (req: AgencyApiRequest, res: AgencyApiRe try { await cache.readDevice(event.device_id) } catch (err) { - await Promise.all([cache.writeDevice(device), stream.writeDevice(device)]) - log.info('Re-adding previously deregistered device to cache', err) + try { + await Promise.all([cache.writeDevice(device), stream.writeDevice(device)]) + logger.info('Re-adding previously deregistered device to cache', err) + } catch (error) { + logger.warn(`Error writing to cache/stream ${error}`) + } } if (event.telemetry) { event.telemetry.device_id = event.device_id @@ -319,28 +294,37 @@ export const submitVehicleEvent = async (req: AgencyApiRequest, res: AgencyApiRe const failure = (await badEvent(event)) || (event.telemetry ? badTelemetry(event.telemetry) : null) // TODO unify with fail() above if (failure) { - log.info(name, 'event failure', failure, event) + logger.info(name, 'event failure', failure, event) return res.status(400).send(failure) } - // make a note of the service area - event.service_area_id = getServiceArea(event) + const { telemetry } = event + if (telemetry) { + await db.writeTelemetry(normalizeToArray(telemetry)) + } // database write is crucial; failures of cache/stream should be noted and repaired const recorded_event = await db.writeEvent(event) + try { await Promise.all([cache.writeEvent(recorded_event), stream.writeEvent(recorded_event)]) - await finish() + + if (telemetry) { + telemetry.recorded = recorded + await Promise.all([cache.writeTelemetry([telemetry]), stream.writeTelemetry([telemetry])]) + } + + await success() } catch (err) { - await log.warn('/event exception cache/stream', err) - await finish() + logger.warn('/event exception cache/stream', err) + await success() } } catch (err) { await fail(err) } } -export const submitVehicleTelemetry = async (req: AgencyApiRequest, res: AgencyApiResponse) => { +export const submitVehicleTelemetry = async (req: AgencyApiRequest, res: AgencySubmitVehicleTelemetryResponse) => { const start = Date.now() const { data } = req.body @@ -348,7 +332,7 @@ export const submitVehicleTelemetry = async (req: AgencyApiRequest, res: AgencyA if (!provider_id) { res.status(400).send({ error: 'bad_param', - error_description: 'bad or missing provider_id' + error_description: 'Bad or missing provider_id' }) return } @@ -384,6 +368,12 @@ export const submitVehicleTelemetry = async (req: AgencyApiRequest, res: AgencyA recorded } + try { + isValidTelemetry(telemetry) + } catch (err) { + logger.info(`Telemetry ValidationError for ${providerName(provider_id)}. Error: ${err}`) + } + const bad_telemetry: ErrorObject | null = badTelemetry(telemetry) if (bad_telemetry) { const msg = `bad telemetry for device_id ${telemetry.device_id}: ${bad_telemetry.error_description}` @@ -399,9 +389,10 @@ export const submitVehicleTelemetry = async (req: AgencyApiRequest, res: AgencyA if (valid.length) { const recorded_telemetry = await writeTelemetry(valid) + const delta = Date.now() - start if (delta > 300) { - log.info( + logger.info( name, 'writeTelemetry', valid.length, @@ -419,31 +410,71 @@ export const submitVehicleTelemetry = async (req: AgencyApiRequest, res: AgencyA failures }) } else { - await log.info(name, 'no unique telemetry in', data.length, 'items') + logger.info(name, 'no unique telemetry in', data.length, 'items') res.status(400).send({ error: 'invalid_data', - error_description: 'none of the provided data was unique', - result: 'no new valid telemetry submitted', - unique: 0 + error_description: 'None of the provided data was valid', + error_details: failures }) } } else { const body = `${JSON.stringify(req.body).substring(0, 128)} ...` const fails = `${JSON.stringify(failures).substring(0, 128)} ...` - log.info(name, 'no valid telemetry in', data.length, 'items:', body, 'failures:', fails) + logger.info(name, 'no valid telemetry in', data.length, 'items:', body, 'failures:', fails) res.status(400).send({ error: 'invalid_data', - error_description: 'none of the provided data was valid', - result: 'no valid telemetry submitted', - failures + error_description: 'None of the provided data was valid', + error_details: failures }) } } catch (err) { - res.status(400).send({ - error: 'invalid_data', - error_description: 'none of the provided data was valid', - result: 'no valid telemetry submitted', - failures: [`device_id ${data[0].device_id}: not found`] + res.status(500).send({ + error: 'server_error', + error_description: 'None of the provided data was valid', + error_details: [` device_id ${data[0].device_id}: not found`] }) } } + +export const registerStop = async (req: AgencyApiRequest, res: AgencyRegisterStopResponse) => { + const stop = req.body + + try { + isValidStop(stop) + const recorded_stop = await db.writeStop(stop) + return res.status(201).send({ ...recorded_stop }) + } catch (error) { + if (error instanceof ValidationError) { + return res.status(400).send({ error }) + } + + return res.status(500).send({ error: new ServerError() }) + } +} + +export const readStop = async (req: AgencyApiRequest, res: AgencyReadStopResponse) => { + const { stop_id } = req.params + try { + const recorded_stop = await db.readStop(stop_id) + + if (!recorded_stop) { + return res.status(404).send({ error: new NotFoundError('Stop not found') }) + } + res.status(200).send({ ...recorded_stop }) + } catch (err) { + res.status(500).send({ error: new ServerError() }) + } +} + +export const readStops = async (req: AgencyApiRequest, res: AgencyReadStopsResponse) => { + try { + const stops = await db.readStops() + + if (!stops) { + return res.status(404).send({ error: new NotFoundError('No stops were found') }) + } + res.status(200).send({ stops }) + } catch (err) { + return res.status(500).send({ error: new ServerError() }) + } +} diff --git a/packages/mds-agency/sandbox-admin-request-handlers.ts b/packages/mds-agency/sandbox-admin-request-handlers.ts index 86023bb98..a784530e7 100644 --- a/packages/mds-agency/sandbox-admin-request-handlers.ts +++ b/packages/mds-agency/sandbox-admin-request-handlers.ts @@ -1,14 +1,15 @@ -import { AgencyApiRequest, AgencyApiResponse } from '@mds-core/mds-agency/types' -import log from '@mds-core/mds-logger' -import cache from '@mds-core/mds-cache' +import logger from '@mds-core/mds-logger' +import cache from '@mds-core/mds-agency-cache' import db from '@mds-core/mds-db' import { ServerError } from '@mds-core/mds-utils' +import { parseRequest } from '@mds-core/mds-api-helpers' +import { AgencyApiRequest, AgencyApiResponse } from './types' import { refresh } from './utils' export const getCacheInfo = async (req: AgencyApiRequest, res: AgencyApiResponse) => { try { const details = await cache.info() - await log.warn('cache', details) + logger.warn('cache', details) res.status(200).send(details) } catch (err) { res.status(500).send(new ServerError()) @@ -18,11 +19,11 @@ export const getCacheInfo = async (req: AgencyApiRequest, res: AgencyApiResponse export const wipeDevice = async (req: AgencyApiRequest, res: AgencyApiResponse) => { try { const { device_id } = req.params - await log.info('about to wipe', device_id) + logger.info('about to wipe', device_id) const cacheResult = await cache.wipeDevice(device_id) - await log.info('cache wiped', cacheResult) + logger.info('cache wiped', cacheResult) const dbResult = await db.wipeDevice(device_id) - await log.info('db wiped', dbResult) + logger.info('db wiped', dbResult) if (cacheResult >= 1) { res.status(200).send({ result: `successfully wiped ${device_id}` @@ -33,23 +34,21 @@ export const wipeDevice = async (req: AgencyApiRequest, res: AgencyApiResponse) }) } } catch (err) { - await log.error(`/admin/wipe/:device_id failed`, err) + logger.error(`/admin/wipe/:device_id failed`, err) res.status(500).send(new ServerError()) } } export const refreshCache = async (req: AgencyApiRequest, res: AgencyApiResponse) => { // wipe the cache and rebuild from db - let { skip, take } = req.query - skip = parseInt(skip) || 0 - take = parseInt(take) || 10000000000 + const { skip = 0, take = 10000000000 } = parseRequest(req, { parser: Number }).query('skip', 'take') try { const rows = await db.readDeviceIds() - await log.info('read', rows.length, 'device_ids. skip', skip, 'take', take) + logger.info('read', rows.length, 'device_ids. skip', skip, 'take', take) const devices = rows.slice(skip, take + skip) - await log.info('device_ids', devices) + logger.info('device_ids', devices) const promises = devices.map(device => refresh(device.device_id, device.provider_id)) await Promise.all(promises) @@ -57,7 +56,7 @@ export const refreshCache = async (req: AgencyApiRequest, res: AgencyApiResponse result: `success for ${devices.length} devices` }) } catch (err) { - await log.error('cache refresh fail', err) + logger.error('cache refresh fail', err) res.status(500).send(new ServerError()) } } diff --git a/packages/mds-agency/server.ts b/packages/mds-agency/server.ts index c1c97aa23..63b4c8ec6 100644 --- a/packages/mds-agency/server.ts +++ b/packages/mds-agency/server.ts @@ -14,14 +14,7 @@ limitations under the License. */ -// Express local -import { ApiServer } from '@mds-core/mds-api-server' +import { HttpServer, ApiServer } from '@mds-core/mds-api-server' import { api } from './api' -const { - env: { npm_package_name, PORT = 4001 } -} = process - -/* eslint-reason avoids import of logger */ -/* eslint-disable-next-line no-console */ -ApiServer(api).listen(PORT, () => console.log(`${npm_package_name} running on port ${PORT}`)) +HttpServer(ApiServer(api), { port: process.env.AGENCY_API_PORT }) diff --git a/packages/mds-agency/tests/db-integration-tests.ts b/packages/mds-agency/tests/integration-tests.spec.ts similarity index 89% rename from packages/mds-agency/tests/db-integration-tests.ts rename to packages/mds-agency/tests/integration-tests.spec.ts index f27cd7f14..080b5f907 100644 --- a/packages/mds-agency/tests/db-integration-tests.ts +++ b/packages/mds-agency/tests/integration-tests.spec.ts @@ -36,14 +36,18 @@ import { PROPULSION_TYPES, Timestamp, Device, - VehicleEvent + VehicleEvent, + Geography, + Stop } from '@mds-core/mds-types' import db from '@mds-core/mds-db' -import cache from '@mds-core/mds-cache' +import cache from '@mds-core/mds-agency-cache' import stream from '@mds-core/mds-stream' -import { makeDevices, makeEvents } from '@mds-core/mds-test-data' +import { shutdown as socketShutdown } from '@mds-core/mds-web-sockets' +import { makeDevices, makeEvents, GEOGRAPHY_UUID, LA_CITY_BOUNDARY, JUMP_TEST_DEVICE_1 } from '@mds-core/mds-test-data' import { ApiServer } from '@mds-core/mds-api-server' import { TEST1_PROVIDER_ID, TEST2_PROVIDER_ID } from '@mds-core/mds-providers' + import { api } from '../api' /* eslint-disable-next-line no-console */ @@ -55,9 +59,8 @@ function now(): Timestamp { return Date.now() } -const APP_JSON = 'application/json; charset=utf-8' +const APP_JSON = 'application/vnd.mds.agency+json; charset=utf-8; version=0.4' -const LA_CITY_BOUNDARY = '1f943d59-ccc9-4d91-b6e2-0c5e771cbc49' const PROVIDER_SCOPES = 'admin:all' const DEVICE_UUID = 'ec551174-f324-4251-bfed-28d9f3f473fc' const TRIP_UUID = '1f981864-cc17-40cf-aea3-70fd985e2ea7' @@ -109,6 +112,14 @@ const test_event = { testTimestamp += 1 +const LAGeography: Geography = { + name: 'Los Angeles', + geography_id: GEOGRAPHY_UUID, + geography_json: LA_CITY_BOUNDARY +} + +const JUMP_TEST_DEVICE_1_ID = JUMP_TEST_DEVICE_1.device_id + function deepCopy(obj: T): T { return JSON.parse(JSON.stringify(obj)) } @@ -116,45 +127,17 @@ function deepCopy(obj: T): T { // TODO Inherit all of these from mds-test-data const AUTH = `basic ${Buffer.from(`${TEST1_PROVIDER_ID}|${PROVIDER_SCOPES}`).toString('base64')}` const AUTH2 = `basic ${Buffer.from(`${TEST2_PROVIDER_ID}|${PROVIDER_SCOPES}`).toString('base64')}` -const AUTH_GARBAGE_PROVIDER = `basic ${Buffer.from(`tinylittleinvalidteapot|${PROVIDER_SCOPES}`).toString('base64')}` -const AUTH_UNKNOWN_UUID_PROVIDER = `basic ${Buffer.from( - `c8f984c5-62a5-4453-b1f7-3b7704a95cfe|${PROVIDER_SCOPES}` -).toString('base64')}` const AUTH_NO_SCOPE = `basic ${Buffer.from(`${TEST1_PROVIDER_ID}`).toString('base64')}` before(async () => { - await Promise.all([db.initialize(), cache.initialize(), stream.initialize()]) + await Promise.all([db.initialize(), cache.initialize()]) }) after(async () => { - await Promise.all([db.shutdown(), cache.shutdown(), stream.shutdown()]) + await Promise.all([db.shutdown(), cache.shutdown(), stream.shutdown(), socketShutdown()]) }) describe('Tests API', () => { - it('verifies non-uuid provider_id is rejected', done => { - request - .get('/devices') - .set('Authorization', AUTH_GARBAGE_PROVIDER) - .expect(400) - .end((err, result) => { - test.value(result).hasHeader('content-type', APP_JSON) - test.string(result.body.result).contains('invalid provider_id', 'is not a UUID') - done(err) - }) - }) - - it('verifies unknown provider_id is rejected', done => { - request - .get('/devices') - .set('Authorization', AUTH_UNKNOWN_UUID_PROVIDER) - .expect(400) - .end((err, result) => { - test.value(result).hasHeader('content-type', APP_JSON) - test.string(result.body.result).contains('invalid provider_id', 'is not a known provider') - done(err) - }) - }) - it('verifies unable to access admin if not scoped', done => { request .get('/admin/cache/info') @@ -186,7 +169,6 @@ describe('Tests API', () => { .expect(404) .end((err, result) => { log('err', err, 'body', result.body) - test.string(result.body.error).contains('not_found') test.value(result).hasHeader('content-type', APP_JSON) done(err) }) @@ -198,7 +180,6 @@ describe('Tests API', () => { .expect(404) .end((err, result) => { log('err', err, 'body', result.body) - test.string(result.body.error).contains('not_found') test.value(result).hasHeader('content-type', APP_JSON) done(err) }) @@ -351,7 +332,6 @@ describe('Tests API', () => { .expect(201) .end((err, result) => { log('err', err, 'body', result.body) - test.string(result.body.result).contains('success') test.value(result).hasHeader('content-type', APP_JSON) done(err) }) @@ -406,7 +386,6 @@ describe('Tests API', () => { .expect(404) .end((err, result) => { log('err', err, 'error', result.body.error) - test.string(result.body.error).contains('not_found') test.value(result).hasHeader('content-type', APP_JSON) done(err) }) @@ -418,7 +397,6 @@ describe('Tests API', () => { .expect(404) .end((err, result) => { log('err', err, 'error', result.body.error) - test.string(result.body.error).contains('not_found') test.value(result).hasHeader('content-type', APP_JSON) done(err) }) @@ -431,7 +409,7 @@ describe('Tests API', () => { .expect(409) .end((err, result) => { log('err', err, 'body', result.body) - test.string(result.body.error).contains('already_registered') + test.string(result.body.error_description).contains('already registered') test.value(result).hasHeader('content-type', APP_JSON) done(err) }) @@ -462,7 +440,6 @@ describe('Tests API', () => { .expect(404) .end((err, result) => { log('----> err', err, 'body', result.body.error) - test.string(result.body.error).contains('not_found') test.value(result).hasHeader('content-type', APP_JSON) done(err) }) @@ -550,7 +527,7 @@ describe('Tests API', () => { .set('Authorization', AUTH) .expect(200) .end((err, result) => { - test.value(result).hasHeader('content-type', APP_JSON) + test.string(result.body.result).contains('success') done(err) }) }) @@ -571,7 +548,6 @@ describe('Tests API', () => { .expect(201) .end((err, result) => { testTimestamp += 20000 - test.string(result.body.result).contains('success') test.string(result.body.status).is('available') done(err) }) @@ -603,7 +579,6 @@ describe('Tests API', () => { }) .expect(201) .end((err, result) => { - test.string(result.body.result).contains('success') test.string(result.body.status).is('unavailable') done(err) }) @@ -618,7 +593,6 @@ describe('Tests API', () => { .expect(201) .end((err, result) => { log('post deregister response:', JSON.stringify(result.body)) - test.string(result.body.result).contains('success') done(err) }) }) @@ -671,7 +645,7 @@ describe('Tests API', () => { .expect(400) .end((err, result) => { log('post event err', result.body) - test.string(result.body.error).contains('unregistered') + test.string(result.body.error_description).contains('The specified device_id has not been registered') done(err) }) }) @@ -719,8 +693,6 @@ describe('Tests API', () => { }) .expect(201) .end((err, result) => { - // log('post event', result.body) - test.string(result.body.result).contains('success') done(err) }) }) @@ -733,10 +705,12 @@ describe('Tests API', () => { telemetry: TEST_TELEMETRY, timestamp: testTimestamp - 1 }) - .expect(409) + .expect(400) .end((err, result) => { // log('post event', result.body) - test.string(result.body.error).contains('duplicate') + test + .string(result.body.error_description) + .contains('An event with this device_id and timestamp has already been received') done(err) }) }) @@ -752,7 +726,7 @@ describe('Tests API', () => { .expect(400) .end((err, result) => { // log('----> post event meant to fail', result.body) - test.string(result.body.error).contains('unregistered') + test.string(result.body.error_description).contains('The specified device_id has not been registered') done(err) }) }) @@ -787,7 +761,6 @@ describe('Tests API', () => { }) .expect(201) .end((err, result) => { - test.string(result.body.result).contains('success') done(err) }) }) @@ -819,7 +792,6 @@ describe('Tests API', () => { }) .expect(201) .end((err, result) => { - test.string(result.body.result).contains('success') done(err) }) }) @@ -835,7 +807,6 @@ describe('Tests API', () => { }) .expect(201) .end((err, result) => { - test.string(result.body.result).contains('success') done(err) }) }) @@ -851,7 +822,6 @@ describe('Tests API', () => { }) .expect(201) .end((err, result) => { - test.string(result.body.result).contains('success') done(err) }) }) @@ -874,7 +844,6 @@ describe('Tests API', () => { }) .expect(201) .end((err, result) => { - test.string(result.body.result).contains('success') done(err) }) }) @@ -890,7 +859,6 @@ describe('Tests API', () => { }) .expect(201) .end((err, result) => { - test.string(result.body.result).contains('success') done(err) }) }) @@ -1098,7 +1066,6 @@ describe('Tests API', () => { }) .expect(201) .end((err, result) => { - test.string(result.body.result).contains('success') done(err) }) }) @@ -1149,7 +1116,6 @@ describe('Tests API', () => { log('telemetry err', err) } else { // log('telemetry result', result) - test.string(result.body.result).contains('success') } done(err) }) @@ -1167,7 +1133,7 @@ describe('Tests API', () => { log('telemetry err', err) } else { // log('telemetry result', result) - test.string(result.body.result).contains('no new valid') + test.string(result.body.error_description).contains('None of the provided data was valid') } done(err) }) @@ -1199,7 +1165,6 @@ describe('Tests API', () => { log('telemetry err', err) } else { log('telemetry result', result.body) - test.value(result.body.failures.length).is(1) } done(err) }) @@ -1218,10 +1183,10 @@ describe('Tests API', () => { .expect(400) .end((err, result) => { if (err) { - log('telemetry err', err) + log('post bad telemetry err', err) } else { - log('telemetry result', result.body) - test.value(result.body.failures.length).is(1) + log('post bad telemetry result', result.body) + test.value(result.body.error_details.length).is(1) } done(err) }) @@ -1243,7 +1208,7 @@ describe('Tests API', () => { log('telemetry err', err) } else { log('telemetry result', result.body) - test.value(result.body.failures.length).is(1) + test.value(result.body.error_details.length).is(1) } done(err) }) @@ -1265,7 +1230,7 @@ describe('Tests API', () => { log('telemetry err', err) } else { log('telemetry result', result.body) - test.value(result.body.failures.length).is(1) + test.value(result.body.error_details.length).is(1) } done(err) }) @@ -1287,7 +1252,7 @@ describe('Tests API', () => { log('telemetry err', err) } else { log('telemetry result', result.body) - test.value(result.body.failures.length).is(1) + test.value(result.body.error_details.length).is(1) } done(err) }) @@ -1299,13 +1264,13 @@ describe('Tests API', () => { .send({ data: [TEST_TELEMETRY] }) - .expect(400) + .expect(500) .end((err, result) => { if (err) { - log('telemetry err', err) + log('telemetry err with mismatched provider', err) } else { - log('telemetry result', result.body) - test.value(result.body.failures.length).is(1) + log('telemetry result with mismatched provider', result.body) + test.value(result.body.error_details.length).is(1) } done(err) }) @@ -1346,71 +1311,30 @@ describe('Tests API', () => { }) }) - /* eslint-disable @typescript-eslint/no-explicit-any */ - it('verifies reading a single service_area', done => { - request - .get(`/service_areas/${LA_CITY_BOUNDARY}`) - .set('Authorization', AUTH) - .expect(200) - .end((err, result) => { - if (err) { - log('service_area err', err) - test.value(err).is(undefined) // fail - } else { - // log('service_area result', Object.keys(result.body)) - test.object(result.body).match((obj: any) => Array.isArray(obj.service_areas)) - test.object(result.body).match((obj: any) => typeof obj.service_areas[0].service_area_id === 'string') - } - done(err) - }) - }) - /* eslint-enable @typescript-eslint/no-explicit-any */ + it('verifies get device defaults to `deregister` if cache misses reads for associated events', async () => { + await request.post('/vehicles').set('Authorization', AUTH).send(JUMP_TEST_DEVICE_1).expect(201) - it('tries and fails to read a non-existent service_area', done => { - request - .get('/service_areas/b4bcc213-4888-48ce-a33d-4dd6c3384bda') + await request + .post(`/vehicles/${JUMP_TEST_DEVICE_1_ID}/event`) .set('Authorization', AUTH) - .expect(404) - .end((err, result) => { - log(result.body) - if (err) { - log('service_area err', err) - } else { - // really only care that we got a 404 - } - done(err) - }) - }) - it('verifies you cannot query service_areas that are not UUIDs', done => { - request - .get('/service_areas/definitely-not-a-UUID') - .set('Authorization', AUTH) - .expect(400) - .end((err, result) => { - test.value(result).hasHeader('content-type', APP_JSON) - test.string(result.body.result).contains('invalid service_area_id') - done(err) - }) + .send({ device_id: JUMP_TEST_DEVICE_1, timestamp: now(), event_type: VEHICLE_EVENTS.deregister }) + .expect(201) + + const result = await request.get(`/vehicles/${JUMP_TEST_DEVICE_1_ID}`).set('Authorization', AUTH).expect(200) + test.assert(result.body.status === VEHICLE_STATUSES.inactive) + test.assert(result.body.prev_event === VEHICLE_EVENTS.deregister) }) - /* eslint-disable @typescript-eslint/no-explicit-any */ - it('verifies reading all service_areas', done => { - request - .get('/service_areas') - .set('Authorization', AUTH) - .expect(200) - .end((err, result) => { - if (err) { - log('service_areas err', err) - } else { - // log('service_areas result', Object.keys(result.body)) - test.object(result.body).match((obj: any) => Array.isArray(obj.service_areas)) - test.object(result.body).match((obj: any) => typeof obj.service_areas[0].service_area_id === 'string') - } - done(err) - }) + it('get multiple devices endpoint has vehicle status default to `inactive` if event is missing for a device', async () => { + const result = await request.get(`/vehicles/`).set('Authorization', AUTH).expect(200) + const ids = result.body.vehicles.map((device: any) => device.device_id) + test.assert(ids.includes(JUMP_TEST_DEVICE_1_ID)) + result.body.vehicles.map((device: any) => { + if (device.device_id === JUMP_TEST_DEVICE_1_ID) { + test.assert(device.status === VEHICLE_STATUSES.inactive) + } + }) }) - /* eslint-enable @typescript-eslint/no-explicit-any */ it('gets cache info', done => { request @@ -1531,3 +1455,68 @@ describe('Tests pagination', async () => { }) }) }) + +describe('Tests Stops', async () => { + const TEST_STOP: Stop = { + stop_id: '821f8dee-dd43-4f03-99d4-3cf761f4fe7e', + stop_name: 'LA Stop', + geography_id: GEOGRAPHY_UUID, + lat: 34.0522, + lng: -118.2437, + capacity: { + bicycle: 10, + scooter: 10, + car: 5, + moped: 3 + }, + num_vehicles_available: { + bicycle: 3, + scooter: 7, + car: 0, + moped: 1 + }, + num_spots_available: { + bicycle: 7, + scooter: 3, + car: 5, + moped: 2 + } + } + + before(async () => { + await Promise.all([db.initialize(), cache.initialize()]) + }) + + it('verifies failing to POST a stop (garbage data)', async () => { + await request.post(`/stops`).set('Authorization', AUTH).send({ foo: 'bar' }).expect(400) + }) + + it('verifies successfully POSTing a stop', async () => { + await db.writeGeography(LAGeography) + await db.publishGeography({ geography_id: GEOGRAPHY_UUID, publish_date: now() }) + await request.post(`/stops`).set('Authorization', AUTH).send(TEST_STOP).expect(201) + }) + + it('verifies successfully GETing a stop', done => { + request + .get(`/stops/${TEST_STOP.stop_id}`) + .set('Authorization', AUTH) + .expect(200) + .end((err, result) => { + test.assert(result.body.stop_id === TEST_STOP.stop_id) + done(err) + }) + }) + + it('verifies successfully GETing all stops', done => { + request + .get(`/stops`) + .set('Authorization', AUTH) + .expect(200) + .end((err, result) => { + test.assert(result.body.stops.length === 1) + test.assert(result.body.stops[0].stop_id === TEST_STOP.stop_id) + done(err) + }) + }) +}) diff --git a/packages/mds-agency/tests/request-handlers.ts b/packages/mds-agency/tests/request-handlers.spec.ts similarity index 72% rename from packages/mds-agency/tests/request-handlers.ts rename to packages/mds-agency/tests/request-handlers.spec.ts index c99d33c6f..444c7841a 100644 --- a/packages/mds-agency/tests/request-handlers.ts +++ b/packages/mds-agency/tests/request-handlers.spec.ts @@ -1,15 +1,12 @@ -import areas, { ServiceArea } from 'ladot-service-areas' import Sinon from 'sinon' import assert from 'assert' -import uuid from 'uuid' -import { Device, VEHICLE_TYPES } from 'packages/mds-types' +import { uuid } from '@mds-core/mds-utils' +import { Device, VEHICLE_TYPES } from '@mds-core/mds-types' import db from '@mds-core/mds-db' -import cache from '@mds-core/mds-cache' +import cache from '@mds-core/mds-agency-cache' import stream from '@mds-core/mds-stream' -import { AgencyApiRequest, AgencyApiResponse } from '../types' +import { AgencyApiRequest, AgencyApiResponse, AgencyGetVehiclesByProviderResponse } from '../types' import { - getAllServiceAreas, - getServiceAreaById, registerVehicle, getVehicleById, getVehiclesByProvider, @@ -25,125 +22,11 @@ function getLocals(provider_id: string) { } describe('Agency API request handlers', () => { - describe('Get all service areas', () => { - it('Gets all service areas', async () => { - const serviceAreas: ServiceArea[] = [ - { - start_date: 0, - end_date: null, - prev_area: null, - replacement_area: null, - type: 'unrestricted', - description: 'Los Angeles', - area: {} as any - } - ] - Sinon.replace(areas, 'readServiceAreas', Sinon.fake.resolves(serviceAreas)) - const res: AgencyApiResponse = {} as AgencyApiResponse - const sendHandler = Sinon.fake.returns('asdf') - const statusHandler = Sinon.fake.returns({ - send: sendHandler - } as any) - res.status = statusHandler - await getAllServiceAreas({} as AgencyApiRequest, res) - assert.equal(statusHandler.calledWith(200), true) - assert.equal(sendHandler.calledWith({ service_areas: serviceAreas }), true) - Sinon.restore() - }) - - it('Handles a service area read exception', async () => { - Sinon.replace(areas, 'readServiceAreas', Sinon.fake.rejects('fake-rejects')) - const res: AgencyApiResponse = {} as AgencyApiResponse - const sendHandler = Sinon.fake.returns('asdf') - const statusHandler = Sinon.fake.returns({ - send: sendHandler - } as any) - res.status = statusHandler - await getAllServiceAreas({} as AgencyApiRequest, res) - assert.equal(statusHandler.calledWith(404), true) - assert.equal(sendHandler.calledWith({ result: 'not found' }), true) - Sinon.restore() - }) - }) - - describe('Get service area by id', () => { - it('Gets service area by id', async () => { - const serviceAreas: ServiceArea[] = [ - { - start_date: 0, - end_date: null, - prev_area: null, - replacement_area: null, - type: 'unrestricted', - description: 'Los Angeles', - area: {} as any - } - ] - Sinon.replace(areas, 'readServiceAreas', Sinon.fake.resolves(serviceAreas)) - const service_area_id = uuid() - const res: AgencyApiResponse = {} as AgencyApiResponse - const sendHandler = Sinon.fake.returns('asdf') - const statusHandler = Sinon.fake.returns({ - send: sendHandler - } as any) - res.status = statusHandler - await getServiceAreaById({ params: { service_area_id } } as AgencyApiRequest, res) - assert.equal(statusHandler.calledWith(200), true) - assert.equal(sendHandler.calledWith({ service_areas: serviceAreas }), true) - Sinon.restore() - }) - - it('Handles a service area read exception', async () => { - Sinon.replace(areas, 'readServiceAreas', Sinon.fake.rejects('fake-rejects')) - const res: AgencyApiResponse = {} as AgencyApiResponse - const service_area_id = uuid() - const sendHandler = Sinon.fake.returns('asdf') - const statusHandler = Sinon.fake.returns({ - send: sendHandler - } as any) - res.status = statusHandler - await getServiceAreaById({ params: { service_area_id } } as AgencyApiRequest, res) - assert.equal(statusHandler.calledWith(404), true) - assert.equal(sendHandler.calledWith({ result: `${service_area_id} not found` }), true) - Sinon.restore() - }) - - it('Handles no service areas found gracefully', async () => { - Sinon.replace(areas, 'readServiceAreas', Sinon.fake.resolves([])) - const res: AgencyApiResponse = {} as AgencyApiResponse - const service_area_id = uuid() - const sendHandler = Sinon.fake.returns('asdf') - const statusHandler = Sinon.fake.returns({ - send: sendHandler - } as any) - res.status = statusHandler - await getServiceAreaById({ params: { service_area_id } } as AgencyApiRequest, res) - assert.equal(statusHandler.calledWith(404), true) - assert.equal(sendHandler.calledWith({ result: `${service_area_id} not found` }), true) - Sinon.restore() - }) - - it('Handles a non-uuid service area id gracefully', async () => { - Sinon.replace(areas, 'readServiceAreas', Sinon.fake.rejects('fake-rejects')) - const res: AgencyApiResponse = {} as AgencyApiResponse - const service_area_id = 'not-a-uuid' - const sendHandler = Sinon.fake.returns('asdf') - const statusHandler = Sinon.fake.returns({ - send: sendHandler - } as any) - res.status = statusHandler - await getServiceAreaById({ params: { service_area_id } } as AgencyApiRequest, res) - assert.equal(statusHandler.calledWith(400), true) - assert.equal(sendHandler.calledWith({ result: `invalid service_area_id ${service_area_id} is not a UUID` }), true) - Sinon.restore() - }) - }) - describe('Register vehicle', () => { const getFakeBody = () => { const device_id = uuid() const vehicle_id = uuid() - const type: Device['type'] = VEHICLE_TYPES.carshare + const type: Device['type'] = VEHICLE_TYPES.car const propulsion: Device['propulsion'] = ['combustion'] const body = { device_id, @@ -151,7 +34,7 @@ describe('Agency API request handlers', () => { type, propulsion, year: 1990, - mfgr: 'foo inc.', + mfgr: 'foo inc', model: 'i date one' } return body @@ -281,10 +164,10 @@ describe('Agency API request handlers', () => { Sinon.replace(db, 'readEvent', Sinon.fake.resolves('it-worked')) Sinon.replace(db, 'readTelemetry', Sinon.fake.resolves('it-worked')) await getVehicleById( - { + ({ params: { device_id }, query: { cached: false } - } as AgencyApiRequest, + } as unknown) as AgencyApiRequest<{ device_id: string }>, res ) assert.equal(statusHandler.calledWith(404), true) @@ -313,10 +196,10 @@ describe('Agency API request handlers', () => { Sinon.replace(db, 'readTelemetry', Sinon.fake.resolves({})) Sinon.replace(utils, 'computeCompositeVehicleData', Sinon.fake.resolves('it-worked')) await getVehicleById( - { + ({ params: { device_id }, query: { cached: false } - } as AgencyApiRequest, + } as unknown) as AgencyApiRequest<{ device_id: string }>, res ) assert.equal(statusHandler.calledWith(200), true) @@ -338,11 +221,11 @@ describe('Agency API request handlers', () => { res.locals = getLocals(provider_id) as any Sinon.replace(utils, 'getVehicles', Sinon.fake.rejects('it-broke')) await getVehiclesByProvider( - { + ({ params: { device_id }, query: { cached: false }, get: Sinon.fake.returns('foo') as any - } as AgencyApiRequest, + } as unknown) as AgencyApiRequest<{ device_id: string }>, res ) assert.equal(statusHandler.calledWith(500), true) @@ -353,24 +236,26 @@ describe('Agency API request handlers', () => { it('Gets vehicles by provider', async () => { const provider_id = uuid() const device_id = uuid() - const res: AgencyApiResponse = {} as AgencyApiResponse + const res: AgencyGetVehiclesByProviderResponse = {} as AgencyGetVehiclesByProviderResponse const sendHandler = Sinon.fake.returns('asdf') const statusHandler = Sinon.fake.returns({ send: sendHandler } as any) res.status = statusHandler res.locals = getLocals(provider_id) as any - Sinon.replace(utils, 'getVehicles', Sinon.fake.resolves('it-worked')) + + const stubbedResponse = { total: 0, links: { first: '0', last: '0', prev: null, next: null }, vehicles: [] } + Sinon.replace(utils, 'getVehicles', Sinon.fake.resolves(stubbedResponse)) await getVehiclesByProvider( - { + ({ params: { device_id }, query: { cached: false }, get: Sinon.fake.returns('foo') as any - } as AgencyApiRequest, + } as unknown) as AgencyApiRequest<{ device_id: string }>, res ) assert.equal(statusHandler.calledWith(200), true) - assert.equal(sendHandler.calledWith('it-worked'), true) + assert.equal(sendHandler.calledWith({ ...stubbedResponse }), true) Sinon.restore() }) }) @@ -389,23 +274,18 @@ describe('Agency API request handlers', () => { res.locals = getLocals(provider_id) as any Sinon.replace(utils, 'getVehicles', Sinon.fake.rejects('it-broke')) await updateVehicleFail( - { + ({ params: { device_id }, query: { cached: false }, get: Sinon.fake.returns('foo') as any - } as AgencyApiRequest, + } as unknown) as AgencyApiRequest<{ device_id: string }>, res, provider_id, device_id, 'not found' ) assert.equal(statusHandler.calledWith(404), true) - assert.equal( - sendHandler.calledWith({ - error: 'not_found' - }), - true - ) + assert.equal(sendHandler.called, true) Sinon.restore() }) @@ -421,11 +301,11 @@ describe('Agency API request handlers', () => { res.locals = getLocals(provider_id) as any Sinon.replace(utils, 'getVehicles', Sinon.fake.rejects('it-broke')) await updateVehicleFail( - { + ({ params: { device_id }, query: { cached: false }, get: Sinon.fake.returns('foo') as any - } as AgencyApiRequest, + } as unknown) as AgencyApiRequest<{ device_id: string }>, res, provider_id, device_id, @@ -434,7 +314,8 @@ describe('Agency API request handlers', () => { assert.equal(statusHandler.calledWith(400), true) assert.equal( sendHandler.calledWith({ - error: 'invalid_data' + error: 'bad_param', + error_description: 'Invalid parameters for vehicle were sent' }), true ) @@ -453,23 +334,18 @@ describe('Agency API request handlers', () => { res.locals = getLocals(provider_id) as any Sinon.replace(utils, 'getVehicles', Sinon.fake.rejects('it-broke')) await updateVehicleFail( - { + ({ params: { device_id }, query: { cached: false }, get: Sinon.fake.returns('foo') as any - } as AgencyApiRequest, + } as unknown) as AgencyApiRequest<{ device_id: string }>, res, provider_id, device_id, 'not found' ) assert.equal(statusHandler.calledWith(404), true) - assert.equal( - sendHandler.calledWith({ - error: 'not_found' - }), - true - ) + assert.equal(sendHandler.called, true) Sinon.restore() }) @@ -485,11 +361,11 @@ describe('Agency API request handlers', () => { res.locals = getLocals(provider_id) as any Sinon.replace(utils, 'getVehicles', Sinon.fake.rejects('it-broke')) await updateVehicleFail( - { + ({ params: { device_id }, query: { cached: false }, get: Sinon.fake.returns('foo') as any - } as AgencyApiRequest, + } as unknown) as AgencyApiRequest<{ device_id: string }>, res, provider_id, device_id, @@ -513,11 +389,11 @@ describe('Agency API request handlers', () => { res.locals = getLocals(provider_id) as any Sinon.replace(utils, 'getVehicles', Sinon.fake.rejects('it-broke')) await getVehiclesByProvider( - { + ({ params: { device_id }, query: { cached: false }, get: Sinon.fake.returns('foo') as any - } as AgencyApiRequest, + } as unknown) as AgencyApiRequest<{ device_id: string }>, res ) assert.equal(statusHandler.calledWith(500), true) @@ -538,12 +414,12 @@ describe('Agency API request handlers', () => { res.locals = getLocals(provider_id) as any Sinon.replace(db, 'readDevice', Sinon.fake.rejects('it-broke')) await updateVehicle( - { + ({ params: { device_id }, query: { cached: false }, get: Sinon.fake.returns('foo') as any, body: { vehicle_id } - } as AgencyApiRequest, + } as unknown) as AgencyApiRequest<{ device_id: string }>, res ) assert.equal(statusHandler.calledWith(404), true) @@ -564,12 +440,12 @@ describe('Agency API request handlers', () => { res.locals = getLocals(provider_id) as any Sinon.replace(db, 'readDevice', Sinon.fake.resolves({ provider_id: 'not-your-provider' })) await updateVehicle( - { + ({ params: { device_id }, query: { cached: false }, get: Sinon.fake.returns('foo') as any, body: { vehicle_id } - } as AgencyApiRequest, + } as unknown) as AgencyApiRequest<{ device_id: string }>, res ) assert.equal(statusHandler.calledWith(404), true) @@ -593,12 +469,12 @@ describe('Agency API request handlers', () => { Sinon.replace(cache, 'writeDevice', Sinon.fake.resolves({ provider_id })) Sinon.replace(stream, 'writeDevice', Sinon.fake.resolves({ provider_id })) await updateVehicle( - { + ({ params: { device_id }, query: { cached: false }, get: Sinon.fake.returns('foo') as any, body: { vehicle_id } - } as AgencyApiRequest, + } as unknown) as AgencyApiRequest<{ device_id: string }>, res ) assert.equal(statusHandler.calledWith(201), true) diff --git a/packages/mds-agency/tests/sandbox-admin-request-handlers.ts b/packages/mds-agency/tests/sandbox-admin-request-handlers.spec.ts similarity index 92% rename from packages/mds-agency/tests/sandbox-admin-request-handlers.ts rename to packages/mds-agency/tests/sandbox-admin-request-handlers.spec.ts index ddd9dddfc..c8e891cd9 100644 --- a/packages/mds-agency/tests/sandbox-admin-request-handlers.ts +++ b/packages/mds-agency/tests/sandbox-admin-request-handlers.spec.ts @@ -1,7 +1,7 @@ import Sinon from 'sinon' import assert from 'assert' -import uuid from 'uuid' -import cache from '@mds-core/mds-cache' +import { uuid } from '@mds-core/mds-utils' +import cache from '@mds-core/mds-agency-cache' import db from '@mds-core/mds-db' import { AgencyApiRequest, AgencyApiResponse } from '../types' import { getCacheInfo, wipeDevice, refreshCache } from '../sandbox-admin-request-handlers' @@ -50,11 +50,11 @@ describe('Sandbox admin request handlers', () => { Sinon.replace(cache, 'wipeDevice', Sinon.fake.rejects('it-failed')) Sinon.replace(db, 'wipeDevice', Sinon.fake.rejects('it-failed')) await wipeDevice( - { + ({ params: { device_id }, query: { cached: false }, get: Sinon.fake.returns('foo') as any - } as AgencyApiRequest, + } as unknown) as AgencyApiRequest<{ device_id: string }>, res ) assert.equal(statusHandler.calledWith(500), true) @@ -73,11 +73,11 @@ describe('Sandbox admin request handlers', () => { Sinon.replace(cache, 'wipeDevice', Sinon.fake.resolves(1)) Sinon.replace(db, 'wipeDevice', Sinon.fake.resolves('it-worked')) await wipeDevice( - { + ({ params: { device_id }, query: { cached: false }, get: Sinon.fake.returns('foo') as any - } as AgencyApiRequest, + } as unknown) as AgencyApiRequest<{ device_id: string }>, res ) assert.equal(statusHandler.calledWith(200), true) @@ -98,11 +98,11 @@ describe('Sandbox admin request handlers', () => { Sinon.replace(db, 'readDeviceIds', Sinon.fake.resolves([1])) Sinon.replace(utils, 'refresh', Sinon.fake.resolves([1])) await refreshCache( - { + ({ params: { device_id }, query: { cached: false }, get: Sinon.fake.returns('foo') as any - } as AgencyApiRequest, + } as unknown) as AgencyApiRequest<{ device_id: string }>, res ) assert.equal(statusHandler.calledWith(200), true) @@ -122,11 +122,11 @@ describe('Sandbox admin request handlers', () => { Sinon.replace(db, 'readDeviceIds', Sinon.fake.rejects('it-fails')) Sinon.replace(utils, 'refresh', Sinon.fake.rejects('it-fails')) await refreshCache( - { + ({ params: { device_id }, query: { cached: false }, get: Sinon.fake.returns('foo') as any - } as AgencyApiRequest, + } as unknown) as AgencyApiRequest<{ device_id: string }>, res ) assert.equal(statusHandler.calledWith(500), true) diff --git a/packages/mds-agency/tsconfig.build.json b/packages/mds-agency/tsconfig.build.json index 76026ac40..993edfc73 100644 --- a/packages/mds-agency/tsconfig.build.json +++ b/packages/mds-agency/tsconfig.build.json @@ -6,10 +6,11 @@ "references": [ { "path": "../../packages/mds-api-helpers/tsconfig.build.json" }, { "path": "../../packages/mds-api-server/tsconfig.build.json" }, - { "path": "../../packages/mds-cache/tsconfig.build.json" }, + { "path": "../../packages/mds-agency-cache/tsconfig.build.json" }, { "path": "../../packages/mds-db/tsconfig.build.json" }, { "path": "../../packages/mds-logger/tsconfig.build.json" }, { "path": "../../packages/mds-providers/tsconfig.build.json" }, + { "path": "../../packages/mds-schema-validators/tsconfig.build.json" }, { "path": "../../packages/mds-stream/tsconfig.build.json" }, { "path": "../../packages/mds-test-data/tsconfig.build.json" }, { "path": "../../packages/mds-types/tsconfig.build.json" }, diff --git a/packages/mds-agency/tsconfig.eslint.json b/packages/mds-agency/tsconfig.eslint.json new file mode 100644 index 000000000..2a400942a --- /dev/null +++ b/packages/mds-agency/tsconfig.eslint.json @@ -0,0 +1 @@ +{ "extends": "../../tsconfig.eslint.json" } diff --git a/packages/mds-agency/types.ts b/packages/mds-agency/types.ts index 1054cbb7a..964d625e5 100644 --- a/packages/mds-agency/types.ts +++ b/packages/mds-agency/types.ts @@ -1,13 +1,51 @@ -import { UUID, Device, VehicleEvent, Telemetry } from '@mds-core/mds-types' +import { UUID, Device, VehicleEvent, Telemetry, Timestamp, Recorded, VEHICLE_STATUS, Stop } from '@mds-core/mds-types' import { MultiPolygon } from 'geojson' -import { ApiRequest, ApiResponse, ApiResponseLocals } from '@mds-core/mds-api-server' +import { ApiRequest, ApiClaims, ApiResponse } from '@mds-core/mds-api-server' +import { Params, ParamsDictionary } from 'express-serve-static-core' -export type AgencyApiRequest = ApiRequest -export interface AgencyApiResponse extends ApiResponse { - locals: ApiResponseLocals & { +export const AGENCY_API_SUPPORTED_VERSIONS = ['0.4.1'] as const +export type AGENCY_API_SUPPORTED_VERSION = typeof AGENCY_API_SUPPORTED_VERSIONS[number] +export const [AGENCY_API_DEFAULT_VERSION] = AGENCY_API_SUPPORTED_VERSIONS + +export type AgencyApiRequest

= ApiRequest

+ +export type AgencyApiAccessTokenScopes = 'admin:all' | 'vehicles:read' + +export type AgencyApiResponse = ApiResponse< + ApiClaims & { provider_id: UUID - } -} + }, + TBody +> + +export type AgencyRegisterVehicleResponse = AgencyApiResponse + +export type AgencyGetVehicleByIdResponse = AgencyApiResponse +export type AgencyGetVehiclesByProviderResponse = AgencyApiResponse +export type AgencyUpdateVehicleResponse = AgencyApiResponse +export type AgencySubmitVehicleEventResponse = AgencyApiResponse<{ + device_id: UUID + status: VEHICLE_STATUS +}> + +export type AgencySubmitVehicleTelemetryResponse = AgencyApiResponse<{ + result: string + recorded: Timestamp + unique: number + failures: string[] +}> + +export type AgencyRegisterStopResponse = AgencyApiResponse> +export type AgencyReadStopResponse = AgencyApiResponse> +export type AgencyReadStopsResponse = AgencyApiResponse<{ + stops: Readonly< + Required< + Stop & { + id: number + } + > + >[] +}> export interface ServiceArea { service_area_id: UUID @@ -35,3 +73,13 @@ export type TelemetryResult = } > >[] + +export type CompositeVehicle = Partial< + Device & { prev_event?: string; updated?: Timestamp; gps?: Recorded['gps'] } +> + +export type PaginatedVehiclesList = { + total: number + links: { first: string; last: string; prev: string | null; next: string | null } + vehicles: (Device & { updated?: number | null; telemetry?: Telemetry | null })[] +} diff --git a/packages/mds-agency/utils.ts b/packages/mds-agency/utils.ts index 73cdad25f..ffadc7e6e 100644 --- a/packages/mds-agency/utils.ts +++ b/packages/mds-agency/utils.ts @@ -1,7 +1,7 @@ import express from 'express' +import { Query } from 'express-serve-static-core' -import { isUUID, isPct, isTimestamp, isFloat, pointInShape, isInsideBoundingBox } from '@mds-core/mds-utils' -import areas from 'ladot-service-areas' +import { isUUID, isPct, isTimestamp, isFloat, isInsideBoundingBox } from '@mds-core/mds-utils' import stream from '@mds-core/mds-stream' import { UUID, @@ -17,18 +17,16 @@ import { PROPULSION_TYPES, EVENT_STATUS_MAP, BoundingBox, - Timestamp, - Recorded, VEHICLE_STATUS, VEHICLE_EVENT } from '@mds-core/mds-types' import db from '@mds-core/mds-db' -import log from '@mds-core/mds-logger' -import cache from '@mds-core/mds-cache' +import logger from '@mds-core/mds-logger' +import cache from '@mds-core/mds-agency-cache' import { isArray } from 'util' -import { VehiclePayload, TelemetryResult } from './types' +import { VehiclePayload, TelemetryResult, CompositeVehicle, PaginatedVehiclesList } from './types' -export function badDevice(device: Device): Partial<{ error: string; error_description: string }> | boolean { +export function badDevice(device: Device): { error: string; error_description: string } | null { if (!device.device_id) { return { error: 'missing_param', @@ -100,21 +98,17 @@ export function badDevice(device: Device): Partial<{ error: string; error_descri // error_description: 'missing string field "model"' // } // } - return false + return null } export async function getVehicles( skip: number, take: number, url: string, - provider_id: string, - reqQuery: { [x: string]: string }, + reqQuery: Query, + provider_id?: string, bbox?: BoundingBox -): Promise<{ - total: number - links: { first: string; last: string; prev: string | null; next: string | null } - vehicles: (Device & { updated?: number | null; telemetry?: Telemetry | null })[] -}> { +): Promise { function fmt(query: { skip: number; take: number }): string { const flat: { [key: string]: number } = { ...reqQuery, ...query } let s = `${url}?` @@ -126,7 +120,7 @@ export async function getVehicles( const rows = await db.readDeviceIds(provider_id) const total = rows.length - log.info(`read ${total} deviceIds in /vehicles`) + logger.info(`read ${total} deviceIds in /vehicles`) const events = await cache.readEvents(rows.map(record => record.device_id)) const eventMap: { [s: string]: VehicleEvent } = {} @@ -148,7 +142,7 @@ export async function getVehicles( throw new Error('device in DB but not in cache') } const event = eventMap[device.device_id] - const status = event ? EVENT_STATUS_MAP[event.event_type] : VEHICLE_STATUSES.removed + const status = event ? EVENT_STATUS_MAP[event.event_type] : VEHICLE_STATUSES.inactive const telemetry = event ? event.telemetry : null const updated = event ? event.timestamp : null return [...acc, { ...device, status, telemetry, updated }] @@ -276,7 +270,7 @@ export async function badEvent(event: VehicleEvent) { if (event.timestamp === undefined) { return { error: 'missing_param', - error_description: 'missing enum field "event_type"' + error_description: 'missing enum field "timestamp"' } } if (!isTimestamp(event.timestamp)) { @@ -351,7 +345,7 @@ export async function badEvent(event: VehicleEvent) { case VEHICLE_EVENTS.cancel_reservation: return null default: - await log.warn(`unsure how to validate mystery event_type ${event.event_type}`) + logger.warn(`unsure how to validate mystery event_type ${event.event_type}`) break } return null // we good @@ -364,55 +358,31 @@ export function lower(s: string): string { return s } -/** - * set the service area on an event based on its gps - */ -export function getServiceArea(event: VehicleEvent): UUID | null { - const tel = event.telemetry - if (tel) { - // TODO: filter service areas by effective date and not having a replacement - const serviceAreaKeys = Object.keys(areas.serviceAreaMap).reverse() - const status = EVENT_STATUS_MAP[event.event_type] - switch (status) { - case VEHICLE_STATUSES.available: - case VEHICLE_STATUSES.unavailable: - case VEHICLE_STATUSES.reserved: - case VEHICLE_STATUSES.trip: - for (const key of serviceAreaKeys) { - const serviceArea = areas.serviceAreaMap[key] - if (pointInShape(tel.gps, serviceArea.area)) { - return key - } - } - break - default: - break - } - } - return null -} - export async function writeTelemetry(telemetry: Telemetry | Telemetry[]) { const recorded_telemetry = await db.writeTelemetry(Array.isArray(telemetry) ? telemetry : [telemetry]) - await Promise.all([cache.writeTelemetry(recorded_telemetry), stream.writeTelemetry(recorded_telemetry)]) + try { + await Promise.all([cache.writeTelemetry(recorded_telemetry), stream.writeTelemetry(recorded_telemetry)]) + } catch (err) { + logger.warn(`Failed to write telemetry to cache/stream, ${err}`) + } return recorded_telemetry } export async function refresh(device_id: UUID, provider_id: UUID): Promise { // TODO all of this back and forth between cache and db is slow const device = await db.readDevice(device_id, provider_id) - // log.info('refresh device', device) + // logger.info('refresh device', device) await cache.writeDevice(device) try { const event = await db.readEvent(device_id) await cache.writeEvent(event) } catch (err) { - await log.info('no events for', device_id, err) + logger.info('no events for', device_id, err) } try { await db.readTelemetry(device_id) } catch (err) { - await log.info('no telemetry for', device_id, err) + logger.info('no telemetry for', device_id, err) } return 'done' } @@ -425,7 +395,7 @@ export async function validateDeviceId(req: express.Request, res: express.Respon /* istanbul ignore if This is never called with no device_id parameter */ if (!device_id) { - await log.warn('agency: missing device_id', req.originalUrl) + logger.warn('agency: missing device_id', req.originalUrl) res.status(400).send({ error: 'missing_param', error_description: 'missing device_id' @@ -433,7 +403,7 @@ export async function validateDeviceId(req: express.Request, res: express.Respon return } if (device_id && !isUUID(device_id)) { - await log.warn('agency: bogus device_id', device_id, req.originalUrl) + logger.warn('agency: bogus device_id', device_id, req.originalUrl) res.status(400).send({ error: 'bad_param', error_description: `invalid device_id ${device_id} is not a UUID` @@ -462,10 +432,10 @@ export async function writeRegisterEvent(device: Device, recorded: number) { // writing to cache and stream is not fatal await Promise.all([cache.writeEvent(recorded_event), stream.writeEvent(recorded_event)]) } catch (err) { - await log.warn('/event exception cache/stream', err) + logger.warn('/event exception cache/stream', err) } } catch (err) { - await log.error('writeRegisterEvent failure', err) + logger.error('writeRegisterEvent failure', err) throw new Error('writeEvent exception db') } } @@ -473,7 +443,7 @@ export async function writeRegisterEvent(device: Device, recorded: number) { export function computeCompositeVehicleData(payload: VehiclePayload) { const { device, event, telemetry } = payload - const composite: Partial['gps'] }> = { + const composite: CompositeVehicle = { ...device } @@ -482,7 +452,8 @@ export function computeCompositeVehicleData(payload: VehiclePayload) { composite.updated = event.timestamp composite.status = (EVENT_STATUS_MAP[event.event_type as VEHICLE_EVENT] || 'unknown') as VEHICLE_STATUS } else { - composite.status = VEHICLE_STATUSES.removed + composite.status = VEHICLE_STATUSES.inactive + composite.prev_event = VEHICLE_EVENTS.deregister } if (telemetry) { if (telemetry.gps) { @@ -499,22 +470,22 @@ const normalizeTelemetry = (telemetry: TelemetryResult) => { return telemetry } -export async function readPayload(store: typeof cache | typeof db, device_id: UUID): Promise { +export async function readPayload(device_id: UUID): Promise { const payload: VehiclePayload = {} try { - payload.device = await store.readDevice(device_id) - } catch (err) { - await log.error(err) - } - try { - payload.event = await store.readEvent(device_id) + payload.device = await db.readDevice(device_id) } catch (err) { - await log.error(err) + logger.error(err) } try { - payload.telemetry = normalizeTelemetry(await store.readTelemetry(device_id)) + payload.event = await cache.readEvent(device_id) + if (payload.event) { + if (payload.event.telemetry) { + payload.telemetry = normalizeTelemetry(payload.event.telemetry) + } + } } catch (err) { - await log.error(err) + logger.error(err) } return payload } diff --git a/packages/mds-api-authorizer/index.ts b/packages/mds-api-authorizer/index.ts index fe43b1c1f..ba42d12cf 100644 --- a/packages/mds-api-authorizer/index.ts +++ b/packages/mds-api-authorizer/index.ts @@ -1,44 +1,86 @@ import express from 'express' -import decode from 'jwt-decode' +import jwt from 'jsonwebtoken' import { UUID } from '@mds-core/mds-types' +import { getEnvVar } from '@mds-core/mds-utils' -export interface ApiAuthorizerClaims { +export interface AuthorizerClaims { principalId: string scope: string provider_id: UUID | null user_email: string | null + jurisdictions: string | null } -const { - TOKEN_PROVIDER_ID_CLAIM = 'https://ladot.io/provider_id', - TOKEN_USER_EMAIL_CLAIM = 'https://ladot.io/user_email' -} = process.env +export type Authorizer = (authorization: string) => AuthorizerClaims | null +export type ApiAuthorizer = (req: express.Request) => AuthorizerClaims | null -export type ApiAuthorizer = (req: express.Request) => ApiAuthorizerClaims | null +export const CustomClaim = (claim: 'provider_id' | 'user_email' | 'jurisdictions') => { + const { TOKEN_CUSTOM_CLAIM_NAMESPACE } = getEnvVar({ + TOKEN_CUSTOM_CLAIM_NAMESPACE: 'https://openmobilityfoundation.org' + }) + return `${TOKEN_CUSTOM_CLAIM_NAMESPACE}${TOKEN_CUSTOM_CLAIM_NAMESPACE.endsWith('/') ? '' : '/'}${claim}` +} -export const AuthorizationHeaderApiAuthorizer: ApiAuthorizer = req => { - if (req.headers && req.headers.authorization) { - const [scheme, token] = req.headers.authorization.split(' ') - const decoders: { [scheme: string]: () => ApiAuthorizerClaims } = { - bearer: () => { - const { - sub: principalId, - scope, - [TOKEN_PROVIDER_ID_CLAIM]: provider_id = null, - [TOKEN_USER_EMAIL_CLAIM]: user_email = null, - ...claims - } = decode(token) - return { principalId, scope, provider_id, user_email, ...claims } - }, - basic: () => { - const [principalId, scope] = Buffer.from(token, 'base64') - .toString() - .split('|') - return { principalId, scope, provider_id: principalId, user_email: null } - } +export const ProviderIdClaim = () => { + const { TOKEN_PROVIDER_ID_CLAIM } = getEnvVar({ + TOKEN_PROVIDER_ID_CLAIM: CustomClaim('provider_id') + }) + return TOKEN_PROVIDER_ID_CLAIM +} + +export const UserEmailClaim = () => { + const { TOKEN_USER_EMAIL_CLAIM } = getEnvVar({ + TOKEN_USER_EMAIL_CLAIM: CustomClaim('user_email') + }) + return TOKEN_USER_EMAIL_CLAIM +} + +export const JurisdictionsClaim = () => { + const { TOKEN_JURISDICTIONS_CLAIM } = getEnvVar({ + TOKEN_JURISDICTIONS_CLAIM: CustomClaim('jurisdictions') + }) + return TOKEN_JURISDICTIONS_CLAIM +} + +const decode = (token: string) => { + const decoded = jwt.decode(token) + return typeof decoded === 'string' || decoded === null ? {} : decoded +} + +const decoders: { [scheme: string]: (token: string) => AuthorizerClaims } = { + bearer: (token: string) => { + const { + sub: principalId, + scope, + [ProviderIdClaim()]: provider_id = null, + [UserEmailClaim()]: user_email = null, + [JurisdictionsClaim()]: jurisdictions = null, + ...claims + } = decode(token) + + return { + principalId, + scope, + provider_id, + user_email, + jurisdictions, + ...claims } - const decoder = decoders[scheme.toLowerCase()] - return decoder ? decoder() : null + }, + basic: (token: string) => { + const [principalId, scope] = Buffer.from(token, 'base64').toString().split('|') + return { principalId, scope, provider_id: principalId, user_email: principalId, jurisdictions: principalId } } - return null } + +const BaseAuthorizer: Authorizer = authorization => { + const [scheme, token] = authorization.split(' ') + const decoder = decoders[scheme.toLowerCase()] + return decoder ? decoder(token) : null +} + +export const AuthorizationHeaderApiAuthorizer: ApiAuthorizer = req => { + return req.headers?.authorization ? BaseAuthorizer(req.headers.authorization) : null +} + +export const WebSocketAuthorizer = BaseAuthorizer diff --git a/packages/mds-api-authorizer/package.json b/packages/mds-api-authorizer/package.json index 7c5997be1..1733c216b 100644 --- a/packages/mds-api-authorizer/package.json +++ b/packages/mds-api-authorizer/package.json @@ -1,6 +1,6 @@ { "name": "@mds-core/mds-api-authorizer", - "version": "0.1.18", + "version": "0.1.26", "description": "MDS API Authorizer", "main": "dist/index.js", "types": "dist/index.d.ts", @@ -10,19 +10,21 @@ "scripts": { "build": "tsc --build tsconfig.build.json", "test": "yarn test:eslint && yarn test:unit", - "test:eslint": "eslint --fix --ignore-path ../../.gitignore '**/*.ts'", - "test:unit": "DOTENV_CONFIG_PATH=../../.env nyc --check-coverage --exclude tests --extension .ts --lines 84 --reporter=text --reporter=html ts-mocha --project ../../tsconfig.json --require tsconfig-paths/register --require source-map-support/register --require dotenv/config --recursive --timeout 5000 tests/**/*.ts" + "test:eslint": "eslint --ignore-path ../../.gitignore '**/*.ts'", + "test:unit": "DOTENV_CONFIG_PATH=../../.env nyc ts-mocha --project ../../tsconfig.json" }, "keywords": [ "mds" ], "author": "City of Los Angeles", + "license": "Apache-2.0", "dependencies": { - "@mds-core/mds-types": "0.1.15", - "jwt-decode": "2.2.0" - }, - "devDependencies": { - "@mds-core/mds-test-data": "0.1.18" - }, - "license": "Apache-2.0" + "@mds-core/mds-test-data": "0.1.26", + "@mds-core/mds-types": "0.1.23", + "@mds-core/mds-utils": "0.1.26", + "@types/express": "4.17.6", + "@types/jsonwebtoken": "8.5.0", + "express": "4.17.1", + "jsonwebtoken": "8.5.1" + } } diff --git a/packages/mds-api-authorizer/tests/index.spec.ts b/packages/mds-api-authorizer/tests/index.spec.ts index 20401716f..095cf2044 100644 --- a/packages/mds-api-authorizer/tests/index.spec.ts +++ b/packages/mds-api-authorizer/tests/index.spec.ts @@ -1,41 +1,95 @@ import test from 'unit.js' import { MOCHA_PROVIDER_ID } from '@mds-core/mds-providers' import express from 'express' -import { AuthorizationHeaderApiAuthorizer } from '../index' +import jwt from 'jsonwebtoken' +import { uuid } from '@mds-core/mds-utils' +import { AuthorizationHeaderApiAuthorizer, WebSocketAuthorizer, CustomClaim } from '../index' const PROVIDER_SCOPES = 'admin:all' -const ADMIN_AUTH = `basic ${Buffer.from(`${MOCHA_PROVIDER_ID}|${PROVIDER_SCOPES}`).toString('base64')}` +const PROVIDER_SUBJECT = uuid() +const PROVIDER_EMAIL = 'user@test.ai' + +const { env } = process + +const Basic = () => `Basic ${Buffer.from(`${MOCHA_PROVIDER_ID}|${PROVIDER_SCOPES}`).toString('base64')}` + +const Bearer = () => + `Bearer ${jwt.sign( + { + sub: PROVIDER_SUBJECT, + [CustomClaim('user_email')]: PROVIDER_EMAIL, + [CustomClaim('provider_id')]: MOCHA_PROVIDER_ID, + scope: PROVIDER_SCOPES + }, + 'secret' + )}` describe('Test API Authorizer', () => { - it('No Authorizaton', done => { - const authorizer = AuthorizationHeaderApiAuthorizer({} as express.Request) - test.value(authorizer).is(null) - done() + before(() => { + process.env = { TOKEN_CUSTOM_CLAIM_NAMESPACE: 'https://test.ai/' } }) - it('Invalid Authorizaton Scheme', done => { - const authorizer = AuthorizationHeaderApiAuthorizer({ headers: { authorization: 'invalid' } } as express.Request) - test.value(authorizer).is(null) - done() + describe('Authorizaton Header Authorizer', () => { + it('No Authorizaton', async () => { + test.value(AuthorizationHeaderApiAuthorizer({} as express.Request)).is(null) + }) + + it('Invalid Authorizaton Scheme', async () => { + test + .value( + AuthorizationHeaderApiAuthorizer({ + headers: { authorization: 'invalid' } + } as express.Request) + ) + .is(null) + }) + + it('Basic Authorizaton', async () => { + test + .object( + AuthorizationHeaderApiAuthorizer({ + headers: { authorization: Basic() } + } as express.Request) + ) + .hasProperty('principalId', MOCHA_PROVIDER_ID) + .hasProperty('provider_id', MOCHA_PROVIDER_ID) + .hasProperty('scope', PROVIDER_SCOPES) + }) + + it('Bearer Authorizaton', async () => { + test + .object( + AuthorizationHeaderApiAuthorizer({ + headers: { authorization: Bearer() } + } as express.Request) + ) + .hasProperty('principalId', PROVIDER_SUBJECT) + .hasProperty('provider_id', MOCHA_PROVIDER_ID) + .hasProperty('user_email', PROVIDER_EMAIL) + .hasProperty('scope', PROVIDER_SCOPES) + }) }) - it('Basic Authorizaton', done => { - const authorizer = AuthorizationHeaderApiAuthorizer({ headers: { authorization: ADMIN_AUTH } } as express.Request) - test - .object(authorizer) - .hasProperty('principalId', MOCHA_PROVIDER_ID) - .hasProperty('scope', PROVIDER_SCOPES) - done() + describe('WebSocket Authorizer', () => { + it('Basic Authorization', async () => { + test + .object(WebSocketAuthorizer(Basic())) + .hasProperty('principalId', MOCHA_PROVIDER_ID) + .hasProperty('provider_id', MOCHA_PROVIDER_ID) + .hasProperty('scope', PROVIDER_SCOPES) + }) + + it('Bearer Authorization', async () => { + test + .object(WebSocketAuthorizer(Bearer())) + .hasProperty('principalId', PROVIDER_SUBJECT) + .hasProperty('provider_id', MOCHA_PROVIDER_ID) + .hasProperty('user_email', PROVIDER_EMAIL) + .hasProperty('scope', PROVIDER_SCOPES) + }) }) - it('Bearer Authorizaton', done => { - const authorizer = AuthorizationHeaderApiAuthorizer({ - headers: { authorization: ADMIN_AUTH } - } as express.Request) - test - .object(authorizer) - .hasProperty('principalId', 'c8051767-4b14-4794-abc1-85aad48baff1') - .hasProperty('scope', PROVIDER_SCOPES) - done() + after(() => { + process.env = env }) }) diff --git a/packages/mds-api-authorizer/tsconfig.build.json b/packages/mds-api-authorizer/tsconfig.build.json index bf2cad597..e21009cdc 100644 --- a/packages/mds-api-authorizer/tsconfig.build.json +++ b/packages/mds-api-authorizer/tsconfig.build.json @@ -5,6 +5,7 @@ }, "references": [ { "path": "../../packages/mds-test-data/tsconfig.build.json" }, - { "path": "../../packages/mds-types/tsconfig.build.json" } + { "path": "../../packages/mds-types/tsconfig.build.json" }, + { "path": "../../packages/mds-utils/tsconfig.build.json" } ] } diff --git a/packages/mds-api-authorizer/tsconfig.eslint.json b/packages/mds-api-authorizer/tsconfig.eslint.json new file mode 100644 index 000000000..2a400942a --- /dev/null +++ b/packages/mds-api-authorizer/tsconfig.eslint.json @@ -0,0 +1 @@ +{ "extends": "../../tsconfig.eslint.json" } diff --git a/packages/mds-api-helpers/index.ts b/packages/mds-api-helpers/index.ts index 148f42563..7706e313b 100644 --- a/packages/mds-api-helpers/index.ts +++ b/packages/mds-api-helpers/index.ts @@ -16,24 +16,13 @@ import urls from 'url' import express from 'express' +import { parseObjectProperties, ParseObjectPropertiesOptions } from '@mds-core/mds-utils' interface PagingParams { skip: number take: number } -export const asPagingParams: >( - params: T -) => T & PagingParams = params => { - const [DEFAULT_PAGE_SIZE, MAX_PAGE_SIZE] = [100, 1000] - const [skip, take] = [params.skip, params.take].map(Number) - return { - ...params, - skip: Number.isNaN(skip) || skip <= 0 ? 0 : skip, - take: Number.isNaN(take) || take <= 0 ? DEFAULT_PAGE_SIZE : Math.min(take, MAX_PAGE_SIZE) - } -} - const jsonApiLink = (req: express.Request, skip: number, take: number): string => urls.format({ protocol: req.get('x-forwarded-proto') || req.protocol, @@ -54,3 +43,18 @@ export const asJsonApiLinks = (req: express.Request, skip: number, take: number, } return undefined } + +export const parseRequest = (req: express.Request, options?: ParseObjectPropertiesOptions) => { + const { keys: query } = parseObjectProperties(req.query, options) + const { keys: params } = parseObjectProperties(req.params, options) + return { params, query } +} + +export const parsePagingQueryParams = (req: express.Request) => { + const [DEFAULT_PAGE_SIZE, MAX_PAGE_SIZE] = [100, 1000] + const { skip = 0, take = DEFAULT_PAGE_SIZE } = parseRequest(req, { parser: Number }).query('skip', 'take') + return { + skip: Number.isNaN(skip) ? 0 : Math.max(0, skip), + take: Number.isNaN(take) ? DEFAULT_PAGE_SIZE : Math.min(take, MAX_PAGE_SIZE) + } +} diff --git a/packages/mds-api-helpers/package.json b/packages/mds-api-helpers/package.json index 4ba26476a..8754bbe44 100644 --- a/packages/mds-api-helpers/package.json +++ b/packages/mds-api-helpers/package.json @@ -1,6 +1,6 @@ { "name": "@mds-core/mds-api-helpers", - "version": "0.1.18", + "version": "0.1.26", "description": "Mobility Data Specification helper functions", "keywords": [ "mds", @@ -9,10 +9,11 @@ "author": "City of Los Angeles", "license": "Apache-2.0", "dependencies": { - "@mds-core/mds-cache": "0.1.18", - "@mds-core/mds-db": "0.1.18", - "@mds-core/mds-types": "0.1.15", - "@mds-core/mds-utils": "0.1.18", + "@mds-core/mds-agency-cache": "0.1.26", + "@mds-core/mds-db": "0.1.26", + "@mds-core/mds-types": "0.1.23", + "@mds-core/mds-utils": "0.1.26", + "@types/express": "4.17.6", "express": "4.17.1" }, "main": "dist/index.js", @@ -23,7 +24,7 @@ "scripts": { "build": "tsc --build tsconfig.build.json", "test": "yarn test:eslint && yarn test:unit", - "test:eslint": "eslint --fix --ignore-path ../../.gitignore '**/*.ts'", + "test:eslint": "eslint --ignore-path ../../.gitignore '**/*.ts'", "test:unit": "exit 0" } } diff --git a/packages/mds-api-helpers/tsconfig.build.json b/packages/mds-api-helpers/tsconfig.build.json index 52fb0c469..3e1e03f08 100644 --- a/packages/mds-api-helpers/tsconfig.build.json +++ b/packages/mds-api-helpers/tsconfig.build.json @@ -4,7 +4,7 @@ "outDir": "dist" }, "references": [ - { "path": "../../packages/mds-cache/tsconfig.build.json" }, + { "path": "../../packages/mds-agency-cache/tsconfig.build.json" }, { "path": "../../packages/mds-db/tsconfig.build.json" }, { "path": "../../packages/mds-types/tsconfig.build.json" }, { "path": "../../packages/mds-utils/tsconfig.build.json" } diff --git a/packages/mds-api-helpers/tsconfig.eslint.json b/packages/mds-api-helpers/tsconfig.eslint.json new file mode 100644 index 000000000..2a400942a --- /dev/null +++ b/packages/mds-api-helpers/tsconfig.eslint.json @@ -0,0 +1 @@ +{ "extends": "../../tsconfig.eslint.json" } diff --git a/packages/mds-api-server/index.ts b/packages/mds-api-server/index.ts index 6013ff75f..fb4282889 100644 --- a/packages/mds-api-server/index.ts +++ b/packages/mds-api-server/index.ts @@ -1,25 +1,53 @@ import morgan from 'morgan' import bodyParser from 'body-parser' import express from 'express' -import cors from 'cors' +import CorsMiddleware from 'cors' import logger from '@mds-core/mds-logger' import { pathsFor, AuthorizationError } from '@mds-core/mds-utils' -import { AuthorizationHeaderApiAuthorizer, ApiAuthorizer, ApiAuthorizerClaims } from '@mds-core/mds-api-authorizer' -import { AccessTokenScope } from '@mds-core/mds-types' +import { + AuthorizationHeaderApiAuthorizer, + ApiAuthorizer, + AuthorizerClaims, + ProviderIdClaim, + UserEmailClaim, + JurisdictionsClaim +} from '@mds-core/mds-api-authorizer' +import { Params, ParamsDictionary } from 'express-serve-static-core' -export type ApiRequest = express.Request +export type ApiRequest

= express.Request

-export interface ApiResponseLocals { - claims: ApiAuthorizerClaims | null - scopes: AccessTokenScope[] +export type ApiQuery = { + query: Partial< + { + [P in Q1]: string + } + > & + Partial< + { + [P in Q2[number]]: string | string[] + } + > & { [x: string]: never } } -export interface ApiResponse extends express.Response { - locals: ApiResponseLocals - status: (code: number) => ApiResponse - send: (body: T) => ApiResponse +export interface ApiResponse + extends express.Response< + B | { error: unknown; error_description?: string; error_details?: string[] } | { errors: unknown[] } + > { + locals: L } +export type ApiClaims = { + claims: AuthorizerClaims | null + scopes: AccessTokenScope[] +} + +export type ApiVersion = { version: TVersion } + +export type ApiVersionedResponse = ApiResponse< + L & ApiVersion, + TBody & ApiVersion +> + const about = () => { const { versions: { node }, @@ -41,99 +69,241 @@ const about = () => { } } -interface ApiServerOptions { - authorizer: ApiAuthorizer - handleCors: boolean +export const RequestLoggingMiddleware = (): express.RequestHandler => + morgan( + (tokens, req: ApiRequest, res: ApiResponse>) => + [ + ...(res.locals.claims?.provider_id ? [res.locals.claims.provider_id] : []), + tokens.method(req, res), + tokens.url(req, res), + tokens.status(req, res), + tokens.res(req, res, 'content-length'), + '-', + tokens['response-time'](req, res), + 'ms' + ].join(' '), + { + skip: (req: ApiRequest, res: ApiResponse) => { + // By default only log 400/500 errors + const { API_REQUEST_LOG_LEVEL = 0 } = process.env + return res.statusCode < Number(API_REQUEST_LOG_LEVEL) + }, + // Use logger, but remove extra line feed added by morgan stream option + stream: { write: msg => logger.info(msg.slice(0, -1)) } + } + ) + +export const JsonBodyParserMiddleware = (options: bodyParser.OptionsJson) => bodyParser.json(options) + +export const MaintenanceModeMiddleware = () => (req: ApiRequest, res: ApiResponse, next: express.NextFunction) => { + if (process.env.MAINTENANCE) { + return res.status(503).send(about()) + } + next() +} + +type AuthorizerMiddlewareOptions = { authorizer: ApiAuthorizer } +export const AuthorizerMiddleware = ({ + authorizer = AuthorizationHeaderApiAuthorizer +}: Partial = {}) => ( + req: ApiRequest, + res: ApiResponse>, + next: express.NextFunction +) => { + const claims = authorizer(req) + res.locals.claims = claims + res.locals.scopes = claims && claims.scope ? (claims.scope.split(' ') as AccessTokenScope[]) : [] + next() } +const MinorVersion = (version: string) => { + const [major, minor] = version.split('.') + return `${major}.${minor}` +} + +export const ApiVersionMiddleware = (mimeType: string, versions: readonly TVersion[]) => ({ + withDefaultVersion: (preferred: TVersion) => async ( + req: ApiRequest, + res: ApiVersionedResponse, + next: express.NextFunction + ) => { + // Parse the Accept header into a list of values separated by commas + const { accept: header } = req.headers + const values = header ? header.split(',').map(value => value.trim()) : [] + + // Parse the version and q properties from all values matching the specified mime type + const accepted = values.reduce<{ version: string; q: number }[] | null>((accept, value) => { + const [mime, ...properties] = value.split(';').map(property => property.trim()) + return mime === mimeType + ? (accept ?? []).concat({ + ...properties.reduce<{ version: string; q: number }>( + (info, property) => { + const [key, val] = property.split('=').map(keyvalue => keyvalue.trim()) + return { + ...info, + version: key === 'version' ? val : info.version, + q: key === 'q' ? Number(val) : info.q + } + }, + { version: preferred, q: 1.0 } + ) + }) + : accept + }, null) ?? [ + { + version: preferred, + q: 1.0 + } + ] + + // Determine if any of the requested versions are supported + const supported = accepted + .map(info => ({ + ...info, + latest: versions.reduce((latest, version) => { + if (MinorVersion(info.version) === MinorVersion(version)) { + if (latest) { + return latest > version ? latest : version + } + return version + } + return latest + }, undefined) + })) + .filter(info => info.latest !== undefined) + + if (req.method === 'OPTIONS') { + /* If the incoming request is an OPTIONS request, + * immediately respond with the negotiated version. + * If the client did not negotiate a valid version, fall-through to a 406 response. + */ + if (supported.length > 0) { + const [{ latest }] = supported.sort((a, b) => b.q - a.q) + if (latest) { + res.locals.version = latest + res.setHeader('Content-Type', `${mimeType};version=${MinorVersion(latest)}`) + return res.status(200).send() + } + } + } else if (supported.length > 0) { + /* If the incoming request is a non-OPTIONS request, + * set the negotiated version header, and forward the request to the next handler. + * If the client did not negotiate a valid version, fall-through to provide the "preferred" version, + * or, if they requested an invalid version, respond with a 406. + */ + const [{ latest }] = supported.sort((a, b) => b.q - a.q) + if (latest) { + res.locals.version = latest + res.setHeader('Content-Type', `${mimeType};version=${MinorVersion(latest)}`) + return next() + } + } else if (values.length === 0) { + /* + * If no versions specified by the client for a non-OPTIONS request, + * fall-back to latest internal version supported + */ + res.locals.version = preferred + res.setHeader('Content-Type', `${mimeType};version=${MinorVersion(preferred)}`) + return next() + } + + // 406 - Not Acceptable + return res.sendStatus(406) + } +}) + +export const AboutRequestHandler = async (req: ApiRequest, res: ApiResponse) => { + return res.status(200).send(about()) +} + +export const HealthRequestHandler = async (req: ApiRequest, res: ApiResponse) => { + return res.status(200).send({ + ...about(), + process: process.pid, + uptime: process.uptime(), + memory: process.memoryUsage() + }) +} + +const serverVersion = () => { + const { npm_package_name, npm_package_version, npm_package_git_commit } = process.env + return npm_package_name && npm_package_version + ? `${npm_package_name} v${npm_package_version} (${npm_package_git_commit || 'local'})` + : 'Server' +} + +type HttpServerOptions = Partial<{ + port: string | number +}> + +export const HttpServer = (api: express.Express, options: HttpServerOptions = {}) => { + const { HTTP_KEEP_ALIVE_TIMEOUT = 15000, HTTP_HEADERS_TIMEOUT = 20000 } = process.env + + const port = Number(options.port || process.env.PORT || 4000) + + const server = api.listen(port, () => { + logger.info( + `${serverVersion()} running on port ${port}; Timeouts(${HTTP_KEEP_ALIVE_TIMEOUT}/${HTTP_HEADERS_TIMEOUT})` + ) + }) + + // Increase default timeout values to mitigate spurious 503 errors from Istio + server.keepAliveTimeout = Number(HTTP_KEEP_ALIVE_TIMEOUT) + server.headersTimeout = Number(HTTP_HEADERS_TIMEOUT) + + return server +} + +type CorsMiddlewareOptions = Omit + export const ApiServer = ( api: (server: express.Express) => express.Express, - options: Partial = {}, + { authorizer, ...corsOptions }: Partial = {}, app: express.Express = express() ): express.Express => { - const { authorizer, handleCors } = { - authorizer: AuthorizationHeaderApiAuthorizer, - handleCors: false, - ...options - } + logger.info(`${serverVersion()} starting`) + // Log the custom authorization namespace/claims + const claims = [ProviderIdClaim, UserEmailClaim, JurisdictionsClaim] + claims.forEach(claim => { + logger.info(`${serverVersion()} using authorization claim ${claim()}`) + }) // Disable x-powered-by header app.disable('x-powered-by') // Middleware app.use( - // - // Request logging - // - morgan('tiny', { - skip: (req, res) => { - // By default only log 400/500 errors - const { API_REQUEST_LOG_LEVEL = 400 } = process.env - return res.statusCode < Number(API_REQUEST_LOG_LEVEL) - }, - // Use logger, but remove extra line feed added by morgan stream option - stream: { write: msg => logger.info(msg.slice(0, -1)) } - }), - // - // JSON body parser - // - bodyParser.json({ limit: '5mb' }), - // - // CORS - // - handleCors - ? cors() // Server handles CORS - : (req: ApiRequest, res: ApiResponse, next: express.NextFunction) => { - res.header('Access-Control-Allow-Origin', '*') - next() - }, - // - // Maintenance - // - (req: ApiRequest, res: ApiResponse, next: express.NextFunction) => { - if (process.env.MAINTENANCE) { - return res.status(503).send(about()) - } - next() - }, - // - // Authorizer - // - (req: ApiRequest, res: ApiResponse, next: express.NextFunction) => { - const claims = authorizer(req) - res.locals.claims = claims - res.locals.scopes = claims && claims.scope ? (claims.scope.split(' ') as AccessTokenScope[]) : [] - next() - } + RequestLoggingMiddleware(), + CorsMiddleware({ preflightContinue: true, ...corsOptions }), + JsonBodyParserMiddleware({ limit: '5mb' }), + MaintenanceModeMiddleware(), + AuthorizerMiddleware({ authorizer }) ) - app.get(pathsFor('/'), async (req: ApiRequest, res: ApiResponse) => { - // 200 OK - return res.status(200).send(about()) - }) - - app.get(pathsFor('/health'), async (req: ApiRequest, res: ApiResponse) => { - // 200 OK - return res.status(200).send({ - ...about(), - process: process.pid, - uptime: process.uptime(), - memory: process.memoryUsage() - }) - }) + // Routes + app.get(pathsFor('/'), AboutRequestHandler) + app.get(pathsFor('/health'), HealthRequestHandler) return api(app) } +export type AccessTokenScopeValidator = ( + scopes: AccessTokenScope[], + claims: AuthorizerClaims | null +) => boolean | Promise + /* istanbul ignore next */ -export const checkAccess = (validator: (scopes: AccessTokenScope[]) => boolean) => ( - req: ApiRequest, - res: ApiResponse, - next: express.NextFunction -) => { - if (process.env.VERIFY_ACCESS_TOKEN_SCOPE === 'false' || validator(res.locals.scopes)) { - next() - } else { - res.status(403).send({ error: new AuthorizationError('no access without scope', { claims: res.locals.claims }) }) - } -} +export const checkAccess = ( + validator: AccessTokenScopeValidator +) => + process.env.VERIFY_ACCESS_TOKEN_SCOPE === 'false' + ? async (req: ApiRequest, res: ApiResponse>, next: express.NextFunction) => { + next() // Bypass + } + : async (req: ApiRequest, res: ApiResponse>, next: express.NextFunction) => { + const { scopes, claims } = res.locals + const valid = await validator(scopes, claims) + return valid + ? next() + : res.status(403).send({ error: new AuthorizationError('no access without scope', { claims }) }) + } diff --git a/packages/mds-api-server/package.json b/packages/mds-api-server/package.json index 3471e9b89..80c6df9ad 100644 --- a/packages/mds-api-server/package.json +++ b/packages/mds-api-server/package.json @@ -1,6 +1,6 @@ { "name": "@mds-core/mds-api-server", - "version": "0.1.18", + "version": "0.1.26", "description": "MDS API Server", "main": "dist/index.js", "types": "dist/index.d.ts", @@ -10,20 +10,25 @@ "scripts": { "build": "tsc --build tsconfig.build.json", "test": "yarn test:eslint && yarn test:unit", - "test:eslint": "eslint --fix --ignore-path ../../.gitignore '**/*.ts'", - "test:unit": "DOTENV_CONFIG_PATH=../../.env nyc --check-coverage --exclude tests --extension .ts --lines 90 --reporter=text --reporter=html ts-mocha --project ../../tsconfig.json --require tsconfig-paths/register --require source-map-support/register --require dotenv/config --recursive --timeout 5000 tests/**/*.ts" + "test:eslint": "eslint --ignore-path ../../.gitignore '**/*.ts'", + "test:unit": "DOTENV_CONFIG_PATH=../../.env nyc ts-mocha --project ../../tsconfig.json" }, "keywords": [ "mds" ], "author": "City of Los Angeles", + "license": "Apache-2.0", "dependencies": { - "@mds-core/mds-api-authorizer": "0.1.18", - "@mds-core/mds-utils": "0.1.18", + "@mds-core/mds-api-authorizer": "0.1.26", + "@mds-core/mds-logger": "0.1.24", + "@mds-core/mds-utils": "0.1.26", + "@types/body-parser": "1.19.0", + "@types/cors": "2.8.6", + "@types/express": "4.17.6", + "@types/morgan": "1.9.0", "body-parser": "1.19.0", "cors": "2.8.5", "express": "4.17.1", - "morgan": "1.9.1" - }, - "license": "Apache-2.0" + "morgan": "1.10.0" + } } diff --git a/packages/mds-api-server/tests/index.spec.ts b/packages/mds-api-server/tests/index.spec.ts index caa64e67f..c8d38be3d 100644 --- a/packages/mds-api-server/tests/index.spec.ts +++ b/packages/mds-api-server/tests/index.spec.ts @@ -18,9 +18,22 @@ import supertest from 'supertest' import test from 'unit.js' -import { ApiServer } from '../index' +import { ApiServer, HttpServer, ApiVersionMiddleware, ApiVersionedResponse } from '../index' -const request = supertest(ApiServer(app => app)) +const TEST_API_MIME_TYPE = 'application/vnd.mds.test+json' +const TEST_API_VERSIONS = ['0.1.0', '0.2.0'] as const +type TEST_API_VERSION = typeof TEST_API_VERSIONS[number] +const [DEFAULT_TEST_API_VERSION, ALTERNATE_TEST_API_VERSION] = TEST_API_VERSIONS + +const api = ApiServer(app => { + app.use(ApiVersionMiddleware(TEST_API_MIME_TYPE, TEST_API_VERSIONS).withDefaultVersion(DEFAULT_TEST_API_VERSION)) + app.get('/api-version-middleware-test', (req, res: ApiVersionedResponse) => + res.status(200).send({ version: res.locals.version }) + ) + return app +}) + +const request = supertest(api) const APP_JSON = 'application/json; charset=utf-8' @@ -118,4 +131,85 @@ describe('Testing API Server', () => { done(err) }) }) + + it('verifies keepAliveTimeout setting', done => { + let error + process.env.HTTP_KEEP_ALIVE_TIMEOUT = '3000' + const server = HttpServer(api) + try { + test.value(server.keepAliveTimeout).is(Number(process.env.HTTP_KEEP_ALIVE_TIMEOUT)) + } catch (err) { + error = err + } + server.close() + done(error) + }) + + it('verifies version middleware OPTIONS request (version not acceptable)', done => { + request + .options('/api-version-middleware-test') + .set('accept', `${TEST_API_MIME_TYPE};version=0.4;q=.9,${TEST_API_MIME_TYPE};version=0.5;`) + .expect(406) + .end((err, result) => { + test.value(result.text).is('Not Acceptable') + done(err) + }) + }) + + it('verifies version middleware OPTIONS request (with versions)', done => { + request + .options('/api-version-middleware-test') + .set('accept', `${TEST_API_MIME_TYPE};version=0.2`) + .expect(200) + .end((err, result) => { + test.value(result.header['content-type']).is(`${TEST_API_MIME_TYPE};version=0.2`) + done(err) + }) + }) + + it('verifies version middleware (default version)', done => { + request + .get('/api-version-middleware-test') + .expect(200) + .end((err, result) => { + test.value(result.header['content-type']).is(`${TEST_API_MIME_TYPE}; charset=utf-8; version=0.1`) + test.value(result.body.version).is(DEFAULT_TEST_API_VERSION) + done(err) + }) + }) + + it('verifies version middleware (with versions)', done => { + request + .get('/api-version-middleware-test') + .set('accept', `${TEST_API_MIME_TYPE};version=0.2`) + .expect(200) + .end((err, result) => { + test.value(result.header['content-type']).is(`${TEST_API_MIME_TYPE}; charset=utf-8; version=0.2`) + test.value(result.body.version).is(ALTERNATE_TEST_API_VERSION) + done(err) + }) + }) + + it('verifies version middleware (versions with q)', done => { + request + .get('/api-version-middleware-test') + .set('accept', `${TEST_API_MIME_TYPE};version=0.2;q=.9,${TEST_API_MIME_TYPE};version=0.1;`) + .expect(200) + .end((err, result) => { + test.value(result.header['content-type']).is(`${TEST_API_MIME_TYPE}; charset=utf-8; version=0.1`) + test.value(result.body.version).is(DEFAULT_TEST_API_VERSION) + done(err) + }) + }) + + it('verifies version middleware (version not acceptable)', done => { + request + .get('/api-version-middleware-test') + .set('accept', `${TEST_API_MIME_TYPE};version=0.4;q=.9,${TEST_API_MIME_TYPE};version=0.5;`) + .expect(406) + .end((err, result) => { + test.value(result.text).is('Not Acceptable') + done(err) + }) + }) }) diff --git a/packages/mds-api-server/tsconfig.build.json b/packages/mds-api-server/tsconfig.build.json index 6d1fe3697..be6e43bd0 100644 --- a/packages/mds-api-server/tsconfig.build.json +++ b/packages/mds-api-server/tsconfig.build.json @@ -5,6 +5,7 @@ }, "references": [ { "path": "../../packages/mds-api-authorizer/tsconfig.build.json" }, + { "path": "../../packages/mds-logger/tsconfig.build.json" }, { "path": "../../packages/mds-utils/tsconfig.build.json" } ] } diff --git a/packages/mds-api-server/tsconfig.eslint.json b/packages/mds-api-server/tsconfig.eslint.json new file mode 100644 index 000000000..2a400942a --- /dev/null +++ b/packages/mds-api-server/tsconfig.eslint.json @@ -0,0 +1 @@ +{ "extends": "../../tsconfig.eslint.json" } diff --git a/packages/mds-audit/api.ts b/packages/mds-audit/api.ts index f79748b3e..dc3f29a02 100644 --- a/packages/mds-audit/api.ts +++ b/packages/mds-audit/api.ts @@ -14,13 +14,22 @@ limitations under the License. */ +import db from '@mds-core/mds-db' import express from 'express' -import uuid from 'uuid' -import log from '@mds-core/mds-logger' -import urls from 'url' import { + uuid, pathsFor, seconds, + AuthorizationError, + ConflictError, + NotFoundError, + ServerError, + UnsupportedTypeError +} from '@mds-core/mds-utils' +import logger from '@mds-core/mds-logger' +import urls from 'url' + +import { isValidAuditDeviceId, isValidAuditEventId, isValidAuditEventType, @@ -32,26 +41,21 @@ import { isValidVehicleEventType, isValidAuditIssueCode, isValidAuditNote, - ValidationError, - AuthorizationError, - ConflictError, - NotFoundError, - ServerError -} from '@mds-core/mds-utils' + ValidationError +} from '@mds-core/mds-schema-validators' + import { providerName } from '@mds-core/mds-providers' // map of uuids -> obj import { AUDIT_EVENT_TYPES, - AuditDetails, AuditEvent, - Recorded, + EVENT_STATUS_MAP, Timestamp, Telemetry, TelemetryData, - VehicleEvent, - VehicleEventSummary + VEHICLE_EVENT } from '@mds-core/mds-types' -import { asPagingParams, asJsonApiLinks } from '@mds-core/mds-api-helpers' -import { checkAccess } from '@mds-core/mds-api-server' +import { parsePagingQueryParams, asJsonApiLinks, parseRequest } from '@mds-core/mds-api-helpers' +import { checkAccess, AccessTokenScopeValidator } from '@mds-core/mds-api-server' import { AuditApiAuditEndRequest, AuditApiAuditNoteRequest, @@ -63,7 +67,20 @@ import { AuditApiResponse, AuditApiTripRequest, AuditApiVehicleEventRequest, - AuditApiVehicleTelemetryRequest + AuditApiVehicleTelemetryRequest, + AuditApiAccessTokenScopes, + PostAuditTripStartResponse, + PostAuditTripVehicleEventResponse, + PostAuditTripTelemetryResponse, + PostAuditTripNoteResponse, + PostAuditTripEndResponse, + GetAuditTripDetailsResponse, + PostAuditTripEventResponse, + DeleteAuditTripResponse, + GetAuditVehiclesResponse, + GetVehicleByVinResponse, + PostAuditAttachmentResponse, + GetAuditTripsDetailsResponse } from './types' import { deleteAudit, @@ -73,14 +90,21 @@ import { readAuditEvents, readAudits, readDevice, - readDeviceByVehicleId, - readEvent, readEvents, readTelemetry, withGpsProperty, writeAudit, writeAuditEvent } from './service' +import { + attachmentSummary, + deleteAuditAttachment, + multipartFormUpload, + readAttachments, + validateFile, + writeAttachment +} from './attachments' +import { AuditApiVersionMiddleware } from './middleware' // TODO lib function flattenTelemetry(telemetry?: Telemetry): TelemetryData { @@ -100,19 +124,15 @@ function flattenTelemetry(telemetry?: Telemetry): TelemetryData { } } -function flattenProviderEvent(providerEvent: Recorded | null): VehicleEventSummary { - return { - provider_event_id: providerEvent ? providerEvent.id : null, - provider_event_type: providerEvent ? providerEvent.event_type : null, - provider_event_type_reason: providerEvent ? providerEvent.event_type_reason : null - } -} +const checkAuditApiAccess = (validator: AccessTokenScopeValidator) => checkAccess(validator) function api(app: express.Express): express.Express { /** * Audit-specific middleware to extract subject_id into locals, do some logging, etc. * NOTE that audit will be city-facing only, not Providers. */ + app.use(AuditApiVersionMiddleware) + app.use(async (req: AuditApiRequest, res: AuditApiResponse, next) => { if (!(req.path.includes('/health') || req.path === '/')) { if (res.locals.claims) { @@ -128,7 +148,7 @@ function api(app: express.Express): express.Express { return next() } } - await log.warn('Missing subject_id', req.method, req.originalUrl) + logger.warn('Missing subject_id', req.method, req.originalUrl) // 403 Forbidden return res.status(403).send({ error: new AuthorizationError('missing_subject_id') }) } @@ -152,7 +172,7 @@ function api(app: express.Express): express.Express { return res.status(400).send({ error: err }) } // 500 Internal Server Error - await log.error(`fail ${req.method} ${req.originalUrl}`, err.stack || JSON.stringify(err)) + logger.error(`fail ${req.method} ${req.originalUrl}`, err.stack || JSON.stringify(err)) return res.status(500).send({ error: new ServerError(err) }) } }) @@ -165,8 +185,8 @@ function api(app: express.Express): express.Express { */ app.post( pathsFor('/trips/:audit_trip_id/start'), - checkAccess(scopes => scopes.includes('audits:write')), - async (req: AuditApiAuditStartRequest, res: AuditApiResponse) => { + checkAuditApiAccess(scopes => scopes.includes('audits:write')), + async (req: AuditApiAuditStartRequest, res: PostAuditTripStartResponse) => { try { const { audit_trip_id, audit, audit_subject_id, recorded } = res.locals @@ -190,10 +210,9 @@ function api(app: express.Express): express.Express { isValidTelemetry(telemetry, { required: false }) ) { // Find provider device and event by vehicle id lookup - const provider_device = await readDeviceByVehicleId(provider_id, provider_vehicle_id) + const provider_device = await getVehicle(provider_id, provider_vehicle_id) const provider_device_id = provider_device ? provider_device.device_id : null const provider_name = providerName(provider_id) - const providerEvent = await readEvent(provider_device_id) // Create the audit await writeAudit({ @@ -215,13 +234,13 @@ function api(app: express.Express): express.Express { audit_subject_id, audit_event_type: AUDIT_EVENT_TYPES.start, ...flattenTelemetry(telemetry), - ...flattenProviderEvent(providerEvent), timestamp, recorded }) // 200 OK return res.status(200).send({ + version: res.locals.version, provider_id, provider_name, provider_vehicle_id, @@ -238,7 +257,7 @@ function api(app: express.Express): express.Express { return res.status(400).send({ error: err }) } // 500 Internal Server Error - await log.error(`fail ${req.method} ${req.originalUrl}`, err.stack || JSON.stringify(err)) + logger.error(`fail ${req.method} ${req.originalUrl}`, err.stack || JSON.stringify(err)) return res.status(500).send({ error: new ServerError(err) }) } } @@ -251,8 +270,8 @@ function api(app: express.Express): express.Express { */ app.post( pathsFor('/trips/:audit_trip_id/vehicle/event'), - checkAccess(scopes => scopes.includes('audits:write')), - async (req: AuditApiVehicleEventRequest, res: AuditApiResponse) => { + checkAuditApiAccess(scopes => scopes.includes('audits:write')), + async (req: AuditApiVehicleEventRequest, res: PostAuditTripVehicleEventResponse) => { try { const { audit_trip_id, audit_subject_id, audit, recorded } = res.locals @@ -266,20 +285,18 @@ function api(app: express.Express): express.Express { isValidTelemetry(telemetry, { required: false }) ) { // Create the audit event - const providerEvent = await readEvent(audit.provider_device_id) await writeAuditEvent({ audit_trip_id, audit_event_id, audit_subject_id, audit_event_type: event_type, ...flattenTelemetry(telemetry), - ...flattenProviderEvent(providerEvent), timestamp, recorded }) // 200 OK - return res.status(200).send({}) + return res.status(200).send({ version: res.locals.version }) } } else { // 404 Not Found @@ -291,7 +308,7 @@ function api(app: express.Express): express.Express { return res.status(400).send({ error: err }) } // 500 Internal Server Error - await log.error(`fail ${req.method} ${req.originalUrl}`, err.stack || JSON.stringify(err)) + logger.error(`fail ${req.method} ${req.originalUrl}`, err.stack || JSON.stringify(err)) return res.status(500).send({ error: new ServerError(err) }) } } @@ -303,8 +320,8 @@ function api(app: express.Express): express.Express { */ app.post( pathsFor('/trips/:audit_trip_id/vehicle/telemetry'), - checkAccess(scopes => scopes.includes('audits:write')), - async (req: AuditApiVehicleTelemetryRequest, res: AuditApiResponse) => { + checkAuditApiAccess(scopes => scopes.includes('audits:write')), + async (req: AuditApiVehicleTelemetryRequest, res: PostAuditTripTelemetryResponse) => { try { const { audit_trip_id, audit_subject_id, audit, recorded } = res.locals @@ -325,7 +342,7 @@ function api(app: express.Express): express.Express { }) // 200 OK - return res.status(200).send({}) + return res.status(200).send({ version: res.locals.version }) } } else { // 404 Not Found @@ -337,7 +354,7 @@ function api(app: express.Express): express.Express { return res.status(400).send({ error: err }) } // 500 Internal Server Error - await log.error(`fail ${req.method} ${req.originalUrl}`, err.stack || JSON.stringify(err)) + logger.error(`fail ${req.method} ${req.originalUrl}`, err.stack || JSON.stringify(err)) return res.status(500).send({ error: new ServerError(err) }) } } @@ -349,8 +366,8 @@ function api(app: express.Express): express.Express { */ app.post( [...pathsFor('/trips/:audit_trip_id/note'), ...pathsFor('/trips/:audit_trip_id/event')], - checkAccess(scopes => scopes.includes('audits:write')), - async (req: AuditApiAuditNoteRequest, res: AuditApiResponse) => { + checkAuditApiAccess(scopes => scopes.includes('audits:write')), + async (req: AuditApiAuditNoteRequest, res: PostAuditTripNoteResponse | PostAuditTripEventResponse) => { try { const { audit_trip_id, audit, audit_subject_id, recorded } = res.locals @@ -378,7 +395,6 @@ function api(app: express.Express): express.Express { }) ) { // Create the audit event - const providerEvent = await readEvent(audit.provider_device_id) await writeAuditEvent({ audit_trip_id, audit_event_id, @@ -387,13 +403,12 @@ function api(app: express.Express): express.Express { audit_issue_code, note, ...flattenTelemetry(telemetry), - ...flattenProviderEvent(providerEvent), timestamp, recorded }) // 200 OK - return res.status(200).send({}) + return res.status(200).send({ version: res.locals.version }) } } else { // 404 Not Found @@ -405,7 +420,7 @@ function api(app: express.Express): express.Express { return res.status(400).send({ error: err }) } // 500 Internal Server Error - await log.error(`fail ${req.method} ${req.originalUrl}`, err.stack || JSON.stringify(err)) + logger.error(`fail ${req.method} ${req.originalUrl}`, err.stack || JSON.stringify(err)) return res.status(500).send({ error: new ServerError(err) }) } } @@ -417,8 +432,8 @@ function api(app: express.Express): express.Express { */ app.post( pathsFor('/trips/:audit_trip_id/end'), - checkAccess(scopes => scopes.includes('audits:write')), - async (req: AuditApiAuditEndRequest, res: AuditApiResponse) => { + checkAuditApiAccess(scopes => scopes.includes('audits:write')), + async (req: AuditApiAuditEndRequest, res: PostAuditTripEndResponse) => { try { const { audit_trip_id, audit, audit_subject_id, recorded } = res.locals if (audit) { @@ -426,26 +441,23 @@ function api(app: express.Express): express.Express { // Validate input params if ( - isValidAuditEventId(audit_event_id) && isValidAuditEventId(audit_event_id) && isValidTimestamp(timestamp) && isValidTelemetry(telemetry, { required: false }) ) { // Create the audit end event - const providerEvent = await readEvent(audit.provider_device_id) await writeAuditEvent({ audit_trip_id, audit_event_id, audit_subject_id, audit_event_type: AUDIT_EVENT_TYPES.end, ...flattenTelemetry(telemetry), - ...flattenProviderEvent(providerEvent), timestamp, recorded }) // 200 OK - return res.status(200).send({}) + return res.status(200).send({ version: res.locals.version }) } } else { // 404 Not Found @@ -457,7 +469,7 @@ function api(app: express.Express): express.Express { return res.status(400).send({ error: err }) } // 500 Internal Server Error - await log.error(`fail ${req.method} ${req.originalUrl}`, err.stack || JSON.stringify(err)) + logger.error(`fail ${req.method} ${req.originalUrl}`, err.stack || JSON.stringify(err)) return res.status(500).send({ error: new ServerError(err) }) } } @@ -469,8 +481,8 @@ function api(app: express.Express): express.Express { */ app.get( pathsFor('/trips/:audit_trip_id'), - checkAccess(scopes => scopes.includes('audits:read')), - async (req: AuditApiGetTripRequest, res: AuditApiResponse) => { + checkAuditApiAccess(scopes => scopes.includes('audits:read')), + async (req: AuditApiGetTripRequest, res: GetAuditTripDetailsResponse) => { try { const { audit_trip_id, audit } = res.locals @@ -480,9 +492,12 @@ function api(app: express.Express): express.Express { // Read the audit events const auditEvents = await readAuditEvents(audit_trip_id) + // Read the audit attachments + const attachments = await readAttachments(audit_trip_id) + const device = provider_device_id ? await readDevice(provider_device_id, provider_id) - : await readDeviceByVehicleId(provider_id, provider_vehicle_id) + : await getVehicle(provider_id, provider_vehicle_id) if (device) { // Calculate the event window for the provider vehicle (trip_start/trip_end) @@ -513,18 +528,34 @@ function api(app: express.Express): express.Express { } }, {}) - const event_viewport_adjustment = seconds(Number(req.query.event_viewport_adjustment) || 30) + const { event_viewport_adjustment = seconds(30) } = parseRequest(req, { + parser: x => seconds(Number(x)) + }).query('event_viewport_adjustment') + const start_time = audit_start && audit_start - event_viewport_adjustment const end_time = (end => end && end + event_viewport_adjustment)(audit_end || last_event) if (start_time && end_time) { const deviceEvents = await readEvents(device.device_id, start_time, end_time) const deviceTelemetry = await readTelemetry(device.device_id, start_time, end_time) - + const providerEvent = await db.readEventsWithTelemetry({ + device_id: device.device_id, + provider_id: device.provider_id, + end_time: audit_start, // Last provider event before the audit started + order_by: 'timestamp DESC', + limit: 1 + }) return res.status(200).send({ + version: res.locals.version, ...audit, provider_vehicle_id: device.vehicle_id, + provider_event_type: providerEvent[0]?.event_type, + provider_event_type_reason: providerEvent[0]?.event_type_reason, + provider_status: EVENT_STATUS_MAP[providerEvent[0]?.event_type as VEHICLE_EVENT], + provider_telemetry: providerEvent[0]?.telemetry, + provider_event_time: providerEvent[0]?.timestamp, events: auditEvents.map(withGpsProperty), + attachments: attachments.map(attachmentSummary), provider: { device, events: deviceEvents, @@ -533,12 +564,20 @@ function api(app: express.Express): express.Express { }) } return res.status(200).send({ + version: res.locals.version, ...audit, events: auditEvents.map(withGpsProperty), + attachments: attachments.map(attachmentSummary), provider: { device, events: [], telemetry: [] } }) } - return res.status(200).send({ ...audit, events: auditEvents.map(withGpsProperty), provider: null }) + return res.status(200).send({ + version: res.locals.version, + ...audit, + events: auditEvents.map(withGpsProperty), + attachments: attachments.map(attachmentSummary), + provider: null + }) } // 404 Not Found return res.status(404).send({ error: new NotFoundError('audit_trip_id_not_found', { audit_trip_id }) }) @@ -548,7 +587,7 @@ function api(app: express.Express): express.Express { return res.status(400).send({ error: err }) } // 500 Internal Server Error - await log.error(`fail ${req.method} ${req.originalUrl}`, err.stack || JSON.stringify(err)) + logger.error(`fail ${req.method} ${req.originalUrl}`, err.stack || JSON.stringify(err)) return res.status(500).send({ error: new ServerError(err) }) } } @@ -556,15 +595,15 @@ function api(app: express.Express): express.Express { app.delete( pathsFor('/trips/:audit_trip_id'), - checkAccess(scopes => scopes.includes('audits:delete')), - async (req: AuditApiTripRequest, res: AuditApiResponse) => { + checkAuditApiAccess(scopes => scopes.includes('audits:delete')), + async (req: AuditApiTripRequest, res: DeleteAuditTripResponse) => { try { const { audit_trip_id, audit } = res.locals if (audit) { // Delete the audit await deleteAudit(audit_trip_id) // 200 OK - return res.status(200).send({}) + return res.status(200).send({ version: res.locals.version }) } // 404 Not Found return res.status(404).send({ error: new NotFoundError('audit_trip_id_not_found', { audit_trip_id }) }) @@ -574,7 +613,7 @@ function api(app: express.Express): express.Express { return res.status(400).send({ error: err }) } // 500 Internal Server Error - await log.error(`fail ${req.method} ${req.originalUrl}`, err.stack || JSON.stringify(err)) + logger.error(`fail ${req.method} ${req.originalUrl}`, err.stack || JSON.stringify(err)) return res.status(500).send({ error: new ServerError(err) }) } } @@ -585,29 +624,40 @@ function api(app: express.Express): express.Express { */ app.get( pathsFor('/trips'), - checkAccess(scopes => scopes.includes('audits:read')), - async (req: AuditApiGetTripsRequest, res: AuditApiResponse) => { + checkAuditApiAccess(scopes => scopes.includes('audits:read')), + async (req: AuditApiGetTripsRequest, res: GetAuditTripsDetailsResponse) => { try { - const { start_time, end_time } = req.query - const { skip, take } = asPagingParams(req.query) + const { skip, take } = parsePagingQueryParams(req) - // Construct the query params const query = { - ...req.query, + ...parseRequest(req, { parser: Number }).query('start_time', 'end_time'), + ...parseRequest(req).query('provider_id', 'provider_vehicle_id', 'audit_subject_id'), skip, - take, - start_time: start_time ? Number(start_time) : undefined, - end_time: end_time ? Number(end_time) : undefined + take } // Query the audits const { count, audits } = await readAudits(query) + const auditsWithAttachments = await Promise.all( + audits.map(async audit => { + const attachments = await readAttachments(audit.audit_trip_id) + return { + ...audit, + attachments: attachments.map(attachmentSummary) + } + }) + ) // 200 OK - return res.status(200).send({ count, audits, links: asJsonApiLinks(req, skip, take, count) }) + return res.status(200).send({ + version: res.locals.version, + count, + audits: auditsWithAttachments, + links: asJsonApiLinks(req, skip, take, count) + }) } catch (err) /* istanbul ignore next */ { // 500 Internal Server Error - await log.error(`fail ${req.method} ${req.originalUrl}`, err.stack || JSON.stringify(err)) + logger.error(`fail ${req.method} ${req.originalUrl}`, err.stack || JSON.stringify(err)) return res.status(500).send({ error: new ServerError(err) }) } } @@ -616,48 +666,106 @@ function api(app: express.Express): express.Express { /** * read back cached vehicle information for vehicles in bbox */ - app.get(pathsFor('/vehicles'), checkAccess(scopes => scopes.includes('audits:vehicles:read')), async (req, res) => { - const { skip, take } = { skip: 0, take: 10000 } - const bbox = JSON.parse(req.query.bbox) - - const url = urls.format({ - protocol: req.get('x-forwarded-proto') || req.protocol, - host: req.get('host'), - pathname: req.path - }) - - const { provider_id } = req.query + app.get( + pathsFor('/vehicles'), + checkAuditApiAccess(scopes => scopes.includes('audits:vehicles:read')), + async (req, res: GetAuditVehiclesResponse) => { + const { skip, take } = { skip: 0, take: 10000 } + const { strict = true, bbox, provider_id } = { + ...parseRequest(req, { parser: JSON.parse }).query('strict', 'bbox'), + ...parseRequest(req).query('provider_id') + } - try { - const response = await getVehicles(skip, take, url, provider_id, req.query, bbox) - return res.status(200).send(response) - } catch (err) { - await log.error('getVehicles fail', err) - return res.status(500).send({ - error: 'server_error', - error_description: 'an internal server error has occurred and been logged' + const url = urls.format({ + protocol: req.get('x-forwarded-proto') || req.protocol, + host: req.get('host'), + pathname: req.path }) + + try { + const response = await getVehicles(skip, take, url, req.query, bbox, strict, provider_id) + return res.status(200).send({ version: res.locals.version, ...response }) + } catch (err) { + logger.error('getVehicles fail', err) + return res.status(500).send({ + error: 'internal_server_error' + }) + } } - }) + ) /** * read back cached information for a single vehicle */ - app.get(pathsFor('/vehicles/:provider_id/vin/:vin'), async (req: AuditApiGetVehicleRequest, res) => { - const { provider_id, vin } = req.params - try { - const response = await getVehicle(provider_id, vin) - if (response) { - res.status(200).send({ vehicles: [response] }) - } else { - res.status(404).send({ error: new NotFoundError('vehicle not found', { provider_id, vin }) }) + app.get( + pathsFor('/vehicles/:provider_id/vin/:vin'), + async (req: AuditApiGetVehicleRequest, res: GetVehicleByVinResponse) => { + const { provider_id, vin } = req.params + try { + const response = await getVehicle(provider_id, vin) + if (response) { + res.status(200).send({ version: res.locals.version, vehicles: [response] }) + } else { + res.status(404).send({ error: new NotFoundError('vehicle not found', { provider_id, vin }) }) + } + } catch (err) { + logger.error('getVehicle fail', err) + res.status(500).send({ + error: 'internal_server_error' + }) + } + } + ) + + /** + * attach media to an audit, uploaded as multipart/form-data + */ + app.post( + pathsFor('/trips/:audit_trip_id/attach/:mimetype'), + multipartFormUpload, + async (req, res: PostAuditAttachmentResponse) => { + const { audit, audit_trip_id } = res.locals + if (!audit) { + return res.status(404).send({ error: new NotFoundError('audit not found', { audit_trip_id }) }) + } + try { + validateFile(req.file) + } catch (err) { + if (err instanceof ValidationError) { + return res.status(400).send({ error: err }) + } + if (err instanceof UnsupportedTypeError) { + return res.status(415).send({ error: err }) + } } + try { + const attachment = await writeAttachment(req.file, audit_trip_id) + res.status(200).send({ + version: res.locals.version, + ...attachmentSummary(attachment), + audit_trip_id + }) + } catch (err) { + logger.error('post attachment fail', err) + return res.status(500).send({ error: new ServerError(err) }) + } + } + ) + + /** + * delete media from an audit + */ + app.delete(pathsFor('/trips/:audit_trip_id/attachment/:attachment_id'), async (req, res) => { + const { audit_trip_id, attachment_id } = req.params + try { + await deleteAuditAttachment(audit_trip_id, attachment_id) + res.status(200).send({ version: res.locals.version }) } catch (err) { - await log.error('getVehicle fail', err) - res.status(500).send({ - error: 'server_error', - error_description: 'an internal server error has occurred and been logged' - }) + logger.error('delete attachment error', err) + if (err instanceof NotFoundError) { + return res.status(404).send({ error: err }) + } + return res.status(500).send({ error: new ServerError(err) }) } }) diff --git a/packages/mds-audit/attachments.ts b/packages/mds-audit/attachments.ts new file mode 100644 index 000000000..c5ecdde17 --- /dev/null +++ b/packages/mds-audit/attachments.ts @@ -0,0 +1,173 @@ +/* + Copyright 2019 City of Los Angeles. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +import db from '@mds-core/mds-db' +import logger from '@mds-core/mds-logger' +import aws from 'aws-sdk' +import path from 'path' +import sharp from 'sharp' +import { uuid, UnsupportedTypeError, ValidationError } from '@mds-core/mds-utils' +import { Attachment, AttachmentSummary, AuditAttachment, Recorded, UUID } from '@mds-core/mds-types' + +/* eslint-disable-next-line */ +const multer = require('multer') +const { env } = process +const supportedMimetypes = ['image/png', 'image/jpeg'] +const thumbnailSize = Number(env.ATTACHMENTS_THUMBNAIL_SIZE || 250) +const s3Bucket = String(env.ATTACHMENTS_BUCKET) +const s3BucketSubdir = String(env.ATTACHMENTS_SUBDIR) +const s3Region = String(env.ATTACHMENTS_REGION) +const s3ACL = String(env.ATTACHMENTS_ACL) +const s3 = new aws.S3() +const memoryStorage = multer.memoryStorage() + +if (env.ATTACHMENTS_BUCKET) { + /* eslint-disable-next-line */ + aws.config.getCredentials(async err => { + if (err) { + logger.error('Error getting AWS credentials', err.stack || err) + } else if (aws.config.credentials) { + aws.config.update({ + secretAccessKey: aws.config.credentials.secretAccessKey, + accessKeyId: aws.config.credentials.accessKeyId, + region: s3Region + }) + } + }) +} + +export const multipartFormUpload = multer({ storage: memoryStorage }).single('file') + +export function attachmentSummary(attachment: Attachment): AttachmentSummary { + const thumbnailUrl = attachment.thumbnail_filename ? attachment.base_url + attachment.thumbnail_filename : '' + return { + attachment_id: attachment.attachment_id, + attachment_url: attachment.base_url + attachment.attachment_filename, + thumbnail_url: thumbnailUrl + } +} + +export function validateFile(file: Express.Multer.File) { + if (!file || file.buffer.byteLength === 0) { + throw new ValidationError('No attachment found') + } else if (path.extname(file.originalname).replace('.', '') === '') { + throw new ValidationError(`Missing file extension in filename ${file.originalname}`) + } else if (!supportedMimetypes.includes(file.mimetype)) { + throw new UnsupportedTypeError(`Unsupported mime type ${file.mimetype}`) + } +} + +async function writeAttachmentS3(file: Express.Multer.File) { + // Process attachment and thumbnail and output to Buffers + const [attachmentBuf, thumbnailBuf] = await Promise.all([ + sharp(file.buffer) + .rotate() // automatically based on EXIF + .toBuffer(), + sharp(file.buffer) + .rotate() + .resize(thumbnailSize, thumbnailSize, { + fit: sharp.fit.inside, + withoutEnlargement: true + }) + .toBuffer() + ]) + + // Upload attachment and thumbnail to S3 + const attachmentId = uuid() + const ext = path.extname(file.originalname).toLowerCase() + const attachmentFilename = attachmentId + ext + const thumbnailFilename = `${attachmentId}.thumbnail${ext}` + await Promise.all([ + s3 + .upload({ + Bucket: s3Bucket, + Key: [s3BucketSubdir, attachmentFilename].join('/'), + Body: attachmentBuf, + ACL: s3ACL + }) + .promise(), + s3 + .upload({ + Bucket: s3Bucket, + Key: [s3BucketSubdir, thumbnailFilename].join('/'), + Body: thumbnailBuf, + ACL: s3ACL + }) + .promise() + ]) + return { + attachment_filename: attachmentFilename, + attachment_id: attachmentId, + base_url: `https://${s3Bucket}.s3-${s3Region}.amazonaws.com/${s3BucketSubdir}/`, + mimetype: file.mimetype, + thumbnail_filename: thumbnailFilename, + thumbnail_mimetype: file.mimetype + } +} + +export async function writeAttachment(file: Express.Multer.File, auditTripId: UUID) { + const attachment = await writeAttachmentS3(file) + await db.writeAttachment(attachment) + await db.writeAuditAttachment({ + attachment_id: attachment.attachment_id, + audit_trip_id: auditTripId + } as AuditAttachment) + return attachment +} + +export async function readAttachments(audit_trip_id: UUID): Promise[]> { + const result: Recorded[] = await db.readAttachmentsForAudit(audit_trip_id) + return result +} + +export async function deleteAttachmentS3(attachment: Attachment) { + const deletePromises = [ + s3 + .deleteObject({ + Bucket: s3Bucket, + Key: s3Bucket.concat('/', attachment.attachment_filename) + }) + .promise() + ] + if (attachment.thumbnail_filename) { + deletePromises.push( + s3 + .deleteObject({ + Bucket: s3Bucket, + Key: s3Bucket.concat('/', attachment.thumbnail_filename) + }) + .promise() + ) + } + await Promise.all(deletePromises) +} + +export async function deleteAuditAttachment(auditTripId: UUID, attachmentId: UUID) { + try { + await db.deleteAuditAttachment(auditTripId, attachmentId) + // Delete the attachment if it is not used by any other audits + const auditAttachments = await db.readAuditAttachments(attachmentId) + if (auditAttachments.length === 0) { + const attachment = await db.deleteAttachment(attachmentId) + if (attachment) { + await deleteAttachmentS3(attachment) + } + } + } catch (err) { + logger.error('deleteAttachment error', err.stack || err) + throw err + } +} diff --git a/packages/mds-audit/middleware/audit-api-version.ts b/packages/mds-audit/middleware/audit-api-version.ts new file mode 100644 index 000000000..e27cf11dc --- /dev/null +++ b/packages/mds-audit/middleware/audit-api-version.ts @@ -0,0 +1,23 @@ +/* + Copyright 2019-2020 City of Los Angeles. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +import { ApiVersionMiddleware } from '@mds-core/mds-api-server' +import { AUDIT_API_SUPPORTED_VERSIONS, AUDIT_API_DEFAULT_VERSION } from '../types' + +export const AuditApiVersionMiddleware = ApiVersionMiddleware( + 'application/vnd.mds.audit+json', + AUDIT_API_SUPPORTED_VERSIONS +).withDefaultVersion(AUDIT_API_DEFAULT_VERSION) diff --git a/packages/mds-audit/middleware/index.ts b/packages/mds-audit/middleware/index.ts new file mode 100644 index 000000000..241a91040 --- /dev/null +++ b/packages/mds-audit/middleware/index.ts @@ -0,0 +1,17 @@ +/* + Copyright 2019-2020 City of Los Angeles. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +export * from './audit-api-version' diff --git a/packages/mds-audit/package.json b/packages/mds-audit/package.json index 46734406b..12fbc343d 100644 --- a/packages/mds-audit/package.json +++ b/packages/mds-audit/package.json @@ -1,6 +1,6 @@ { "name": "@mds-core/mds-audit", - "version": "0.1.30", + "version": "0.1.38", "description": "Endpoint for auditing compliance with MDS", "main": "dist/index.js", "types": "dist/index.d.ts", @@ -11,30 +11,33 @@ "build": "tsc --build tsconfig.build.json", "start": "PATH_PREFIX=/audit yarn watch server", "test": "yarn test:eslint && yarn test:unit", - "test:eslint": "eslint --fix --ignore-path ../../.gitignore '**/*.ts'", - "test:unit": "PATH_PREFIX=/audit DOTENV_CONFIG_PATH=../../.env nyc --check-coverage --exclude tests --extension .ts --lines 90 --reporter=text --reporter=html ts-mocha --project ../../tsconfig.json --require tsconfig-paths/register --require source-map-support/register --require dotenv/config --recursive --timeout 5000 tests/**/*.ts", - "watch": "nodemon --watch '../../packages' --ext 'ts' --ignore '*.d.ts' --exec yarn watch:exec --", - "watch:exec": "yarn build && DOTENV_CONFIG_PATH=../../.env ts-node -r dotenv/config" + "test:eslint": "eslint --ignore-path ../../.gitignore '**/*.ts'", + "test:unit": "PATH_PREFIX=/audit DOTENV_CONFIG_PATH=../../.env nyc ts-mocha --project ../../tsconfig.json", + "ts-node": "yarn build && DOTENV_CONFIG_PATH=../../.env ts-node -r dotenv/config", + "watch": "nodemon --watch '../../packages' --ext 'ts' --ignore '*.d.ts' --exec yarn ts-node --" }, "keywords": [ "mds", "audit" ], "author": "City of Los Angeles", + "license": "Apache-2.0", "dependencies": { - "@mds-core/mds-api-helpers": "0.1.18", - "@mds-core/mds-api-server": "0.1.18", - "@mds-core/mds-cache": "0.1.18", - "@mds-core/mds-db": "0.1.18", - "@mds-core/mds-logger": "0.1.16", - "@mds-core/mds-providers": "0.1.18", - "@mds-core/mds-types": "0.1.15", - "@mds-core/mds-utils": "0.1.18", + "@mds-core/mds-agency-cache": "0.1.26", + "@mds-core/mds-api-helpers": "0.1.26", + "@mds-core/mds-api-server": "0.1.26", + "@mds-core/mds-db": "0.1.26", + "@mds-core/mds-logger": "0.1.24", + "@mds-core/mds-providers": "0.1.26", + "@mds-core/mds-schema-validators": "0.1.2", + "@mds-core/mds-types": "0.1.23", + "@mds-core/mds-utils": "0.1.26", + "@types/express": "4.17.6", + "@types/multer": "1.4.3", + "@types/sharp": "0.25.0", + "aws-sdk": "2.676.0", "express": "4.17.1", - "uuid": "3.3.3" - }, - "devDependencies": { - "@mds-core/mds-test-data": "0.1.18" - }, - "license": "Apache-2.0" + "multer": "1.4.2", + "sharp": "0.25.2" + } } diff --git a/packages/mds-audit/server.ts b/packages/mds-audit/server.ts index 9b25568f1..3194b313f 100644 --- a/packages/mds-audit/server.ts +++ b/packages/mds-audit/server.ts @@ -14,14 +14,7 @@ limitations under the License. */ -// Express local -import { ApiServer } from '@mds-core/mds-api-server' +import { ApiServer, HttpServer } from '@mds-core/mds-api-server' import { api } from './api' -const { - env: { npm_package_name, PORT = 4002 } -} = process - -/* eslint-reason avoids import of logger */ -/* eslint-disable-next-line no-console */ -ApiServer(api, { handleCors: true }).listen(PORT, () => console.log(`${npm_package_name} running on port ${PORT}`)) +HttpServer(ApiServer(api), { port: process.env.AUDIT_API_PORT }) diff --git a/packages/mds-audit/service.ts b/packages/mds-audit/service.ts index 85fc5f9a5..b5502c415 100644 --- a/packages/mds-audit/service.ts +++ b/packages/mds-audit/service.ts @@ -15,7 +15,8 @@ */ import db from '@mds-core/mds-db' -import cache from '@mds-core/mds-cache' +import cache from '@mds-core/mds-agency-cache' +import { Query } from 'express-serve-static-core' import { Audit, AuditEvent, @@ -30,8 +31,11 @@ import { BoundingBox, EVENT_STATUS_MAP, VEHICLE_EVENT, + VEHICLE_EVENTS, VEHICLE_STATUSES } from '@mds-core/mds-types' +import logger from '@mds-core/mds-logger' +import { now } from '@mds-core/mds-utils' export async function deleteAudit(audit_trip_id: UUID): Promise { const result: number = await db.deleteAudit(audit_trip_id) @@ -92,16 +96,20 @@ export async function readDevice(device_id: UUID, provider_id: UUID): Promise | null> { +export async function readDevicesByVehicleId(provider_id: UUID, vehicle_id: string): Promise[]> { try { - const result: Recorded = await db.readDeviceByVehicleId( + const start = now() + const results: Recorded[] = await db.readDevicesByVehicleId( provider_id, vehicle_id, vehicle_id.replace(/[\W_-]/g, '') ) - return result + const finish = now() + const timeElapsed = finish - start + logger.info(`db.readDevicesByVehicleId ${provider_id} ${vehicle_id} time elapsed: ${timeElapsed}ms`) + return results } catch (err) { - return null + return [] } } @@ -162,26 +170,39 @@ export function withGpsProperty({ } export async function getVehicle(provider_id: UUID, vehicle_id: string) { - const device = await readDeviceByVehicleId(provider_id, vehicle_id) - if (!device) { - return null - } - const deviceStatus = (await cache.readDeviceStatus(device.device_id)) as (VehicleEvent & Device) - if (!deviceStatus) { + const devices = await readDevicesByVehicleId(provider_id, vehicle_id) + if (devices.length === 0) { return null } - const status = EVENT_STATUS_MAP[deviceStatus.event_type as VEHICLE_EVENT] - const updated = deviceStatus.timestamp - return { ...deviceStatus, status, updated } + const deviceStatusMap: { + active: (Device & { updated?: Timestamp | null })[] + inactive: (Device & { updated?: Timestamp | null })[] + } = { active: [], inactive: [] } + await Promise.all( + devices.map(async device => { + const deviceStatus = (await cache.readDeviceStatus(device.device_id)) as (VehicleEvent & Device) | null + if (deviceStatus === null || deviceStatus.event_type === VEHICLE_EVENTS.deregister) { + const { device_id } = device + logger.info('Bad vehicle status', { deviceStatus, provider_id, vehicle_id, device_id }) + deviceStatusMap.inactive.push(device) + } else { + const status = EVENT_STATUS_MAP[deviceStatus.event_type as VEHICLE_EVENT] + const updated = deviceStatus.timestamp + deviceStatusMap.active.push({ ...device, ...deviceStatus, status, updated }) + } + }) + ) + return deviceStatusMap.active[0] || deviceStatusMap.inactive[0] || null } export async function getVehicles( skip: number, take: number, url: string, - provider_id: string, - reqQuery: { [x: string]: string }, - bbox: BoundingBox + reqQuery: Query, + bbox: BoundingBox, + strict = true, + provider_id?: string ) { function fmt(query: { skip: number; take: number }): string { const flat: { [key: string]: number } = { ...reqQuery, ...query } @@ -192,7 +213,8 @@ export async function getVehicles( return s } - const statusesSuperset = ((await cache.readDevicesStatus({ bbox })) as (VehicleEvent & Device)[]).filter( + const start = now() + const statusesSuperset = ((await cache.readDevicesStatus({ bbox, strict })) as (VehicleEvent & Device)[]).filter( status => EVENT_STATUS_MAP[status.event_type as VEHICLE_EVENT] !== VEHICLE_STATUSES.removed && (!provider_id || status.provider_id === provider_id) @@ -203,6 +225,10 @@ export async function getVehicles( const updated = item.timestamp return [...acc, { ...item, status, updated }] }, []) + const finish = now() + logger.info( + `getVehicles processing ${JSON.stringify(bbox)} provider ${provider_id} time elapsed: ${finish - start}ms` + ) const noNext = skip + take >= statusesSuperset.length const noPrev = skip === 0 || skip > statusesSuperset.length diff --git a/packages/mds-audit/tests/api.spec.ts b/packages/mds-audit/tests/api.spec.ts index 9bbb9ead7..be1830353 100644 --- a/packages/mds-audit/tests/api.spec.ts +++ b/packages/mds-audit/tests/api.spec.ts @@ -23,27 +23,33 @@ import supertest from 'supertest' import { + Attachment, + Audit, + AuditAttachment, Device, - Timestamp, Telemetry, - VEHICLE_EVENTS, + Timestamp, AUDIT_EVENT_TYPES, PROPULSION_TYPES, + VEHICLE_EVENTS, + VEHICLE_REASONS, VEHICLE_TYPES } from '@mds-core/mds-types' import { makeEventsWithTelemetry, makeDevices, makeTelemetryInArea, SCOPED_AUTH } from '@mds-core/mds-test-data' -import { now, rangeRandomInt } from '@mds-core/mds-utils' -import cache from '@mds-core/mds-cache' +import { NotFoundError, now, rangeRandomInt, uuid } from '@mds-core/mds-utils' +import cache from '@mds-core/mds-agency-cache' import test from 'unit.js' -import uuid from 'uuid' import { ApiServer } from '@mds-core/mds-api-server' import db from '@mds-core/mds-db' import { MOCHA_PROVIDER_ID } from '@mds-core/mds-providers' +import Sinon from 'sinon' import { api } from '../api' +import * as attachments from '../attachments' +import { AUDIT_API_DEFAULT_VERSION } from '../types' const request = supertest(ApiServer(api)) -const APP_JSON = 'application/json; charset=utf-8' +const APP_JSON = 'application/vnd.mds.audit+json; charset=utf-8; version=0.1' const audit_trip_id = uuid() const audit_trip_id_2 = uuid() @@ -63,6 +69,7 @@ const telemetry = (): {} => ({ }) const AUDIT_START = Date.now() +const OLD_EVENT = Date.now() - 60000 const audit_subject_id = 'user@mds-testing.info' @@ -72,30 +79,53 @@ before('Initializing Database', async () => { describe('Testing API', () => { before(done => { - const timestamp = Date.now() + const baseEvent = { + provider_id, + device_id: provider_device_id, + event_type: VEHICLE_EVENTS.agency_drop_off, + event_type_reason: VEHICLE_REASONS.rebalance, + telemetry_timestamp: AUDIT_START, + trip_id: uuid(), + timestamp: AUDIT_START, + recorded: AUDIT_START + } + const baseTelemetry = { + provider_id, + device_id: provider_device_id, + timestamp: AUDIT_START, + recorded: AUDIT_START, + charge: 0.5, + gps: { + lat: 37.4230723, + lng: -122.137429, + speed: 0, + hdop: 1, + heading: 180 + } + } db.writeDevice({ device_id: provider_device_id, provider_id, vehicle_id: provider_vehicle_id, propulsion: [PROPULSION_TYPES.electric], type: VEHICLE_TYPES.scooter, - recorded: timestamp + recorded: AUDIT_START }).then(() => { db.writeEvent({ - provider_id, - device_id: provider_device_id, - event_type: 'trip_start', - telemetry: { - provider_id, - device_id: provider_device_id, - timestamp, - gps: { lat: 37.4230723, lng: -122.13742939999999 } + ...baseEvent, + ...{ telemetry_timestamp: OLD_EVENT, timestamp: OLD_EVENT } + }) + db.writeEvent(baseEvent) + db.writeTelemetry([ + { + ...baseTelemetry, + ...{ timestamp: OLD_EVENT, recorded: OLD_EVENT } }, - telemetry_timestamp: timestamp, - trip_id: uuid(), - timestamp, - recorded: timestamp - }).then(() => done()) + { + ...baseTelemetry, + ...{ timestamp: AUDIT_START, recorded: AUDIT_START } + } + ]).then(() => done()) }) }) @@ -118,6 +148,7 @@ describe('Testing API', () => { test.object(result.body).hasProperty('provider_device') test.object(result.body.provider_device).hasProperty('vehicle_id') test.value(result.body.provider_device.vehicle_id).is(provider_vehicle_id) + test.value(result.body.version, AUDIT_API_DEFAULT_VERSION) done(err) }) }) @@ -159,7 +190,7 @@ describe('Testing API', () => { .expect(200) .end((err, result) => { test.value(result).hasHeader('content-type', APP_JSON) - test.value(result.body).is({}) + test.value(result.body, { version: AUDIT_API_DEFAULT_VERSION }) done(err) }) }) @@ -179,6 +210,7 @@ describe('Testing API', () => { .expect(403) .end((err, result) => { test.value(result).hasHeader('content-type', APP_JSON) + test.value(result.body.version, AUDIT_API_DEFAULT_VERSION) done(err) }) }) @@ -196,7 +228,8 @@ describe('Testing API', () => { .expect(200) .end((err, result) => { test.value(result).hasHeader('content-type', APP_JSON) - test.value(result.body).is({}) + test.value(result.body.version, AUDIT_API_DEFAULT_VERSION) + test.value(Object.keys(result.body)).is(['version']) done(err) }) }) @@ -212,7 +245,8 @@ describe('Testing API', () => { .expect(200) .end((err, result) => { test.value(result).hasHeader('content-type', APP_JSON) - test.value(result.body).is({}) + test.value(result.body.version, AUDIT_API_DEFAULT_VERSION) + test.value(Object.keys(result.body)).is(['version']) done(err) }) }) @@ -228,6 +262,7 @@ describe('Testing API', () => { .expect(403) .end((err, result) => { test.value(result).hasHeader('content-type', APP_JSON) + test.value(result.body.version, AUDIT_API_DEFAULT_VERSION) done(err) }) }) @@ -245,7 +280,7 @@ describe('Testing API', () => { .expect(200) .end((err, result) => { test.value(result).hasHeader('content-type', APP_JSON) - test.value(result.body).is({}) + test.value(result.body, { version: AUDIT_API_DEFAULT_VERSION }) done(err) }) }) @@ -264,7 +299,8 @@ describe('Testing API', () => { .expect(200) .end((err, result) => { test.value(result).hasHeader('content-type', APP_JSON) - test.value(result.body).is({}) + test.value(result.body.version, AUDIT_API_DEFAULT_VERSION) + test.value(result.body, { version: AUDIT_API_DEFAULT_VERSION }) done(err) }) }) @@ -281,7 +317,8 @@ describe('Testing API', () => { .expect(200) .end((err, result) => { test.value(result).hasHeader('content-type', APP_JSON) - test.value(result.body).is({}) + test.value(result.body.version, AUDIT_API_DEFAULT_VERSION) + test.value(result.body, { version: AUDIT_API_DEFAULT_VERSION }) done(err) }) }) @@ -298,21 +335,7 @@ describe('Testing API', () => { .expect(403) .end((err, result) => { test.value(result).hasHeader('content-type', APP_JSON) - done(err) - }) - }) - - it('verify get audit (matched vehicle)', done => { - request - .get(`/audit/trips/${audit_trip_id}`) - .set('Authorization', SCOPED_AUTH(['audits:read'], audit_subject_id)) - .expect(200) - .end((err, result) => { - test.value(result).hasHeader('content-type', APP_JSON) - test.value(result.body.events.length).is(7) - test.value(result.body.events[0].provider_event_id).is(1) - test.value(result.body.events[0].provider_event_type).is('trip_start') - test.value(result.body.events[0].provider_event_type_reason).is(null) + test.value(result.body.version, AUDIT_API_DEFAULT_VERSION) done(err) }) }) @@ -327,11 +350,30 @@ describe('Testing API', () => { .expect(404) .end((err, result) => { test.value(result).hasHeader('content-type', APP_JSON) + test.value(result.body.version, AUDIT_API_DEFAULT_VERSION) done(err) }) }) ) + it('verify get audit (matched vehicle)', done => { + request + .get(`/audit/trips/${audit_trip_id}`) + .set('Authorization', SCOPED_AUTH(['audits:read'], audit_subject_id)) + .expect(200) + .end((err, result) => { + test.value(result).hasHeader('content-type', APP_JSON) + test.value(result.body.version, AUDIT_API_DEFAULT_VERSION) + test.value(result.body.events.length).is(7) + test.value(result.body.provider_event_type).is(VEHICLE_EVENTS.agency_drop_off) + test.value(result.body.provider_event_type_reason).is(VEHICLE_REASONS.rebalance) + test.value(result.body.provider_status).is('available') + test.value(result.body.provider_telemetry.charge).is(0.5) + test.value(result.body.provider_event_time).is(AUDIT_START) + done(err) + }) + }) + it(`verify get audit (no scope)`, done => { request .get(`/audit/trips/${uuid()}`) @@ -339,6 +381,7 @@ describe('Testing API', () => { .expect(403) .end((err, result) => { test.value(result).hasHeader('content-type', APP_JSON) + test.value(result.body.version, AUDIT_API_DEFAULT_VERSION) done(err) }) }) @@ -350,6 +393,7 @@ describe('Testing API', () => { .expect(404) .end((err, result) => { test.value(result).hasHeader('content-type', APP_JSON) + test.value(result.body.version, AUDIT_API_DEFAULT_VERSION) done(err) }) }) @@ -361,6 +405,7 @@ describe('Testing API', () => { .expect(403) .end((err, result) => { test.value(result).hasHeader('content-type', APP_JSON) + test.value(result.body.version, AUDIT_API_DEFAULT_VERSION) done(err) }) }) @@ -387,6 +432,7 @@ describe('Testing API', () => { .expect(200) .end((err, result) => { test.value(result).hasHeader('content-type', APP_JSON) + test.value(result.body.version, AUDIT_API_DEFAULT_VERSION) test.value(result.body.count).is(count) test.value(result.body.audits.length).is(count) done(err) @@ -401,6 +447,7 @@ describe('Testing API', () => { .expect(403) .end((err, result) => { test.value(result).hasHeader('content-type', APP_JSON) + test.value(result.body.version, AUDIT_API_DEFAULT_VERSION) done(err) }) }) @@ -412,6 +459,7 @@ describe('Testing API', () => { .expect(404) .end((err, result) => { test.value(result).hasHeader('content-type', APP_JSON) + test.value(result.body.version, AUDIT_API_DEFAULT_VERSION) done(err) }) }) @@ -423,7 +471,8 @@ describe('Testing API', () => { .expect(200) .end((err, result) => { test.value(result).hasHeader('content-type', APP_JSON) - test.value(result.body).is({}) + test.value(result.body.version, AUDIT_API_DEFAULT_VERSION) + test.value(result.body, { version: AUDIT_API_DEFAULT_VERSION }) done(err) }) }) @@ -443,6 +492,7 @@ describe('Testing API', () => { .expect(200) .end((err, result) => { test.value(result).hasHeader('content-type', APP_JSON) + test.value(result.body.version, AUDIT_API_DEFAULT_VERSION) test.object(result).hasProperty('body') test.object(result.body).hasProperty('provider_device') test.value(result.body.provider_device).is(null) @@ -457,6 +507,7 @@ describe('Testing API', () => { .expect(200) .end((err, result) => { test.value(result).hasHeader('content-type', APP_JSON) + test.value(result.body.version, AUDIT_API_DEFAULT_VERSION) test.value(result.body.events.length).is(1) done(err) }) @@ -465,10 +516,7 @@ describe('Testing API', () => { const vehicles = [ provider_vehicle_id, // test-vehicle provider_vehicle_id.toUpperCase(), // TEST-VEHICLE - provider_vehicle_id - .replace('-', '_') - .split('') - .join('-'), // t-e-s-t-_-v-e-h-i-c-l-e + provider_vehicle_id.replace('-', '_').split('').join('-'), // t-e-s-t-_-v-e-h-i-c-l-e provider_vehicle_id .split('') .map((char, index) => (index % 2 ? char.toLowerCase() : char.toUpperCase())) @@ -490,6 +538,7 @@ describe('Testing API', () => { .expect(200) .end((err, result) => { test.value(result).hasHeader('content-type', APP_JSON) + test.value(result.body.version, AUDIT_API_DEFAULT_VERSION) test.object(result).hasProperty('body') test.object(result.body).hasProperty('provider_device') test.object(result.body.provider_device).hasProperty('vehicle_id') @@ -499,19 +548,23 @@ describe('Testing API', () => { }) ) describe('Tests retreiving vehicles', () => { - let devices_a: Device[] + let devices_a: Device[] // Have events and telemetry outside our BBOX + let devices_b: Device[] // Have events and telemetry inside our BBOX + let devices_c: Device[] // No events or telemetry before(done => { devices_a = makeDevices(10, now(), MOCHA_PROVIDER_ID) - const events_a = makeEventsWithTelemetry(devices_a, now(), SAN_FERNANDO_VALLEY, VEHICLE_EVENTS.trip_start) // Generate a bunch of events outside of our BBOX - const devices_b = makeDevices(10, now(), MOCHA_PROVIDER_ID) - const events_b = makeEventsWithTelemetry(devices_b, now(), CANALS, VEHICLE_EVENTS.trip_start) // Generate a bunch of events inside of our BBOX + const events_a = makeEventsWithTelemetry(devices_a, now(), SAN_FERNANDO_VALLEY, VEHICLE_EVENTS.trip_start) const telemetry_a = devices_a.map(device => makeTelemetryInArea(device, now(), SAN_FERNANDO_VALLEY, rangeRandomInt(10)) ) + devices_b = makeDevices(10, now(), MOCHA_PROVIDER_ID) + const events_b = makeEventsWithTelemetry(devices_b, now(), CANALS, VEHICLE_EVENTS.trip_start) const telemetry_b = devices_b.map(device => makeTelemetryInArea(device, now(), CANALS, rangeRandomInt(10))) + devices_c = makeDevices(10, now(), MOCHA_PROVIDER_ID) const seedData = { - devices: [...devices_a, ...devices_b], + // Include a duplicate device (same vin + provider but different device_id) + devices: [...devices_a, ...devices_b, ...devices_c, { ...devices_c[0], ...{ device_id: uuid() } }], events: [...events_a, ...events_b], telemetry: [...telemetry_a, ...telemetry_b] } @@ -523,13 +576,17 @@ describe('Testing API', () => { }) it('Verify getting vehicles inside of a bounding box', done => { - const bbox = [[-118.484776, 33.996855], [-118.452283, 33.96299]] // BBOX encompasses the entirity of CANALS + const bbox = [ + [-118.484776, 33.996855], + [-118.452283, 33.96299] + ] // BBOX encompasses the entirity of CANALS request .get(`/vehicles?bbox=${JSON.stringify(bbox)}`) .set('Authorization', SCOPED_AUTH(['audits:vehicles:read'], audit_subject_id)) .expect(200) .end((err, result) => { test.value(result).hasHeader('content-type', APP_JSON) + test.value(result.body.version, AUDIT_API_DEFAULT_VERSION) test.assert(result.body.vehicles.length === 10) result.body.vehicles.forEach((device: Device & { updated?: Timestamp | null; telemetry: Telemetry }) => { test.assert(typeof device.telemetry.gps.lat === 'number') @@ -547,6 +604,7 @@ describe('Testing API', () => { .expect(200) .end((err, result) => { test.value(result).hasHeader('content-type', APP_JSON) + test.value(result.body.version, AUDIT_API_DEFAULT_VERSION) test.object(result).hasProperty('body') test.object(result.body).hasProperty('vehicles') test.value(result.body.vehicles[0].provider_id).is(devices_a[0].provider_id) @@ -565,6 +623,242 @@ describe('Testing API', () => { .expect(404) .end((err, result) => { test.value(result).hasHeader('content-type', APP_JSON) + test.value(result.body.version, AUDIT_API_DEFAULT_VERSION) + done(err) + }) + }) + + it('Verify get vehicle by vehicle_id and provider_id (no event or telemetry)', done => { + request + .get(`/audit/vehicles/${devices_c[0].provider_id}/vin/${devices_c[0].vehicle_id}`) + .set('Authorization', SCOPED_AUTH(['audits:vehicles:read'], audit_subject_id)) + .expect(200) + .end((err, result) => { + test.value(result.body.vehicles[0].provider_id).is(devices_c[0].provider_id) + test.value(result.body.vehicles[0].vehicle_id).is(devices_c[0].vehicle_id) + test.object(result.body.vehicles[0]).hasNotProperty('status') + done(err) + }) + }) + + it('Verify get vehicle by vehicle_id and provider_id (duplicate device)', done => { + request + .get(`/audit/vehicles/${devices_c[0].provider_id}/vin/${devices_c[0].vehicle_id}`) + .set('Authorization', SCOPED_AUTH(['audits:vehicles:read'], audit_subject_id)) + .expect(200) + .end((err, result) => { + test.value(result).hasHeader('content-type', APP_JSON) + test.value(result.body.version, AUDIT_API_DEFAULT_VERSION) + test.object(result).hasProperty('body') + test.object(result.body).hasProperty('vehicles') + test.value(result.body.vehicles[0].provider_id).is(devices_c[0].provider_id) + test.value(result.body.vehicles[0].vehicle_id).is(devices_c[0].vehicle_id) + test.value(result.body.vehicles[0].device_id).isNot(devices_c[0].device_id) + done(err) + }) + }) + }) + const attachment_id = uuid() + const baseUrl = 'http://example.com/' + describe('Tests for attachments', () => { + before(done => { + const audit = { + audit_trip_id, + audit_device_id, + audit_subject_id, + provider_id, + provider_name: 'test', + provider_vehicle_id, + provider_device_id, + timestamp: AUDIT_START, + recorded: AUDIT_START + } as Audit + const attachment = { + attachment_id, + attachment_filename: `${attachment_id}.jpg`, + base_url: baseUrl, + mimetype: 'image/jpeg', + thumbnail_filename: `${attachment_id}.thumbnail.jpg`, + attachment_mimetype: 'image/jpeg', + recorded: AUDIT_START + } as Attachment + const auditAttachment = { + attachment_id, + audit_trip_id, + recorded: AUDIT_START + } as AuditAttachment + Promise.all([db.initialize(), cache.initialize()]).then(async () => { + await db.writeDevice({ + device_id: provider_device_id, + provider_id, + vehicle_id: provider_vehicle_id, + propulsion: [PROPULSION_TYPES.electric], + type: VEHICLE_TYPES.scooter, + recorded: AUDIT_START + }) + await db.writeAudit(audit) + await db.writeAttachment(attachment) + await db.writeAuditAttachment(auditAttachment) + done() + }) + }) + + afterEach(() => { + Sinon.restore() + }) + + it('verify get audit by id with attachments', done => { + request + .get(`/audit/trips/${audit_trip_id}`) + .set('Authorization', SCOPED_AUTH(['audits:read'], audit_subject_id)) + .expect(200) + .end((err, result) => { + test.value(result.body.attachments[0].attachment_id).is(attachment_id) + test.value(result.body.attachments[0].attachment_url).is(`http://example.com/${attachment_id}.jpg`) + test.value(result.body.attachments[0].thumbnail_url).is(`http://example.com/${attachment_id}.thumbnail.jpg`) + done(err) + }) + }) + + it('verify get trips with attachments', done => { + request + .get('/audit/trips') + .set('Authorization', SCOPED_AUTH(['audits:read'], audit_subject_id)) + .expect(200) + .end((err, result) => { + test.value(result.body.audits[0].attachments[0].attachment_id).is(attachment_id) + test.value(result.body.audits[0].attachments[0].attachment_url).is(`http://example.com/${attachment_id}.jpg`) + test + .value(result.body.audits[0].attachments[0].thumbnail_url) + .is(`http://example.com/${attachment_id}.thumbnail.jpg`) + done(err) + }) + }) + + it('verify post attachment (audit not found)', done => { + request + .post(`/trips/${uuid()}/attach/image%2Fpng`) + .set('Authorization', SCOPED_AUTH(['audits:write'], audit_subject_id)) + .send({}) // TODO: include file + .expect(404) + .end((err, result) => { + test.value(result.body.error.name).is('NotFoundError') + test.value(result.body.error.reason).is('audit not found') + done(err) + }) + }) + + const attachmentTests = [ + { + name: 'no file', + file: 'empty.png', + status: 400, + errName: 'ValidationError', + errReason: 'No attachment found' + }, + { + name: 'missing extension', + file: 'samplepng', + status: 400, + errName: 'ValidationError', + errReason: `Missing file extension in filename samplepng` + }, + { + name: 'unsupported mimetype', + file: 'sample.gif', + status: 415, + errName: 'UnsupportedTypeError', + errReason: `Unsupported mime type image/gif` + } + ] + + attachmentTests.forEach(testCase => + it(`verify post bad attachment (${testCase.name})`, done => { + request + .post(`/trips/${audit_trip_id}/attach/image%2Fpng`) + .set('Authorization', SCOPED_AUTH(['audits:write'], audit_subject_id)) + .attach('file', `./tests/${testCase.file}`) + .expect(testCase.status) + .end((err, result) => { + test.value(result.body.error.name).is(testCase.errName) + test.value(result.body.error.reason).is(testCase.errReason) + done(err) + }) + }) + ) + + it('verify audit attach (success)', done => { + const fake = Sinon.fake.returns({ + audit_trip_id, + attachment_id, + recorded: AUDIT_START, + attachment_filename: `${attachment_id}.jpg`, + base_url: baseUrl, + mimetype: 'image/jpeg' + } as Attachment) + Sinon.replace(attachments, 'writeAttachment', fake) + request + .post(`/trips/${audit_trip_id}/attach/image%2Fpng`) + .set('Authorization', SCOPED_AUTH(['audits:write'], audit_subject_id)) + .attach('file', `./tests/sample.png`) + .expect(200) + .end((err, result) => { + test.value(result.body.attachment_id).is(attachment_id) + test.value(result.body.attachment_url).is(`${baseUrl + attachment_id}.jpg`) + test.value(result.body.thumbnail_url).is('') + done(err) + }) + }) + + it('verify audit attach (error)', done => { + const fake = Sinon.fake.returns(null) + Sinon.replace(attachments, 'writeAttachment', fake) + request + .post(`/trips/${audit_trip_id}/attach/image%2Fpng`) + .set('Authorization', SCOPED_AUTH(['audits:write'], audit_subject_id)) + .attach('file', `./tests/sample.png`) + .expect(500) + .end((err, result) => { + test.value(result.body.error.name).is('ServerError') + done(err) + }) + }) + + it('verify audit delete (success)', done => { + const fake = Sinon.fake.returns(null) + Sinon.replace(attachments, 'deleteAuditAttachment', fake) + request + .delete(`/trips/${audit_trip_id}/attachment/${attachment_id}`) + .set('Authorization', SCOPED_AUTH(['audits:write'], audit_subject_id)) + .expect(200) + .end((err, result) => { + test.value(result.body, { version: AUDIT_API_DEFAULT_VERSION }) + done(err) + }) + }) + + it('verify audit delete (not found)', done => { + const fake = Sinon.fake.throws(new NotFoundError()) + Sinon.replace(attachments, 'deleteAuditAttachment', fake) + request + .delete(`/trips/${audit_trip_id}/attachment/${attachment_id}`) + .set('Authorization', SCOPED_AUTH(['audits:write'], audit_subject_id)) + .expect(404) + .end((err, result) => { + test.value(result.body.error.name).is('NotFoundError') + done(err) + }) + }) + + it('verify audit delete (error)', done => { + const fake = Sinon.fake.throws(new Error()) + Sinon.replace(attachments, 'deleteAuditAttachment', fake) + request + .delete(`/trips/${audit_trip_id}/attachment/${attachment_id}`) + .set('Authorization', SCOPED_AUTH(['audits:write'], audit_subject_id)) + .expect(500) + .end((err, result) => { + test.value(result.body.error.name).is('ServerError') done(err) }) }) diff --git a/packages/mds-audit/tests/attachments.spec.ts b/packages/mds-audit/tests/attachments.spec.ts new file mode 100644 index 000000000..01cceaef9 --- /dev/null +++ b/packages/mds-audit/tests/attachments.spec.ts @@ -0,0 +1,154 @@ +/* eslint-disable promise/no-callback-in-promise */ +/* eslint-disable promise/no-nesting */ +/* eslint-disable promise/prefer-await-to-then */ +/* eslint-disable promise/always-return */ +/* eslint-disable promise/prefer-await-to-callbacks */ +/* eslint-disable @typescript-eslint/no-floating-promises */ +/* eslint-disable promise/catch-or-return */ +/* + Copyright 2019 City of Los Angeles. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +import db from '@mds-core/mds-db' +import { Attachment, AuditAttachment, Recorded } from '@mds-core/mds-types' +import { isUUID, NotFoundError, uuid } from '@mds-core/mds-utils' +import assert from 'assert' +import fs from 'fs' +import Sinon from 'sinon' +import { attachmentSummary, deleteAuditAttachment, readAttachments, writeAttachment } from '../attachments' +import { getWriteableClient } from '../../mds-db/client' +import schema from '../../mds-db/schema' + +/* eslint-disable-next-line */ +const aws = require('aws-sdk') + +describe('Testing Attachments Service', () => { + const attachmentId = uuid() + const auditTripId = uuid() + const baseUrl = 'http://example.com/' + const mimetype = 'image/png' + const extension = '.png' + const now = Date.now() + const attachment = { + attachment_id: attachmentId, + attachment_filename: attachmentId + extension, + base_url: baseUrl, + mimetype, + thumbnail_filename: `${attachmentId}.thumbnail${extension}`, + attachment_mimetype: mimetype, + recorded: now + } as Attachment + const auditAttachment = { + attachment_id: attachmentId, + audit_trip_id: auditTripId, + recorded: now + } as AuditAttachment + const recordedAttachment = { + ...{ id: 1 }, + ...attachment + } as Recorded + const attachmentFile = { + fieldname: 'file', + originalname: 'sample.png', + encoding: '7bit', + mimetype, + size: 68, + buffer: fs.readFileSync('./tests/sample.png') + } as Express.Multer.File + + before('Initializing database', async () => { + await db.initialize() + }) + + beforeEach(async () => { + const client = await getWriteableClient() + await client.query(`TRUNCATE ${schema.TABLE.attachments}, ${schema.TABLE.audit_attachments}`) + }) + + it('verify attachment summary', () => { + const summary = attachmentSummary(attachment) + assert.equal(summary.attachment_id, attachment.attachment_id) + assert.equal(summary.attachment_url, attachment.base_url + attachment.attachment_filename) + assert.equal(summary.thumbnail_url, attachment.base_url + attachment.thumbnail_filename) + }) + + it('verify attachment summary (without thumbnail)', () => { + const summary = attachmentSummary({ ...attachment, ...{ thumbnail_filename: '' } }) + assert.equal(summary.attachment_id, attachment.attachment_id) + assert.equal(summary.attachment_url, attachment.base_url + attachment.attachment_filename) + assert.equal(summary.thumbnail_url, '') + }) + + it('verify writeAttachment', async () => { + const uploadStub = Sinon.stub(aws.S3.prototype, 'upload') + const writeAttachmentStub = Sinon.stub(db, 'writeAttachment') + const writeAuditAttachmentStub = Sinon.stub(db, 'writeAuditAttachment') + uploadStub.returns({ + promise: () => { + // Intentionally empty + } + }) + const res: Attachment | null = await writeAttachment(attachmentFile, auditTripId) + assert.equal(res && res.attachment_filename.includes('.png'), true) + assert.equal(res && res.thumbnail_filename && res.thumbnail_filename.includes('.png'), true) + assert.equal(res && isUUID(res.attachment_id), true) + assert.equal(res && res.mimetype, mimetype) + assert.equal(res && res.thumbnail_mimetype, mimetype) + Sinon.assert.calledTwice(uploadStub) // For attachment and thumbnail + Sinon.assert.calledOnce(writeAttachmentStub) + Sinon.assert.calledOnce(writeAuditAttachmentStub) + }) + + it('verify readAttachment', async () => { + const readAttachmentsStub = Sinon.stub(db, 'readAttachmentsForAudit') + readAttachmentsStub.resolves([recordedAttachment, recordedAttachment]) + const res: Recorded[] = await readAttachments(auditTripId) + Sinon.assert.calledOnce(readAttachmentsStub) + assert.equal(res.length, 2) + assert.equal(res[0], recordedAttachment) + assert.equal(res[1], recordedAttachment) + }) + + it('verify delete audit attachment (not found)', async () => { + const deleteS3ObjectSpy = Sinon.spy(aws.S3.prototype.deleteObject) + const deleteAttachmentSpy = Sinon.spy(db, 'deleteAttachment') + const deleteAuditAttachmentSpy = Sinon.spy(db, 'deleteAuditAttachment') + await assert.rejects(() => deleteAuditAttachment(uuid(), uuid()), NotFoundError) + Sinon.assert.notCalled(deleteS3ObjectSpy) + Sinon.assert.notCalled(deleteAttachmentSpy) + Sinon.assert.calledOnce(deleteAuditAttachmentSpy) + }) + + it('verify delete audit attachment (still in use)', async () => { + const deleteS3ObjectSpy = Sinon.spy(aws.S3.prototype.deleteObject) + const deleteAttachmentSpy = Sinon.spy(db, 'deleteAttachment') + const deleteAuditAttachmentSpy = Sinon.spy(db, 'deleteAuditAttachment') + await db.writeAuditAttachment(auditAttachment) + await db.writeAuditAttachment({ ...auditAttachment, ...{ audit_trip_id: uuid() } }) + await deleteAuditAttachment(auditAttachment.audit_trip_id, auditAttachment.attachment_id) + Sinon.assert.notCalled(deleteS3ObjectSpy) + Sinon.assert.notCalled(deleteAttachmentSpy) + Sinon.assert.calledOnce(deleteAuditAttachmentSpy) + }) + + afterEach(() => { + Sinon.restore() + }) + + after('Clearing and shutting down database', async () => { + await db.initialize() + await db.shutdown() + }) +}) diff --git a/packages/mds-audit/tests/empty.png b/packages/mds-audit/tests/empty.png new file mode 100644 index 000000000..e69de29bb diff --git a/packages/mds-audit/tests/sample.gif b/packages/mds-audit/tests/sample.gif new file mode 100644 index 0000000000000000000000000000000000000000..f191b280ce91e6cb8c387735c10ef9bc5da6c83b GIT binary patch literal 42 ocmZ?wbhEHbWMp7uXkY+=|Ns9h{$ybUF?B!$NQQxl(S^Yp0J!f4_W%F@ literal 0 HcmV?d00001 diff --git a/packages/mds-audit/tests/sample.png b/packages/mds-audit/tests/sample.png new file mode 100644 index 0000000000000000000000000000000000000000..909c66db1740b7c1b41eb4db6c414a7ab5bb6a23 GIT binary patch literal 68 zcmeAS@N?(olHy`uVBq!ia0vp^j3CUx0wlM}@Gt=>Zci7-kcwN$DG5Lh8v~O;;{|;n Oi^0>?&t;ucLK6U5DhwL{ literal 0 HcmV?d00001 diff --git a/packages/mds-audit/tests/samplepng b/packages/mds-audit/tests/samplepng new file mode 100644 index 0000000000000000000000000000000000000000..909c66db1740b7c1b41eb4db6c414a7ab5bb6a23 GIT binary patch literal 68 zcmeAS@N?(olHy`uVBq!ia0vp^j3CUx0wlM}@Gt=>Zci7-kcwN$DG5Lh8v~O;;{|;n Oi^0>?&t;ucLK6U5DhwL{ literal 0 HcmV?d00001 diff --git a/packages/mds-audit/tsconfig.build.json b/packages/mds-audit/tsconfig.build.json index 9d62ea484..86d94c169 100644 --- a/packages/mds-audit/tsconfig.build.json +++ b/packages/mds-audit/tsconfig.build.json @@ -6,10 +6,11 @@ "references": [ { "path": "../../packages/mds-api-helpers/tsconfig.build.json" }, { "path": "../../packages/mds-api-server/tsconfig.build.json" }, - { "path": "../../packages/mds-cache/tsconfig.build.json" }, + { "path": "../../packages/mds-agency-cache/tsconfig.build.json" }, { "path": "../../packages/mds-db/tsconfig.build.json" }, { "path": "../../packages/mds-logger/tsconfig.build.json" }, { "path": "../../packages/mds-providers/tsconfig.build.json" }, + { "path": "../../packages/mds-schema-validators/tsconfig.build.json" }, { "path": "../../packages/mds-test-data/tsconfig.build.json" }, { "path": "../../packages/mds-types/tsconfig.build.json" }, { "path": "../../packages/mds-utils/tsconfig.build.json" } diff --git a/packages/mds-audit/tsconfig.eslint.json b/packages/mds-audit/tsconfig.eslint.json new file mode 100644 index 000000000..2a400942a --- /dev/null +++ b/packages/mds-audit/tsconfig.eslint.json @@ -0,0 +1 @@ +{ "extends": "../../tsconfig.eslint.json" } diff --git a/packages/mds-audit/types.ts b/packages/mds-audit/types.ts index 76c2f88c5..25296b82f 100644 --- a/packages/mds-audit/types.ts +++ b/packages/mds-audit/types.ts @@ -14,15 +14,31 @@ limitations under the License. */ -import { Audit, Telemetry, Timestamp, UUID } from '@mds-core/mds-types' -import { ApiRequest, ApiResponse, ApiResponseLocals } from '@mds-core/mds-api-server' +import { + Audit, + Telemetry, + Timestamp, + UUID, + Device, + VehicleEvent, + AttachmentSummary, + AuditEvent, + WithGpsProperty, + VEHICLE_EVENT, + VEHICLE_REASON, + VEHICLE_STATUS +} from '@mds-core/mds-types' +import { ApiRequest, ApiQuery, ApiClaims, ApiVersionedResponse } from '@mds-core/mds-api-server' +import { Params, ParamsDictionary } from 'express-serve-static-core' + +export const AUDIT_API_SUPPORTED_VERSIONS = ['0.1.0'] as const +export type AUDIT_API_SUPPORTED_VERSION = typeof AUDIT_API_SUPPORTED_VERSIONS[number] +export const [AUDIT_API_DEFAULT_VERSION] = AUDIT_API_SUPPORTED_VERSIONS // Allow adding type definitions for Express Request objects -export type AuditApiRequest = ApiRequest +export type AuditApiRequest

= ApiRequest

-export interface AuditApiTripRequest extends AuditApiRequest { - params: { audit_trip_id: UUID } -} +export type AuditApiTripRequest = AuditApiRequest<{ audit_trip_id: UUID }> export interface AuditApiAuditStartRequest extends AuditApiTripRequest { body: { @@ -74,25 +90,10 @@ export interface AuditApiAuditEndRequest extends AuditApiTripRequest { } } -export interface AuditApiGetTripsRequest extends AuditApiRequest { - // Query string parameters always come in as strings - query: Partial< - { - [P in - | 'skip' - | 'take' - | 'provider_id' - | 'provider_vehicle_id' - | 'audit_subject_id' - | 'start_time' - | 'end_time']: string - } - > -} +export type AuditApiGetTripsRequest = AuditApiRequest & + ApiQuery<'skip' | 'take' | 'provider_id' | 'provider_vehicle_id' | 'audit_subject_id' | 'start_time' | 'end_time'> -export interface AuditApiGetTripRequest extends AuditApiTripRequest { - query: Partial<{ [P in 'event_viewport_adjustment']: string }> -} +export type AuditApiGetTripRequest = AuditApiTripRequest & ApiQuery<'event_viewport_adjustment'> export interface AuditApiGetVehicleRequest extends AuditApiRequest { params: { @@ -101,12 +102,103 @@ export interface AuditApiGetVehicleRequest extends AuditApiRequest { } } +export type AuditApiAccessTokenScopes = 'audits:write' | 'audits:read' | 'audits:delete' | 'audits:vehicles:read' + +type AuditWithAttachmentSummary = Audit & { attachments: AttachmentSummary[]; id: number } +type AuditedDevice = + | Readonly< + Required< + Device & { + id: number + } + > + > + | (Device & { + updated?: number | null | undefined + }) // Allow adding type definitions for Express Response objects -export interface AuditApiResponse extends ApiResponse { - locals: ApiResponseLocals & { +export type AuditApiResponse = ApiVersionedResponse< + AUDIT_API_SUPPORTED_VERSION, + ApiClaims & { audit_subject_id: string audit_trip_id: UUID audit: Audit | null recorded: Timestamp + }, + T +> + +export type PostAuditTripStartResponse = AuditApiResponse<{ + provider_id: string + provider_name: string + provider_vehicle_id: string + provider_device: (Device & { updated?: number | null | undefined }) | null +}> + +export type PostAuditTripVehicleEventResponse = AuditApiResponse<{}> +export type PostAuditTripTelemetryResponse = AuditApiResponse<{}> +export type PostAuditTripNoteResponse = AuditApiResponse<{}> +export type PostAuditTripEventResponse = AuditApiResponse<{}> +export type PostAuditTripEndResponse = AuditApiResponse<{}> +export type PostAuditAttachmentResponse = AuditApiResponse + +type ReadOnlyVehicleEvent = Readonly> +type ReadOnlyAuditEvent = Readonly> + +export type GetAuditTripDetailsResponse = AuditApiResponse< + Audit & { + events: WithGpsProperty[] + attachments: AttachmentSummary[] + provider_event_type?: VEHICLE_EVENT + provider_event_type_reason?: VEHICLE_REASON | null + provider_status?: VEHICLE_STATUS // any // EVENT_STATUS_MAP[providerEvent[0]?.event_type as VEHICLE_EVENT], + provider_telemetry?: Telemetry | null // providerEvent[0]?.telemetry, + provider_event_time?: Timestamp // providerEvent[0]?.timestamp, + provider: { + device: AuditedDevice + events: ReadOnlyVehicleEvent[] | never[] + telemetry: Readonly< + Required< + Telemetry & { + id: number + } + > + >[] + } | null } -} +> + +export type GetAuditTripsDetailsResponse = AuditApiResponse<{ + count: number + audits: AuditWithAttachmentSummary[] + links: + | Partial<{ + first: string | undefined + prev: string | undefined + next: string | undefined + last: string | undefined + }> + | undefined +}> + +export type GetAuditVehiclesResponse = AuditApiResponse<{ + total: number + links: { + first: string + last: string + prev: string | null + next: string | null + } + vehicles: (Device & VehicleEvent)[] +}> +export type GetVehicleByVinResponse = AuditApiResponse<{ + vehicles: ( + | (Device & { + updated?: number | null | undefined + }) + | null + )[] +}> + +export type DeleteAuditTripResponse = AuditApiResponse<{}> +export type DeleteAuditAttachmentResponse = AuditApiResponse<{}> diff --git a/packages/mds-compliance/api.ts b/packages/mds-compliance/api.ts index c97a523cc..618644120 100644 --- a/packages/mds-compliance/api.ts +++ b/packages/mds-compliance/api.ts @@ -15,28 +15,38 @@ */ import express from 'express' -import cache from '@mds-core/mds-cache' +import cache from '@mds-core/mds-agency-cache' import db from '@mds-core/mds-db' -import log from '@mds-core/mds-logger' +import logger from '@mds-core/mds-logger' import { isUUID, now, - days, pathsFor, getPolygon, pointInShape, isInStatesOrEvents, - ServerError + ServerError, + NotFoundError, + BadParamsError, + AuthorizationError } from '@mds-core/mds-utils' import { Geography, Device, UUID, VehicleEvent } from '@mds-core/mds-types' import { TEST1_PROVIDER_ID, TEST2_PROVIDER_ID, BLUE_SYSTEMS_PROVIDER_ID, providerName } from '@mds-core/mds-providers' import { Geometry, FeatureCollection } from 'geojson' +import { parseRequest } from '@mds-core/mds-api-helpers' import * as compliance_engine from './mds-compliance-engine' -import { ComplianceApiRequest, ComplianceApiResponse } from './types' +import { + ComplianceApiRequest, + ComplianceApiResponse, + ComplianceSnapshotApiResponse, + ComplianceCountApiResponse +} from './types' +import { ComplianceApiVersionMiddleware } from './middleware' const AllowedProviderIDs = [TEST1_PROVIDER_ID, TEST2_PROVIDER_ID, BLUE_SYSTEMS_PROVIDER_ID] function api(app: express.Express): express.Express { + app.use(ComplianceApiVersionMiddleware) app.use(async (req: ComplianceApiRequest, res: ComplianceApiResponse, next: express.NextFunction) => { try { // verify presence of provider_id @@ -46,61 +56,56 @@ function api(app: express.Express): express.Express { /* istanbul ignore next */ if (!provider_id) { - await log.warn('Missing provider_id in', req.originalUrl) - return res.status(400).send({ - result: 'missing provider_id' - }) + logger.warn('Missing provider_id in', req.originalUrl) + return res.status(400).send({ error: new BadParamsError('missing provider_id') }) } /* istanbul ignore next */ if (!isUUID(provider_id)) { - await log.warn(req.originalUrl, 'invalid provider_id is not a UUID', provider_id) - return res.status(400).send({ - result: `invalid provider_id ${provider_id} is not a UUID` - }) + logger.warn(req.originalUrl, 'invalid provider_id is not a UUID', provider_id) + return res + .status(400) + .send({ error: new BadParamsError(`invalid provider_id ${provider_id} is not a UUID`) }) } // stash provider_id res.locals.provider_id = provider_id - log.info(providerName(provider_id), req.method, req.originalUrl) + logger.info(providerName(provider_id), req.method, req.originalUrl) } else { - return res.status(401).send('Unauthorized') + return res.status(401).send({ error: new AuthorizationError('Unauthorized') }) } } } catch (err) { /* istanbul ignore next */ - await log.error(req.originalUrl, 'request validation fail:', err.stack) + logger.error(req.originalUrl, 'request validation fail:', err.stack) } next() }) - app.get(pathsFor('/snapshot/:policy_uuid'), async (req: ComplianceApiRequest, res: ComplianceApiResponse) => { - const { provider_id } = res.locals - const { provider_id: queried_provider_id } = req.query - - /* istanbul ignore next */ - async function fail(err: Error) { - await log.error(err.stack || err) - return res.status(500).send(new ServerError()) + app.get(pathsFor('/snapshot/:policy_uuid'), async (req: ComplianceApiRequest, res: ComplianceSnapshotApiResponse) => { + const { provider_id, version } = res.locals + const { provider_id: queried_provider_id, timestamp } = { + ...parseRequest(req).query('provider_id'), + ...parseRequest(req, { parser: Number }).query('timestamp') } + // default to now() if no timestamp supplied + const query_date = timestamp || now() + const { policy_uuid } = req.params - const { end_date: query_end_date } = req.query if (!isUUID(policy_uuid)) { - return res.status(400).send({ err: 'bad_param' }) + return res.status(400).send({ error: new BadParamsError('Bad policy UUID') }) } - const { start_date, end_date } = query_end_date - ? { end_date: parseInt(query_end_date), start_date: parseInt(query_end_date) - days(365) } - : { end_date: now() + days(365), start_date: now() - days(365) } + try { - const all_policies = await db.readPolicies({ start_date }) + const all_policies = await db.readActivePolicies(query_date) const policy = compliance_engine.filterPolicies(all_policies).find(p => { return p.policy_id === policy_uuid }) if (!policy) { - return res.status(404).send({ err: 'not found' }) + return res.status(404).send({ error: new NotFoundError('Policy not found') }) } if ( @@ -110,6 +115,7 @@ function api(app: express.Express): express.Express { (AllowedProviderIDs.includes(provider_id) && ((policy.provider_ids && policy.provider_ids.length !== 0 && + queried_provider_id && policy.provider_ids.includes(queried_provider_id)) || !policy.provider_ids || policy.provider_ids.length === 0))) @@ -127,10 +133,10 @@ function api(app: express.Express): express.Express { ]) const deviceIdSubset = deviceRecords.map((record: { device_id: UUID; provider_id: UUID }) => record.device_id) const devices = await cache.readDevices(deviceIdSubset) - const events = - query_end_date && end_date < now() - ? await db.readHistoricalEvents({ provider_id: target_provider_id, end_date }) - : await cache.readEvents(deviceIdSubset) + // if a timestamp was supplied, the data we want is probably old enough it's going to be in the db + const events = timestamp + ? await db.readHistoricalEvents({ provider_id: target_provider_id, end_date: timestamp }) + : await cache.readEvents(deviceIdSubset) const deviceMap = devices.reduce((map: { [d: string]: Device }, device) => { return device ? Object.assign(map, { [device.device_id]: device }) : map @@ -139,49 +145,50 @@ function api(app: express.Express): express.Express { const filteredEvents = compliance_engine.filterEvents(events) const result = compliance_engine.processPolicy(policy, filteredEvents, geographies, deviceMap) if (result === undefined) { - return res.status(400).send({ err: 'bad_param' }) + return res.status(400).send({ error: new BadParamsError('Unable to process compliance results') }) } - return res.status(200).send(result) + + return res.status(200).send({ ...result, timestamp: query_date, version }) } } else { - return res.status(401).send({ err: 'Unauthorized' }) + return res.status(401).send({ error: new AuthorizationError() }) } } catch (err) { - if (err.message.includes('not_found')) { - return res.status(400).send({ err: 'bad_param' }) - } - await fail(err) + return res.status(500).send({ error: new ServerError() }) } }) - app.get(pathsFor('/count/:rule_id'), async (req: ComplianceApiRequest, res: ComplianceApiResponse) => { - if (!AllowedProviderIDs.includes(res.locals.provider_id)) { - return res.status(401).send({ result: 'unauthorized access' }) + app.get(pathsFor('/count/:rule_id'), async (req: ComplianceApiRequest, res: ComplianceCountApiResponse) => { + const { timestamp } = { + ...parseRequest(req, { parser: Number }).query('timestamp') } - - async function fail(err: Error) { - await log.error(err.stack || err) - if (err.message.includes('invalid rule_id')) { - return res.status(404).send(err.message) - } - /* istanbul ignore next */ - return res - .status(500) - .send({ error: 'server_error', error_description: 'an internal server error has occurred and been logged' }) + const query_date = timestamp || now() + if (!AllowedProviderIDs.includes(res.locals.provider_id)) { + return res.status(401).send({ error: new AuthorizationError('Unauthorized') }) } const { rule_id } = req.params try { - const rule = await db.readRule(rule_id) + const activePolicies = await db.readActivePolicies(query_date) + const [policy] = activePolicies.filter(activePolicy => { + const matches = activePolicy.rules.filter(policy_rule => policy_rule.rule_id === rule_id) + return matches.length !== 0 + }) + if (!policy) { + throw new NotFoundError('invalid rule_id') + } + const [rule] = policy.rules.filter(r => r.rule_id === rule_id) const geography_ids = rule.geographies.reduce((acc: UUID[], geo: UUID) => { return [...acc, geo] }, []) - const geographies = (await Promise.all( - geography_ids.reduce((acc: Promise[], geography_id) => { - const geography = db.readSingleGeography(geography_id) - return [...acc, geography] - }, []) - )).reduce((acc: Geography[], geos) => { + const geographies = ( + await Promise.all( + geography_ids.reduce((acc: Promise[], geography_id) => { + const geography = db.readSingleGeography(geography_id) + return [...acc, geography] + }, []) + ) + ).reduce((acc: Geography[], geos) => { return [...acc, geos] }, []) @@ -189,11 +196,13 @@ function api(app: express.Express): express.Express { return [...acc, getPolygon(geographies, geography.geography_id)] }, []) + const events = timestamp ? await db.readHistoricalEvents({ end_date: timestamp }) : await cache.readAllEvents() + // https://stackoverflow.com/a/51577579 to remove nulls in typesafe way - const events = (await cache.readAllEvents()).filter( + const filteredVehicleEvents = events.filter( (event): event is VehicleEvent => event !== null && isInStatesOrEvents(rule, event) ) - const filteredEvents = compliance_engine.filterEvents(events) + const filteredEvents = compliance_engine.filterEvents(filteredVehicleEvents) const count = filteredEvents.reduce((count_acc, event) => { return ( @@ -207,9 +216,14 @@ function api(app: express.Express): express.Express { ) }, 0) - return res.status(200).send({ count }) - } catch (err) { - await fail(err) + const { version } = res.locals + return res.status(200).send({ policy, count, timestamp: query_date, version }) + } catch (error) { + await logger.error(error.stack) + if (error instanceof NotFoundError) { + return res.status(404).send({ error }) + } + return res.status(500).send({ error: new ServerError('An internal server error has occurred and been logged') }) } }) return app diff --git a/packages/mds-compliance/mds-compliance-cli.ts b/packages/mds-compliance/mds-compliance-cli.ts index d649e1874..94d99c39b 100644 --- a/packages/mds-compliance/mds-compliance-cli.ts +++ b/packages/mds-compliance/mds-compliance-cli.ts @@ -14,11 +14,11 @@ limitations under the License. */ import * as fs from 'fs' -import log from '@mds-core/mds-logger' +import logger from '@mds-core/mds-logger' import * as yargs from 'yargs' import { Policy, Geography, ComplianceResponse, VehicleEvent, Device } from '@mds-core/mds-types' +import { validateEvents, validateGeographies, validatePolicies } from '@mds-core/mds-schema-validators' import { filterPolicies, processPolicy, filterEvents } from './mds-compliance-engine' -import { validateEvents, validateGeographies, validatePolicies } from './validators' const args = yargs .options('geographies', { @@ -53,20 +53,20 @@ async function readJson(path: string): Promise { async function main(): Promise<(ComplianceResponse | undefined)[]> { const geographies = (await readJson(args.geographies)) as Geography[] if (!geographies || !validateGeographies(geographies)) { - await log.error('unable to read geographies') + logger.error('unable to read geographies') process.exit(1) } const policies = (await readJson(args.policies)) as Policy[] if (!policies || !validatePolicies(policies)) { - await log.error('unable to read policies') + logger.error('unable to read policies') process.exit(1) } // read events const events = (await readJson(args.events)) as VehicleEvent[] if (!events || !validateEvents(events)) { - await log.error('unable to read events') + logger.error('unable to read events') process.exit(1) } const filteredEvents = filterEvents(events) @@ -77,7 +77,7 @@ async function main(): Promise<(ComplianceResponse | undefined)[]> { }, {}) // TODO Validate Devices if (!devices) { - await log.error('unable to read devices') + logger.error('unable to read devices') process.exit(1) } @@ -92,21 +92,21 @@ main() .then( /* eslint-disable-next-line promise/always-return */ result => { - log.info(JSON.stringify(result, undefined, 2)) + logger.info(JSON.stringify(result, undefined, 2)) }, failure => { // TODO use payload response type instead of peering into body const reason = failure.slice && failure.slice(0, 2) === '{"' ? JSON.parse(failure) : failure if (reason.error_description) { - log.info(`${reason.error_description} (${reason.error})`) + logger.info(`${reason.error_description} (${reason.error})`) } else if (reason.result) { - log.info(reason.result) + logger.info(reason.result) } else { - log.info('failure:', reason) + logger.info('failure:', reason) } } ) /* eslint-disable-next-line promise/prefer-await-to-callbacks */ .catch(async err => { - await log.error('exception:', err.stack) + logger.error('exception:', err.stack) }) diff --git a/packages/mds-compliance/mds-compliance-engine.ts b/packages/mds-compliance/mds-compliance-engine.ts index 8568be142..b8051e928 100644 --- a/packages/mds-compliance/mds-compliance-engine.ts +++ b/packages/mds-compliance/mds-compliance-engine.ts @@ -216,6 +216,9 @@ function processPolicy( devices: { [d: string]: Device } ): ComplianceResponse | undefined { if (isPolicyActive(policy)) { + const sortedEvents = events.sort((e_1, e_2) => { + return e_1.timestamp - e_2.timestamp + }) const vehiclesToFilter: MatchedVehicle[] = [] let overflowVehiclesMap: { [key: string]: MatchedVehiclePlusRule } = {} let countVehiclesMap: { [d: string]: MatchedVehiclePlusRule } = {} @@ -233,7 +236,12 @@ function processPolicy( switch (rule.rule_type) { case 'count': { - const comp: Compliance & { matches: CountMatch[] } = processCountRule(rule, events, geographies, devices) + const comp: Compliance & { matches: CountMatch[] } = processCountRule( + rule, + sortedEvents, + geographies, + devices + ) const compressedComp = { rule, @@ -315,7 +323,12 @@ function processPolicy( break } case 'time': { - const comp: Compliance & { matches: TimeMatch[] | null } = processTimeRule(rule, events, geographies, devices) + const comp: Compliance & { matches: TimeMatch[] | null } = processTimeRule( + rule, + sortedEvents, + geographies, + devices + ) compliance_acc.push(comp) const timeVehicles = comp.matches @@ -334,7 +347,7 @@ function processPolicy( { const comp: Compliance & { matches: SpeedMatch[] | null } = processSpeedRule( rule, - events, + sortedEvents, geographies, devices ) diff --git a/packages/mds-compliance/middleware/compliance-api-version.ts b/packages/mds-compliance/middleware/compliance-api-version.ts new file mode 100644 index 000000000..75b86fe4a --- /dev/null +++ b/packages/mds-compliance/middleware/compliance-api-version.ts @@ -0,0 +1,7 @@ +import { ApiVersionMiddleware } from '@mds-core/mds-api-server' +import { COMPLIANCE_API_SUPPORTED_VERSIONS, COMPLIANCE_API_DEFAULT_VERSION } from '../types' + +export const ComplianceApiVersionMiddleware = ApiVersionMiddleware( + 'application/vnd.mds.compliance+json', + COMPLIANCE_API_SUPPORTED_VERSIONS +).withDefaultVersion(COMPLIANCE_API_DEFAULT_VERSION) diff --git a/packages/mds-compliance/middleware/index.ts b/packages/mds-compliance/middleware/index.ts new file mode 100644 index 000000000..e106a20bf --- /dev/null +++ b/packages/mds-compliance/middleware/index.ts @@ -0,0 +1 @@ +export * from './compliance-api-version' diff --git a/packages/mds-compliance/package.json b/packages/mds-compliance/package.json index 10bf7dcf0..c5f4604b3 100644 --- a/packages/mds-compliance/package.json +++ b/packages/mds-compliance/package.json @@ -1,6 +1,6 @@ { "name": "@mds-core/mds-compliance", - "version": "0.1.19", + "version": "0.1.27", "description": "MDS Compliance Tool", "main": "dist/index.js", "types": "dist/index.d.ts", @@ -11,10 +11,10 @@ "build": "tsc --build tsconfig.build.json", "start": "PATH_PREFIX=/compliance yarn watch server", "test": "yarn test:eslint && yarn test:unit", - "test:eslint": "eslint --fix --ignore-path ../../.gitignore '**/*.ts'", - "test:unit": "PATH_PREFIX=/compliance DOTENV_CONFIG_PATH=../../.env nyc --check-coverage --exclude tests --extension .ts --lines 90 --reporter=text --reporter=html ts-mocha --project ../../tsconfig.json --require tsconfig-paths/register --require source-map-support/register --require dotenv/config --recursive --timeout 5000 tests/**/*.ts", - "watch": "nodemon --watch '../../packages' --ext 'ts' --ignore '*.d.ts' --exec yarn watch:exec --", - "watch:exec": "yarn build && DOTENV_CONFIG_PATH=../../.env ts-node -r dotenv/config" + "test:eslint": "eslint --ignore-path ../../.gitignore '**/*.ts'", + "test:unit": "PATH_PREFIX=/compliance DOTENV_CONFIG_PATH=../../.env nyc ts-mocha --project ../../tsconfig.json --exit", + "ts-node": "yarn build && DOTENV_CONFIG_PATH=../../.env ts-node -r dotenv/config", + "watch": "nodemon --watch '../../packages' --ext 'ts' --ignore '*.d.ts' --exec yarn ts-node --" }, "keywords": [ "mds", @@ -23,18 +23,22 @@ "author": "City of Los Angeles.", "license": "Apache-2.0", "dependencies": { - "@hapi/joi": "15.1.1", - "@mds-core/mds-api-server": "0.1.18", - "@mds-core/mds-cache": "0.1.18", - "@mds-core/mds-db": "0.1.18", - "@mds-core/mds-logger": "0.1.16", - "@mds-core/mds-stream": "0.1.18", - "@mds-core/mds-types": "0.1.15", - "@mds-core/mds-utils": "0.1.18", + "@mds-core/mds-agency-cache": "0.1.26", + "@mds-core/mds-api-helpers": "0.1.26", + "@mds-core/mds-api-server": "0.1.26", + "@mds-core/mds-db": "0.1.26", + "@mds-core/mds-logger": "0.1.24", + "@mds-core/mds-providers": "0.1.26", + "@mds-core/mds-schema-validators": "0.1.2", + "@mds-core/mds-stream": "0.1.26", + "@mds-core/mds-types": "0.1.23", + "@mds-core/mds-utils": "0.1.26", + "@types/express": "4.17.6", + "@types/moment-timezone": "0.5.13", + "@types/yargs": "15.0.5", "express": "4.17.1", "fs": "0.0.1-security", - "moment-timezone": "0.5.27", - "uuid": "3.3.3", - "yargs": "14.2.0" + "moment-timezone": "0.5.28", + "yargs": "15.3.1" } } diff --git a/packages/mds-compliance/server.ts b/packages/mds-compliance/server.ts index 78a97d070..a35fc7d56 100644 --- a/packages/mds-compliance/server.ts +++ b/packages/mds-compliance/server.ts @@ -11,14 +11,7 @@ limitations under the License. */ -// Express local -import { ApiServer } from '@mds-core/mds-api-server' +import { ApiServer, HttpServer } from '@mds-core/mds-api-server' import { api } from './api' -const { - env: { npm_package_name, PORT = 4004 } -} = process - -/* eslint-reason avoids import of logger */ -/* eslint-disable-next-line no-console */ -ApiServer(api).listen(PORT, () => console.log(`${npm_package_name} running on port ${PORT}`)) +HttpServer(ApiServer(api), { port: process.env.COMPLIANCE_API_PORT }) diff --git a/packages/mds-compliance/test_data/low_limit_policy.json b/packages/mds-compliance/test_data/low_limit_policy.json new file mode 100644 index 000000000..b4c8fc3e4 --- /dev/null +++ b/packages/mds-compliance/test_data/low_limit_policy.json @@ -0,0 +1,28 @@ +[ + { + "policy_id": "6d7a9c7e-853c-4ff7-a86f-e17c06d3bd80", + "name": "Very Low Count Limit", + "description": "Very low count limit", + "start_date": 1552678594428, + "end_date": null, + "prev_policies": null, + "rules": [ + { + "name": "Greater LA", + "rule_id": "2aa6953d-fa8f-4018-9b54-84c8b4b83c6d", + "rule_type": "count", + "geographies": [ + "1f943d59-ccc9-4d91-b6e2-0c5e771cbc49" + ], + "statuses": { + "trip": [] + }, + "vehicle_types": [ + "bicycle", + "scooter" + ], + "maximum": 5 + } + ] + } +] \ No newline at end of file diff --git a/packages/mds-compliance/test_data/policies.json b/packages/mds-compliance/test_data/policies.json index 06f16dc57..31d188e11 100644 --- a/packages/mds-compliance/test_data/policies.json +++ b/packages/mds-compliance/test_data/policies.json @@ -6,14 +6,23 @@ "start_date": 1552678594428, "end_date": null, "prev_policies": null, + "provider_ids": null, "rules": [ { "name": "Greater LA", "rule_id": "47c8c7d4-14b5-43a3-b9a5-a32ecc2fb2c6", "rule_type": "count", - "geographies": ["1f943d59-ccc9-4d91-b6e2-0c5e771cbc49"], - "statuses": { "available": [], "trip": [] }, - "vehicle_types": ["bicycle", "scooter"], + "geographies": [ + "1f943d59-ccc9-4d91-b6e2-0c5e771cbc49" + ], + "statuses": { + "available": [], + "trip": [] + }, + "vehicle_types": [ + "bicycle", + "scooter" + ], "maximum": 3000, "minimum": 500 } @@ -26,15 +35,23 @@ "start_date": 1552678594428, "end_date": null, "prev_policies": null, + "provider_ids": null, "rules": [ { "name": "Greater LA (rentable)", "rule_id": "5e18fcdf-8847-4842-bc83-c2ad76fb7b10", "rule_type": "time", "rule_units": "minutes", - "geographies": ["1f943d59-ccc9-4d91-b6e2-0c5e771cbc49"], - "statuses": { "available": [] }, - "vehicle_types": ["bicycle", "scooter"], + "geographies": [ + "1f943d59-ccc9-4d91-b6e2-0c5e771cbc49" + ], + "statuses": { + "available": [] + }, + "vehicle_types": [ + "bicycle", + "scooter" + ], "maximum": 7200 } ] @@ -46,17 +63,25 @@ "start_date": 1552678594428, "end_date": null, "prev_policies": null, + "provider_ids": null, "rules": [ { "name": "Greater LA", "rule_id": "2aa6953d-fa8f-4018-9b54-84c8b4b83c6d", "rule_type": "speed", "rule_units": "mph", - "geographies": ["1f943d59-ccc9-4d91-b6e2-0c5e771cbc49"], - "statuses": { "trip": [] }, - "vehicle_types": ["bicycle", "scooter"], + "geographies": [ + "1f943d59-ccc9-4d91-b6e2-0c5e771cbc49" + ], + "statuses": { + "trip": [] + }, + "vehicle_types": [ + "bicycle", + "scooter" + ], "maximum": 15 } ] } -] +] \ No newline at end of file diff --git a/packages/mds-compliance/tests/api.spec.ts b/packages/mds-compliance/tests/api.spec.ts index 3da856768..fcdf6c78c 100644 --- a/packages/mds-compliance/tests/api.spec.ts +++ b/packages/mds-compliance/tests/api.spec.ts @@ -5,15 +5,22 @@ /* eslint-disable promise/catch-or-return */ /* eslint-disable promise/prefer-await-to-callbacks */ /* eslint-disable @typescript-eslint/no-floating-promises */ -import { makeDevices, makeEventsWithTelemetry, makeTelemetryInArea } from '@mds-core/mds-test-data' - +import { + makeDevices, + makeEventsWithTelemetry, + makeTelemetryInArea, + restrictedAreas, + veniceSpecOps, + LA_CITY_BOUNDARY, + START_ONE_MONTH_AGO +} from '@mds-core/mds-test-data' import test from 'unit.js' import { api as agency } from '@mds-core/mds-agency' -import cache from '@mds-core/mds-cache' +import cache from '@mds-core/mds-agency-cache' import db from '@mds-core/mds-db' import stream from '@mds-core/mds-stream' import supertest from 'supertest' -import { now } from '@mds-core/mds-utils' +import { now, uuid } from '@mds-core/mds-utils' import { Telemetry, Device, @@ -27,10 +34,8 @@ import { } from '@mds-core/mds-types' import MockDate from 'mockdate' import { Feature, Polygon } from 'geojson' -import uuid from 'uuid' import { ApiServer } from '@mds-core/mds-api-server' import { TEST1_PROVIDER_ID, TEST2_PROVIDER_ID, MOCHA_PROVIDER_ID } from '@mds-core/mds-providers' -import { la_city_boundary } from './la-city-boundary' import { api } from '../api' const request = supertest(ApiServer(api)) @@ -44,11 +49,6 @@ const DEVICE_UUID = 'ec551174-f324-4251-bfed-28d9f3f473fc' const CITY_OF_LA = '1f943d59-ccc9-4d91-b6e2-0c5e771cbc49' const LA_BEACH = 'ff822e26-a70c-4721-ac32-2f6734beff9b' -/* eslint-disable @typescript-eslint/no-var-requires */ -const restrictedAreas = require('../../ladot-service-areas/restricted-areas') -const veniceSpecialOpsZone = require('../../ladot-service-areas/venice-special-ops-zone') -/* eslint-enable @typescript-eslint/no-var-requires */ - let testTimestamp = now() const TEST_TELEMETRY = { @@ -89,6 +89,7 @@ const COUNT_POLICY_JSON: Policy = { description: 'Mobility caps as described in the One-Year Permit', policy_id: COUNT_POLICY_UUID, start_date: 1558389669540, + publish_date: 1558389669540, end_date: null, prev_policies: null, provider_ids: [], @@ -133,6 +134,7 @@ const COUNT_POLICY_JSON_2: Policy = { policy_id: COUNT_POLICY_UUID_2, start_date: 1558389669540, end_date: null, + publish_date: 1558389669540, prev_policies: null, provider_ids: [], rules: [ @@ -174,6 +176,7 @@ const COUNT_POLICY_JSON_4: Policy = { name: 'LADOT Mobility Caps', description: 'Mobility caps as described in the One-Year Permit', policy_id: COUNT_POLICY_UUID_4, + publish_date: 1558389669540, start_date: 1558389669540, end_date: null, prev_policies: null, @@ -210,7 +213,7 @@ const COUNT_POLICY_JSON_5: Policy = { ], end_date: null, policy_id: '25851571-b53f-4426-a033-f375be0e7957', - start_date: 1552678594428, + start_date: Date.now(), description: 'Prohibited areas for dockless vehicles within the City of Los Angeles for the LADOT Dockless On-Demand Personal Mobility Program', prev_policies: null @@ -240,7 +243,7 @@ const TIME_POLICY_JSON: Policy = { ] } -const APP_JSON = 'application/json; charset=utf-8' +const APP_JSON = 'application/vnd.mds.compliance+json; charset=utf-8; version=0.1' describe('Tests Compliance API:', () => { afterEach(async () => { await Promise.all([db.shutdown(), cache.shutdown(), stream.shutdown()]) @@ -248,7 +251,7 @@ describe('Tests Compliance API:', () => { describe('Singular Policy API Sanity Checks: ', () => { beforeEach(done => { - Promise.all([db.initialize(), cache.initialize(), stream.initialize()]).then(() => { + Promise.all([db.initialize(), cache.initialize()]).then(() => { agency_request .post('/vehicles') .set('Authorization', ADMIN_AUTH) @@ -266,10 +269,10 @@ describe('Tests Compliance API:', () => { }) .expect(201) .end(async () => { - const geography = { name: 'la', geography_id: GEOGRAPHY_UUID, geography_json: la_city_boundary } + const geography = { name: 'la', geography_id: GEOGRAPHY_UUID, geography_json: LA_CITY_BOUNDARY } await db.writeGeography(geography) await db.writePolicy(COUNT_POLICY_JSON) - await db.publishPolicy(COUNT_POLICY_UUID) + await db.publishGeography({ geography_id: geography.geography_id }) done() }) }) @@ -323,12 +326,12 @@ describe('Tests Compliance API:', () => { telemetry.push(makeTelemetryInArea(device, now(), CITY_OF_LA, 10)) }) const seedData = { devices, events, telemetry } - Promise.all([db.initialize(), cache.initialize(), stream.initialize()]).then(() => { + Promise.all([db.initialize(), cache.initialize()]).then(() => { Promise.all([db.seed(seedData), cache.seed(seedData)]).then(async () => { - const geography = { name: 'la', geography_id: GEOGRAPHY_UUID, geography_json: la_city_boundary } + const geography = { name: 'la', geography_id: GEOGRAPHY_UUID, geography_json: LA_CITY_BOUNDARY } await db.writeGeography(geography) + await db.publishGeography({ geography_id: geography.geography_id }) await db.writePolicy(COUNT_POLICY_JSON) - await db.publishPolicy(COUNT_POLICY_UUID) done() }) }) @@ -341,6 +344,8 @@ describe('Tests Compliance API:', () => { .expect(200) .end((err, result) => { test.assert.deepEqual(result.body.total_violations, 0) + test.object(result.body).hasProperty('timestamp') + test.value(result.body.policy, COUNT_POLICY_UUID) test.value(result).hasHeader('content-type', APP_JSON) done(err) }) @@ -372,7 +377,7 @@ describe('Tests Compliance API:', () => { // .expect(201) // .end((err, result) => { // test.value(result).hasHeader('content-type', APP_JSON) - // const geography = { geography_id: GEOGRAPHY_UUID, geography_json: la_city_boundary } + // const geography = { geography_id: GEOGRAPHY_UUID, geography_json: LA_CITY_BOUNDARY } // policy_request // .post(`/admin/geographies/${GEOGRAPHY_UUID}`) // .set('Authorization', ADMIN_AUTH) @@ -425,12 +430,12 @@ describe('Tests Compliance API:', () => { telemetry.push(makeTelemetryInArea(device, now(), CITY_OF_LA, 10)) }) const seedData = { devices, events, telemetry } - Promise.all([db.initialize(), cache.initialize(), stream.initialize()]).then(() => { + Promise.all([db.initialize(), cache.initialize()]).then(() => { Promise.all([db.seed(seedData), cache.seed(seedData)]).then(async () => { - const geography = { name: 'la', geography_id: GEOGRAPHY_UUID, geography_json: la_city_boundary } + const geography = { name: 'la', geography_id: GEOGRAPHY_UUID, geography_json: LA_CITY_BOUNDARY } await db.writeGeography(geography) + await db.publishGeography({ geography_id: geography.geography_id }) await db.writePolicy(COUNT_POLICY_JSON) - await db.publishPolicy(COUNT_POLICY_UUID) done() }) }) @@ -444,6 +449,8 @@ describe('Tests Compliance API:', () => { .end((err, result) => { test.assert.deepEqual(result.body.compliance[0].matches[0].measured, 10) test.assert.deepEqual(result.body.total_violations, 5) + test.object(result.body).hasProperty('timestamp') + test.object(result.body).hasProperty('version') test.value(result).hasHeader('content-type', APP_JSON) done(err) }) @@ -463,10 +470,11 @@ describe('Tests Compliance API:', () => { telemetry.push(makeTelemetryInArea(device, now(), CITY_OF_LA, 10)) }) const seedData = { devices, events, telemetry } - Promise.all([db.initialize(), cache.initialize(), stream.initialize()]).then(() => { + Promise.all([db.initialize(), cache.initialize()]).then(() => { Promise.all([db.seed(seedData), cache.seed(seedData)]).then(async () => { - const geography = { name: 'la', geography_id: GEOGRAPHY_UUID, geography_json: la_city_boundary } + const geography = { name: 'la', geography_id: GEOGRAPHY_UUID, geography_json: LA_CITY_BOUNDARY } await db.writeGeography(geography) + await db.publishGeography({ geography_id: geography.geography_id }) await db.writePolicy(TIME_POLICY_JSON) await db.publishPolicy(TIME_POLICY_UUID) done() @@ -500,10 +508,11 @@ describe('Tests Compliance API:', () => { telemetry.push(makeTelemetryInArea(device, now(), CITY_OF_LA, 10)) }) const seedData = { devices, events, telemetry } - Promise.all([db.initialize(), cache.initialize(), stream.initialize()]).then(() => { + Promise.all([db.initialize(), cache.initialize()]).then(() => { Promise.all([db.seed(seedData), cache.seed(seedData)]).then(async () => { - const geography = { name: 'la', geography_id: GEOGRAPHY_UUID, geography_json: la_city_boundary } + const geography = { name: 'la', geography_id: GEOGRAPHY_UUID, geography_json: LA_CITY_BOUNDARY } await db.writeGeography(geography) + await db.publishGeography({ geography_id: geography.geography_id }) await db.writePolicy(TIME_POLICY_JSON) await db.publishPolicy(TIME_POLICY_UUID) done() @@ -530,7 +539,7 @@ describe('Tests Compliance API:', () => { describe('Attempts to check compliance for non-existent policy', () => { it('Checks for a valid UUID, but not present in db', done => { - Promise.all([db.initialize(), cache.initialize(), stream.initialize()]).then(async () => { + Promise.all([db.initialize(), cache.initialize()]).then(async () => { request .get(`/snapshot/${TIME_POLICY_UUID}`) .set('Authorization', ADMIN_AUTH) @@ -551,12 +560,12 @@ describe('Tests Compliance API:', () => { telemetry.push(makeTelemetryInArea(device, now(), LA_BEACH, 10)) }) const seedData = { devices, events, telemetry } - Promise.all([db.initialize(), cache.initialize(), stream.initialize()]).then(() => { + Promise.all([db.initialize(), cache.initialize()]).then(() => { Promise.all([db.seed(seedData), cache.seed(seedData)]).then(async () => { const geography = { name: 'la beach', geography_id: LA_BEACH, geography_json: restrictedAreas } await db.writeGeography(geography) + await db.publishGeography({ geography_id: geography.geography_id }) await db.writePolicy(COUNT_POLICY_JSON_2) - await db.publishPolicy(COUNT_POLICY_UUID_2) done() }) }) @@ -603,10 +612,11 @@ describe('Tests Compliance API:', () => { telemetry.push(makeTelemetryInArea(device, now(), CITY_OF_LA, 10)) }) const seedData = { devices, events, telemetry } - Promise.all([db.initialize(), cache.initialize(), stream.initialize()]).then(() => { + Promise.all([db.initialize(), cache.initialize()]).then(() => { Promise.all([db.seed(seedData), cache.seed(seedData)]).then(async () => { - const geography = { name: 'la', geography_id: GEOGRAPHY_UUID, geography_json: la_city_boundary } + const geography = { name: 'la', geography_id: GEOGRAPHY_UUID, geography_json: LA_CITY_BOUNDARY } await db.writeGeography(geography) + await db.publishGeography({ geography_id: geography.geography_id }) await db.writePolicy(COUNT_POLICY_JSON_3) await db.publishPolicy(COUNT_POLICY_UUID_3) done() @@ -641,10 +651,11 @@ describe('Tests Compliance API:', () => { telemetry.push(makeTelemetryInArea(device, now(), CITY_OF_LA, 10)) }) const seedData = { devices, events, telemetry } - Promise.all([db.initialize(), cache.initialize(), stream.initialize()]).then(() => { + Promise.all([db.initialize(), cache.initialize()]).then(() => { Promise.all([db.seed(seedData), cache.seed(seedData)]).then(async () => { - const geography = { name: 'LA', geography_id: GEOGRAPHY_UUID, geography_json: la_city_boundary } + const geography = { name: 'LA', geography_id: GEOGRAPHY_UUID, geography_json: LA_CITY_BOUNDARY } await db.writeGeography(geography) + await db.publishGeography({ geography_id: geography.geography_id }) await db.writePolicy(COUNT_POLICY_JSON_3) await db.publishPolicy(COUNT_POLICY_UUID_3) done() @@ -672,7 +683,7 @@ describe('Tests Compliance API:', () => { describe('Verifies venice beach spec ops', () => { before(done => { const veniceSpecOpsPointIds: UUID[] = [] - const geographies: Geography[] = veniceSpecialOpsZone.features.map((feature: Feature) => { + const geographies = (veniceSpecOps.features.map((feature: Feature) => { if (feature.geometry.type === 'Point') { const geography_id = uuid() veniceSpecOpsPointIds.push(geography_id) @@ -685,7 +696,7 @@ describe('Tests Compliance API:', () => { geography_id: 'e0e4a085-7a50-43e0-afa4-6792ca897c5a', geography_json: feature.geometry } - }) + }) as unknown) as Geography[] const VENICE_SPEC_OPS_POLICY: Policy = { name: 'Venice Special Operations Zone', @@ -731,7 +742,7 @@ describe('Tests Compliance API:', () => { const devices_a: Device[] = makeDevices(22, now()) let iter = 0 - const events_a: VehicleEvent[] = veniceSpecialOpsZone.features.reduce((acc: VehicleEvent[], feature: Feature) => { + const events_a: VehicleEvent[] = veniceSpecOps.features.reduce((acc: VehicleEvent[], feature: Feature) => { if (feature.geometry.type === 'Point') { acc.push(...makeEventsWithTelemetry([devices_a[iter++]], now() - 10, feature.geometry, 'provider_drop_off')) } @@ -745,7 +756,7 @@ describe('Tests Compliance API:', () => { TEST_ZONE_NO_VALID_DROP_OFF_POINTS, 'provider_drop_off' ) - Promise.all([db.initialize(), cache.initialize(), stream.initialize()]).then(async () => { + Promise.all([db.initialize(), cache.initialize()]).then(async () => { const seedData: { devices: Device[]; events: VehicleEvent[]; telemetry: Telemetry[] } = { devices: [...devices_a, ...devices_b], events: [...events_a, ...events_b], @@ -754,6 +765,9 @@ describe('Tests Compliance API:', () => { await Promise.all([db.initialize(), cache.initialize()]) await Promise.all([cache.seed(seedData), db.seed(seedData)]) await Promise.all(geographies.map((geography: Geography) => db.writeGeography(geography))) + await Promise.all( + geographies.map((geography: Geography) => db.publishGeography({ geography_id: geography.geography_id })) + ) await db.writePolicy(VENICE_SPEC_OPS_POLICY) await db.publishPolicy(VENICE_SPEC_OPS_POLICY.policy_id) done() @@ -799,21 +813,18 @@ describe('Tests Compliance API:', () => { telemetry: [...telemetry_a, ...telemetry_b] } Promise.all([db.initialize(), cache.initialize()]).then(() => { - Promise.all([cache.seed(seedData), db.seed(seedData)]).then(() => { - db.writeGeography({ name: 'la', geography_id: GEOGRAPHY_UUID, geography_json: la_city_boundary }).then(() => { - db.writePolicy(COUNT_POLICY_JSON_4).then(() => { - db.publishPolicy(COUNT_POLICY_JSON_4.policy_id).then(() => { - done() - }) - }) - }) + Promise.all([cache.seed(seedData), db.seed(seedData)]).then(async () => { + await db.writeGeography({ name: 'la', geography_id: GEOGRAPHY_UUID, geography_json: LA_CITY_BOUNDARY }) + await db.publishGeography({ geography_id: GEOGRAPHY_UUID }) + await db.writePolicy(COUNT_POLICY_JSON_4) + done() }) }) }) it('Historical check reports 5 violations', done => { request - .get(`/snapshot/${COUNT_POLICY_UUID_4}?end_date=${yesterday + 200}`) + .get(`/snapshot/${COUNT_POLICY_UUID_4}?timestamp=${yesterday + 200}`) .set('Authorization', ADMIN_AUTH) .expect(200) .end((err, result) => { @@ -837,7 +848,7 @@ describe('Tests Compliance API:', () => { }) describe('Tests count endpoint', () => { - before(done => { + before(async () => { const devices_a: Device[] = makeDevices(15, now()) const events_a = makeEventsWithTelemetry(devices_a, now(), CITY_OF_LA, 'trip_start') const telemetry_a: Telemetry[] = devices_a.reduce((acc: Telemetry[], device) => { @@ -856,26 +867,35 @@ describe('Tests Compliance API:', () => { events: [...events_a, ...events_b], telemetry: [...telemetry_a, ...telemetry_b] } - Promise.all([db.initialize(), cache.initialize()]).then(() => { - Promise.all([cache.seed(seedData), db.seed(seedData)]).then(() => { - db.writePolicy(COUNT_POLICY_JSON).then(() => { - db.writeGeography({ name: 'la', geography_id: GEOGRAPHY_UUID, geography_json: la_city_boundary }).then( - () => { - done() - } - ) - }) - }) - }) + await db.initialize() + await cache.initialize() + await cache.seed(seedData) + await db.seed(seedData) + await db.writeGeography({ name: 'la', geography_id: GEOGRAPHY_UUID, geography_json: LA_CITY_BOUNDARY }) + await db.publishGeography({ geography_id: GEOGRAPHY_UUID }) + await db.writePolicy(COUNT_POLICY_JSON) }) - it('Test count endpoint success', done => { + it('Test count endpoint, expecting events', done => { request .get(`/count/47c8c7d4-14b5-43a3-b9a5-a32ecc2fb2c6`) .set('Authorization', ADMIN_AUTH) .expect(200) .end((err, result) => { test.assert.deepEqual(result.body.count, 30) + test.object(result.body).hasProperty('policy') + test.value(result).hasHeader('content-type', APP_JSON) + done(err) + }) + }) + + it('Test count endpoint, expecting no events', done => { + request + .get(`/count/47c8c7d4-14b5-43a3-b9a5-a32ecc2fb2c6?timestamp=${START_ONE_MONTH_AGO}`) + .set('Authorization', ADMIN_AUTH) + .expect(200) + .end((err, result) => { + test.assert.deepEqual(result.body.count, 0) test.value(result).hasHeader('content-type', APP_JSON) done(err) }) @@ -905,8 +925,9 @@ describe('Tests Compliance API:', () => { await Promise.all([db.initialize(), cache.initialize()]) await Promise.all([cache.seed(seedData), db.seed(seedData)]) - const geography = { name: 'la', geography_id: GEOGRAPHY_UUID, geography_json: la_city_boundary } + const geography = { name: 'la', geography_id: GEOGRAPHY_UUID, geography_json: LA_CITY_BOUNDARY } await db.writeGeography(geography) + await db.publishGeography({ geography_id: geography.geography_id }) await db.writePolicy(SCOPED_COUNT_POLICY_JSON) await db.publishPolicy(SCOPED_COUNT_POLICY_JSON.policy_id) }) @@ -955,6 +976,7 @@ describe('Tests Compliance API:', () => { await Promise.all([db.initialize(), cache.initialize()]) await Promise.all([cache.seed(seedData), db.seed(seedData)]) await db.writeGeography(geography) + await db.publishGeography({ geography_id: geography.geography_id }) await db.writePolicy(COUNT_POLICY_JSON_5) await db.publishPolicy(COUNT_POLICY_JSON_5.policy_id) }) diff --git a/packages/mds-compliance/tests/engine-test.spec.ts b/packages/mds-compliance/tests/engine-test.spec.ts index d40d57239..c56c06bfd 100644 --- a/packages/mds-compliance/tests/engine-test.spec.ts +++ b/packages/mds-compliance/tests/engine-test.spec.ts @@ -2,15 +2,16 @@ import test from 'unit.js' import fs from 'fs' import { makeDevices, makeEventsWithTelemetry } from '@mds-core/mds-test-data' -import { RULE_TYPES, Geography, Policy, Device } from '@mds-core/mds-types' +import { RULE_TYPES, Geography, Policy, Device, VehicleEvent } from '@mds-core/mds-types' import { la_city_boundary } from '@mds-core/mds-policy/tests/la-city-boundary' import { FeatureCollection } from 'geojson' import { processPolicy, filterPolicies, filterEvents } from '@mds-core/mds-compliance/mds-compliance-engine' -import { ValidationError, RuntimeError } from '@mds-core/mds-utils' -import { validateEvents, validateGeographies, validatePolicies } from '../validators' +import { RuntimeError } from '@mds-core/mds-utils' +import { ValidationError, validateEvents, validateGeographies, validatePolicies } from '@mds-core/mds-schema-validators' let policies: Policy[] = [] +let low_count_policies: Policy[] = [] const CITY_OF_LA = '1f943d59-ccc9-4d91-b6e2-0c5e771cbc49' @@ -28,6 +29,12 @@ async function readJson(path: string): Promise { return Promise.resolve(JSON.parse(fs.readFileSync(path).toString())) } +function getDeviceMap(devices: Device[]): { [d: string]: Device } { + return devices.reduce((deviceMapAcc: { [d: string]: Device }, device: Device) => { + return Object.assign(deviceMapAcc, { [device.device_id]: device }) + }, {}) +} + describe('Tests Compliance Engine', () => { before(async () => { policies = await readJson('test_data/policies.json') @@ -318,6 +325,7 @@ describe('Verifies errors are being properly thrown', () => { }) it('Verifies RuntimeErrors are being thrown with an invalid TIMEZONE env_var', done => { + const oldTimezone = process.env.TIMEZONE process.env.TIMEZONE = 'Pluto/Potato_Land' const devices = makeDevices(1, now()) const events = makeEventsWithTelemetry(devices, now(), CITY_OF_LA, 'trip_end') @@ -337,6 +345,45 @@ describe('Verifies errors are being properly thrown', () => { () => filteredPolicies.map(policy => processPolicy(policy, filteredEvents, geographies, deviceMap)), RuntimeError ) + process.env.TIMEZONE = oldTimezone + done() + }) +}) + +describe('Verifies compliance engine processes by vehicle most recent event', async () => { + before(async () => { + low_count_policies = await readJson('test_data/low_limit_policy.json') + // geographies = await readJson('test_data/geographies.json') + }) + + it('should process count violation vehicles with the most recent event last', done => { + test.assert.doesNotThrow(() => validatePolicies(low_count_policies)) + const devices = makeDevices(6, now()) + const start_time = now() - 10000000 + const latest_device: Device = devices[0] + const events: VehicleEvent[] = devices.reduce((events_acc: VehicleEvent[], device: Device, current_index) => { + const device_events = makeEventsWithTelemetry([device], start_time - current_index * 10, CITY_OF_LA, 'trip_start') + events_acc.push(...device_events) + return events_acc + }, []) + const deviceMap = getDeviceMap(devices) + const results = low_count_policies.map(policy => processPolicy(policy, events, geographies, deviceMap)) + results.forEach(result => { + if (result) { + result.compliance.forEach(compliance => { + // It's not necessary to verify it works for the other rule types since the sorting happens before + // any policy processing happens. + if ( + compliance.rule.geographies.includes(CITY_OF_LA) && + compliance.matches && + compliance.rule.rule_type === RULE_TYPES.count + ) { + test.assert.deepEqual(compliance.matches.length, 1) + test.assert.deepEqual(result.vehicles_in_violation[0].device_id, latest_device.device_id) + } + }) + } + }) done() }) }) diff --git a/packages/mds-compliance/tsconfig.build.json b/packages/mds-compliance/tsconfig.build.json index 070faffca..9375fd5a5 100644 --- a/packages/mds-compliance/tsconfig.build.json +++ b/packages/mds-compliance/tsconfig.build.json @@ -5,12 +5,14 @@ }, "references": [ { "path": "../../packages/mds-agency/tsconfig.build.json" }, + { "path": "../../packages/mds-api-helpers/tsconfig.build.json" }, { "path": "../../packages/mds-api-server/tsconfig.build.json" }, - { "path": "../../packages/mds-cache/tsconfig.build.json" }, + { "path": "../../packages/mds-agency-cache/tsconfig.build.json" }, { "path": "../../packages/mds-db/tsconfig.build.json" }, { "path": "../../packages/mds-logger/tsconfig.build.json" }, { "path": "../../packages/mds-policy/tsconfig.build.json" }, { "path": "../../packages/mds-providers/tsconfig.build.json" }, + { "path": "../../packages/mds-schema-validators/tsconfig.build.json" }, { "path": "../../packages/mds-stream/tsconfig.build.json" }, { "path": "../../packages/mds-test-data/tsconfig.build.json" }, { "path": "../../packages/mds-types/tsconfig.build.json" }, diff --git a/packages/mds-compliance/tsconfig.eslint.json b/packages/mds-compliance/tsconfig.eslint.json new file mode 100644 index 000000000..2a400942a --- /dev/null +++ b/packages/mds-compliance/tsconfig.eslint.json @@ -0,0 +1 @@ +{ "extends": "../../tsconfig.eslint.json" } diff --git a/packages/mds-compliance/types.ts b/packages/mds-compliance/types.ts index 7143d4e7e..dbb391b3c 100644 --- a/packages/mds-compliance/types.ts +++ b/packages/mds-compliance/types.ts @@ -1,12 +1,30 @@ -import { MatchedVehicle, VehicleEvent, UUID } from '@mds-core/mds-types' -import { ApiRequest, ApiResponse, ApiResponseLocals } from '@mds-core/mds-api-server' +import { MatchedVehicle, VehicleEvent, UUID, Timestamp, ComplianceResponse, Policy } from '@mds-core/mds-types' +import { ApiRequest, ApiClaims, ApiVersionedResponse } from '@mds-core/mds-api-server' + +export const COMPLIANCE_API_SUPPORTED_VERSIONS = ['0.1.0'] as const +export type COMPLIANCE_API_SUPPORTED_VERSION = typeof COMPLIANCE_API_SUPPORTED_VERSIONS[number] +export const [COMPLIANCE_API_DEFAULT_VERSION] = COMPLIANCE_API_SUPPORTED_VERSIONS export type ComplianceApiRequest = ApiRequest -export interface ComplianceApiResponse extends ApiResponse { - locals: ApiResponseLocals & { + +export type ComplianceApiAccessTokenScopes = never + +export type ComplianceApiResponse = ApiVersionedResponse< + COMPLIANCE_API_SUPPORTED_VERSION, + ApiClaims & { provider_id: UUID - } -} + }, + TBody +> + +type ComplianceSnapshotResponse = ComplianceResponse & { timestamp: Timestamp } + +export type ComplianceSnapshotApiResponse = ComplianceApiResponse +export type ComplianceCountApiResponse = ComplianceApiResponse<{ + policy: Policy + count: number + timestamp: Timestamp +}> export type MatchedVehiclePlusRule = MatchedVehicle & { rule_id: UUID } diff --git a/packages/mds-compliance/validators.ts b/packages/mds-compliance/validators.ts deleted file mode 100644 index 358e36c34..000000000 --- a/packages/mds-compliance/validators.ts +++ /dev/null @@ -1,156 +0,0 @@ -import * as Joi from '@hapi/joi' - -import { VEHICLE_EVENTS, VEHICLE_TYPES, Policy, Geography, VehicleEvent } from '@mds-core/mds-types' -import { ValidationError } from '@mds-core/mds-utils' - -const DAYS_OF_WEEK = ['sun', 'mon', 'tue', 'wed', 'thu', 'fri', 'sat'] - -const ruleSchema = Joi.object().keys({ - name: Joi.string().required(), - rule_id: Joi.string() - .guid() - .required(), - rule_type: Joi.string() - .valid(['count', 'time', 'speed', 'user']) - .required(), - rule_units: Joi.string().valid(['seconds', 'minutes', 'hours', 'mph', 'kph']), - geographies: Joi.array().items(Joi.string().guid()), - statuses: Joi.object().keys({ - available: Joi.array(), - reserved: Joi.array(), - unavailable: Joi.array(), - removed: Joi.array(), - inactive: Joi.array(), - trip: Joi.array(), - elsewhere: Joi.array() - }), - vehicle_types: Joi.array().items(Joi.string().valid(Object.values(VEHICLE_TYPES))), - maximum: Joi.number(), - minimum: Joi.number(), - start_time: Joi.string(), - end_time: Joi.string(), - days: Joi.array().items(Joi.string().valid(DAYS_OF_WEEK)), - messages: Joi.object(), - value_url: Joi.string().uri() -}) - -const policiesSchema = Joi.array().items( - Joi.object().keys({ - name: Joi.string().required(), - description: Joi.string().required(), - policy_id: Joi.string() - .guid() - .required(), - start_date: Joi.date() - .timestamp('javascript') - .required(), - end_date: Joi.date() - .timestamp('javascript') - .allow(null), - prev_policies: Joi.array() - .items(Joi.string().guid()) - .allow(null), - rules: Joi.array() - .min(1) - .items(ruleSchema) - .required() - }) -) - -const featureSchema = Joi.object() - .keys({ - type: Joi.string() - .valid(['Feature']) - .required(), - properties: Joi.object().required(), - geometry: Joi.object().required() - }) - .unknown(true) // TODO - -const featureCollectionSchema = Joi.object() - .keys({ - type: Joi.string() - .valid(['FeatureCollection']) - .required(), - features: Joi.array() - .min(1) - .items(featureSchema) - .required() - }) - .unknown(true) // TODO - -const geographiesSchema = Joi.array().items( - Joi.object() - .keys({ - geography_id: Joi.string() - .guid() - .required(), - geography_json: featureCollectionSchema, - read_only: Joi.boolean().allow(null), - previous_geography_ids: Joi.array() - .items(Joi.string().guid()) - .allow(null), - name: Joi.string().required() - }) - .unknown(true) -) - -const eventsSchema = Joi.array().items( - Joi.object().keys({ - device_id: Joi.string() - .guid() - .required(), - provider_id: Joi.string() - .guid() - .required(), - timestamp: Joi.date() - .timestamp('javascript') - .required(), - event_type: Joi.string().valid(Object.values(VEHICLE_EVENTS)), - event_type_reason: Joi.string(), - telemetry_timestamp: Joi.date().timestamp('javascript'), - telemetry: Joi.object(), // TODO Add telemetry schema - trip_id: Joi.string().guid(), - service_area_id: Joi.string().guid(), - recorded: Joi.date().timestamp('javascript') - }) -) - -const Format = (property: string, error: Joi.ValidationError): string => { - const [{ message, path }] = error.details - const [, ...details] = message.split(' ') - return `${[property, ...path].join('.')} ${details.join(' ')}` -} - -export function validatePolicies(policies: unknown): policies is Policy[] { - const { error } = Joi.validate(policies, policiesSchema) - if (error) { - throw new ValidationError('invalid_policies', { - policies, - details: Format('policies', error) - }) - } - return true -} - -export function validateGeographies(geographies: unknown): geographies is Geography[] { - const { error } = Joi.validate(geographies, geographiesSchema) - if (error) { - throw new ValidationError('invalid_geographies', { - geographies, - details: Format('geographies', error) - }) - } - return true -} - -export function validateEvents(events: unknown): events is VehicleEvent[] { - const { error } = Joi.validate(events, eventsSchema) - if (error) { - throw new ValidationError('invalid events', { - events, - details: Format('events', error) - }) - } - return true -} diff --git a/packages/mds-daily/api.ts b/packages/mds-daily/api.ts index 4d0dec693..6f8e4fd78 100644 --- a/packages/mds-daily/api.ts +++ b/packages/mds-daily/api.ts @@ -16,17 +16,24 @@ import express from 'express' -import log from '@mds-core/mds-logger' -import cache from '@mds-core/mds-cache' +import logger from '@mds-core/mds-logger' +import cache from '@mds-core/mds-agency-cache' import { providerName, isProviderId } from '@mds-core/mds-providers' import { isUUID, pathsFor, now } from '@mds-core/mds-utils' -import { checkAccess } from '@mds-core/mds-api-server' -import { DailyApiRequest, DailyApiResponse } from './types' +import { checkAccess, AccessTokenScopeValidator } from '@mds-core/mds-api-server' +import { DailyApiRequest, DailyApiResponse, DailyApiAccessTokenScopes } from './types' import { getRawTripData, getVehicleCounts, getLastDayTripsByProvider, - getLastDayStatsByProvider + getLastDayStatsByProvider, + getTimeSinceLastEventHandler, + getNumVehiclesRegisteredLast24HoursHandler, + getNumEventsLast24HoursHandler, + getTripCountsSinceHandler, + getEventCountsPerProviderSinceHandler, + getTelemetryCountsPerProviderSinceHandler, + getConformanceLast24HoursHandler } from './request-handlers' async function agencyMiddleware(req: DailyApiRequest, res: DailyApiResponse, next: Function) { @@ -47,7 +54,7 @@ async function agencyMiddleware(req: DailyApiRequest, res: DailyApiResponse, nex if (provider_id) { if (!isUUID(provider_id)) { - await log.warn(req.originalUrl, 'bogus provider_id', provider_id) + logger.warn(req.originalUrl, 'bogus provider_id', provider_id) return res.status(400).send({ result: `invalid provider_id ${provider_id} is not a UUID` }) @@ -59,7 +66,7 @@ async function agencyMiddleware(req: DailyApiRequest, res: DailyApiResponse, nex }) } - log.info(providerName(provider_id), req.method, req.originalUrl) + logger.info(providerName(provider_id), req.method, req.originalUrl) } } else { return res.status(401).send('Unauthorized') @@ -67,11 +74,13 @@ async function agencyMiddleware(req: DailyApiRequest, res: DailyApiResponse, nex } } catch (err) { /* istanbul ignore next */ - await log.error(req.originalUrl, 'request validation fail:', err.stack) + logger.error(req.originalUrl, 'request validation fail:', err.stack) } next() } +const checkDailyApiAccess = (validator: AccessTokenScopeValidator) => checkAccess(validator) + function api(app: express.Express): express.Express { /** * Agency-specific middleware to extract provider_id into locals, do some logging, etc. @@ -82,18 +91,22 @@ function api(app: express.Express): express.Express { // ///////////////////// begin daily endpoints /////////////////////// - app.get(pathsFor('/admin/vehicle_counts'), checkAccess(scopes => scopes.includes('admin:all')), getVehicleCounts) + app.get( + pathsFor('/admin/vehicle_counts'), + checkDailyApiAccess(scopes => scopes.includes('admin:all')), + getVehicleCounts + ) // read all the latest events out of the cache app.get( pathsFor('/admin/events'), - checkAccess(scopes => scopes.includes('admin:all')), + checkDailyApiAccess(scopes => scopes.includes('admin:all')), async (req: DailyApiRequest, res: DailyApiResponse) => { const start = now() const events = await cache.readAllEvents() const finish = now() const timeElapsed = finish - start - await log.info(`MDS-DAILY /admin/events -> cache.readAllEvents() time elapsed: ${timeElapsed}`) + logger.info(`MDS-DAILY /admin/events -> cache.readAllEvents() time elapsed: ${timeElapsed}`) res.status(200).send({ events }) @@ -102,14 +115,14 @@ function api(app: express.Express): express.Express { app.get( pathsFor('/admin/last_day_trips_by_provider'), - checkAccess(scopes => scopes.includes('admin:all')), + checkDailyApiAccess(scopes => scopes.includes('admin:all')), getLastDayTripsByProvider ) // get raw trip data for analysis app.get( pathsFor('/admin/raw_trip_data/:trip_id'), - checkAccess(scopes => scopes.includes('admin:all')), + checkDailyApiAccess(scopes => scopes.includes('admin:all')), getRawTripData ) @@ -121,10 +134,52 @@ function api(app: express.Express): express.Express { // This function is ludicrously long as it is. app.get( pathsFor('/admin/last_day_stats_by_provider'), - checkAccess(scopes => scopes.includes('admin:all')), + checkDailyApiAccess(scopes => scopes.includes('admin:all')), getLastDayStatsByProvider ) + app.get( + pathsFor('/admin/time_since_last_event'), + checkDailyApiAccess(scopes => scopes.includes('admin:all')), + getTimeSinceLastEventHandler + ) + + app.get( + pathsFor('/admin/num_vehicles_registered_last_24_hours'), + checkDailyApiAccess(scopes => scopes.includes('admin:all')), + getNumVehiclesRegisteredLast24HoursHandler + ) + + app.get( + pathsFor('/admin/num_event_last_24_hours'), + checkDailyApiAccess(scopes => scopes.includes('admin:all')), + getNumEventsLast24HoursHandler + ) + + app.get( + pathsFor('/admin/trip_counts_since'), + checkDailyApiAccess(scopes => scopes.includes('admin:all')), + getTripCountsSinceHandler + ) + + app.get( + pathsFor('/admin/event_counts_per_provider_since'), + checkDailyApiAccess(scopes => scopes.includes('admin:all')), + getEventCountsPerProviderSinceHandler + ) + + app.get( + pathsFor('/admin/telemetry_counts_per_provider_since'), + checkDailyApiAccess(scopes => scopes.includes('admin:all')), + getTelemetryCountsPerProviderSinceHandler + ) + + app.get( + pathsFor('/admin/conformance_last_24_hours'), + checkDailyApiAccess(scopes => scopes.includes('admin:all')), + getConformanceLast24HoursHandler + ) + return app // /////////////////// end Agency candidate endpoints //////////////////// diff --git a/packages/mds-daily/db-helpers.ts b/packages/mds-daily/db-helpers.ts index f3be4f26d..681f381d1 100644 --- a/packages/mds-daily/db-helpers.ts +++ b/packages/mds-daily/db-helpers.ts @@ -1,5 +1,6 @@ -import log from '@mds-core/mds-logger' +import logger from '@mds-core/mds-logger' import db from '@mds-core/mds-db' +import cache from '@mds-core/mds-agency-cache' import { VehicleEvent } from '@mds-core/mds-types' import { now, isStateTransitionValid } from '@mds-core/mds-utils' import { DbHelperArgs } from './types' @@ -13,8 +14,8 @@ export const getTripCountsSince = async ({ start_time, end_time, provider_info, const rows = await db.getTripCountsPerProviderSince(start_time, end_time) const finish = now() const timeElapsed = finish - start - await log.info(`MDS-DAILY db.getTripCountsPerProviderSince() time elapsed: ${timeElapsed}`) - await log.info('trips last 24h', rows) + logger.info(`MDS-DAILY db.getTripCountsPerProviderSince() time elapsed: ${timeElapsed}`) + logger.info('trips last 24h', rows) rows.map(row => { const pid = row.provider_id provider_info[pid] = provider_info[pid] || {} @@ -28,11 +29,13 @@ export const getTripCountsSince = async ({ start_time, end_time, provider_info, export const getTimeSinceLastEvent = async ({ provider_info, fail }: DbHelperArgs) => { try { const start = now() - const rows = await db.getMostRecentEventByProvider() + const rows = await cache.getMostRecentEventByProvider() + /* FIXME fall back to DB if we're missing providers + await db.getMostRecentEventByProvider() */ const finish = now() const timeElapsed = finish - start - await log.info(`MDS-DAILY db.getMostRecentEventByProvider() time elapsed: ${timeElapsed}`) - await log.info('time since last event', rows) + logger.info(`MDS-DAILY cache.getMostRecentEventByProvider() time elapsed: ${timeElapsed}`) + logger.info('time since last event', rows) rows.map(row => { const pid = row.provider_id provider_info[pid] = provider_info[pid] || {} @@ -49,8 +52,8 @@ export const getEventCountsPerProviderSince = async ({ start_time, end_time, pro const rows = await db.getEventCountsPerProviderSince(start_time, end_time) const finish = now() const timeElapsed = finish - start - await log.info(`MDS-DAILY db.getEventCountsPerProviderSince() time elapsed: ${timeElapsed}`) - await log.info('time since last event', rows) + logger.info(`MDS-DAILY db.getEventCountsPerProviderSince() time elapsed: ${timeElapsed}`) + logger.info('time since last event', rows) rows.map(row => { const pid = row.provider_id provider_info[pid] = provider_info[pid] || {} @@ -75,8 +78,8 @@ export const getTelemetryCountsPerProviderSince = async ({ const rows = await db.getTelemetryCountsPerProviderSince(start_time, end_time) const finish = now() const timeElapsed = finish - start - await log.info(`MDS-DAILY db.getTelemetryCountsPerProviderSince() time elapsed: ${timeElapsed}`) - await log.info('time since last event', rows) + logger.info(`MDS-DAILY db.getTelemetryCountsPerProviderSince() time elapsed: ${timeElapsed}`) + logger.info('time since last event', rows) rows.map(row => { const pid = row.provider_id provider_info[pid] = provider_info[pid] || {} @@ -99,8 +102,8 @@ export const getNumVehiclesRegisteredLast24Hours = async ({ const rows = await db.getNumVehiclesRegisteredLast24HoursByProvider(start_time, end_time) const finish = now() const timeElapsed = finish - start - await log.info(`MDS-DAILY db.getNumVehiclesRegisteredLast24HoursByProvider() time elapsed: ${timeElapsed}`) - await log.info('num vehicles since last 24', rows) + logger.info(`MDS-DAILY db.getNumVehiclesRegisteredLast24HoursByProvider() time elapsed: ${timeElapsed}`) + logger.info('num vehicles since last 24', rows) rows.map(row => { const pid = row.provider_id provider_info[pid] = provider_info[pid] || {} @@ -117,7 +120,7 @@ export const getNumEventsLast24Hours = async ({ start_time, end_time, provider_i const rows = await db.getNumEventsLast24HoursByProvider(start_time, end_time) const finish = now() const timeElapsed = finish - start - await log.info(`MDS-DAILY db.getNumEventsLast24HoursByProvider() time elapsed: ${timeElapsed}`) + logger.info(`MDS-DAILY db.getNumEventsLast24HoursByProvider() time elapsed: ${timeElapsed}`) rows.map(row => { const pid = row.provider_id provider_info[pid] = provider_info[pid] || {} @@ -134,9 +137,9 @@ export const getConformanceLast24Hours = async ({ start_time, end_time, provider const rows = await db.getEventsLast24HoursPerProvider(start_time, end_time) const finish = now() const timeElapsed = finish - start - await log.info(`MDS-DAILY db.getEventsLast24HoursPerProvider() time elapsed: ${timeElapsed}`) + logger.info(`MDS-DAILY db.getEventsLast24HoursPerProvider() time elapsed: ${timeElapsed}`) const prev_event: { [key: string]: VehicleEvent } = {} - await log.info('event', rows) + logger.info('event', rows) rows.map(event => { const pid = event.provider_id provider_info[pid] = provider_info[pid] || {} diff --git a/packages/mds-daily/package.json b/packages/mds-daily/package.json index 92ec23ab6..f1fd5cd75 100644 --- a/packages/mds-daily/package.json +++ b/packages/mds-daily/package.json @@ -1,24 +1,20 @@ { "name": "@mds-core/mds-daily", "description": "MDS Daily Reporting API", - "version": "0.0.19", + "version": "0.0.27", "author": "City of Los Angeles", "license": "Apache-2.0", "dependencies": { - "@mds-core/mds-api-server": "0.1.18", - "@mds-core/mds-cache": "0.1.18", - "@mds-core/mds-db": "0.1.18", - "@mds-core/mds-logger": "0.1.16", - "@mds-core/mds-providers": "0.1.18", - "@mds-core/mds-stream": "0.1.18", - "@mds-core/mds-types": "0.1.15", - "@mds-core/mds-utils": "0.1.18", - "express": "4.17.1", - "ladot-service-areas": "0.1.9", - "uuid": "3.3.3" - }, - "devDependencies": { - "@mds-core/mds-test-data": "0.1.18" + "@mds-core/mds-api-server": "0.1.26", + "@mds-core/mds-agency-cache": "0.1.26", + "@mds-core/mds-db": "0.1.26", + "@mds-core/mds-logger": "0.1.24", + "@mds-core/mds-providers": "0.1.26", + "@mds-core/mds-stream": "0.1.26", + "@mds-core/mds-types": "0.1.23", + "@mds-core/mds-utils": "0.1.26", + "@types/express": "4.17.6", + "express": "4.17.1" }, "main": "dist/index.js", "types": "dist/index.d.ts", @@ -29,9 +25,9 @@ "build": "tsc --build tsconfig.build.json", "start": "PATH_PREFIX=/agency yarn watch server", "test": "yarn test:eslint && yarn test:unit", - "test:eslint": "eslint --fix --ignore-path ../../.gitignore '**/*.ts'", - "test:unit": "PATH_PREFIX=/agency DOTENV_CONFIG_PATH=../../.env nyc --check-coverage --exclude tests --extension .ts --lines 50 --reporter=text --reporter=html ts-mocha --project ../../tsconfig.json --require tsconfig-paths/register --require source-map-support/register --require dotenv/config --recursive --timeout 5000 tests/**/*.ts", - "watch": "nodemon --watch '../../packages' --ext 'ts' --ignore '*.d.ts' --exec yarn watch:exec --", - "watch:exec": "yarn build && DOTENV_CONFIG_PATH=../../.env ts-node -r dotenv/config" + "test:eslint": "eslint --ignore-path ../../.gitignore '**/*.ts'", + "test:unit": "PATH_PREFIX=/agency DOTENV_CONFIG_PATH=../../.env nyc --lines 55 ts-mocha --project ../../tsconfig.json", + "ts-node": "yarn build && DOTENV_CONFIG_PATH=../../.env ts-node -r dotenv/config", + "watch": "nodemon --watch '../../packages' --ext 'ts' --ignore '*.d.ts' --exec yarn ts-node --" } } diff --git a/packages/mds-daily/request-handlers.ts b/packages/mds-daily/request-handlers.ts index ad252b379..e0b39ef53 100644 --- a/packages/mds-daily/request-handlers.ts +++ b/packages/mds-daily/request-handlers.ts @@ -1,7 +1,7 @@ import db from '@mds-core/mds-db' -import log from '@mds-core/mds-logger' +import logger from '@mds-core/mds-logger' import { providerName } from '@mds-core/mds-providers' -import { now, inc, ServerError, filterEmptyHelper } from '@mds-core/mds-utils' +import { now, inc, ServerError, filterDefined } from '@mds-core/mds-utils' import { UUID, VehicleEvent, @@ -11,7 +11,6 @@ import { TripsStats, Device } from '@mds-core/mds-types' -import areas from 'ladot-service-areas' import { DailyApiRequest, DailyApiResponse, ProviderInfo } from './types' import { getTimeSinceLastEvent, @@ -25,7 +24,7 @@ import { import { startAndEnd, categorizeTrips, getMaps } from './utils' export async function dbHelperFail(err: Error | string): Promise { - await log.error( + logger.error( 'last_day_stats_by_provider err:', err instanceof Error ? err.message : err, err instanceof Error ? err.stack : '' @@ -37,13 +36,6 @@ const SERVER_ERROR = { error_description: 'an internal server error has occurred and been logged' } -const RIGHT_OF_WAY_STATUSES: string[] = [ - VEHICLE_STATUSES.available, - VEHICLE_STATUSES.unavailable, - VEHICLE_STATUSES.reserved, - VEHICLE_STATUSES.trip -] - type Item = Pick export async function getRawTripData(req: DailyApiRequest, res: DailyApiResponse) { @@ -53,7 +45,7 @@ export async function getRawTripData(req: DailyApiRequest, res: DailyApiResponse const eventsAndCount: { events: VehicleEvent[]; count: number } = await db.readEvents({ trip_id }) const finish = now() const timeElapsed = finish - start - await log.info(`MDS-DAILY /admin/raw_trip_data/:trip_id -> db.readEvents({ trip_id }) time elapsed: ${timeElapsed}`) + logger.info(`MDS-DAILY /admin/raw_trip_data/:trip_id -> db.readEvents({ trip_id }) time elapsed: ${timeElapsed}`) if (eventsAndCount.events.length > 0) { const { events } = eventsAndCount events[0].timestamp_long = new Date(events[0].timestamp).toString() @@ -66,14 +58,14 @@ export async function getRawTripData(req: DailyApiRequest, res: DailyApiResponse res.status(404).send({ result: 'not_found' }) } } catch (err) { - await log.error(`raw_trip_data: ${err}`) + logger.error(`raw_trip_data: ${err}`) res.status(500).send(SERVER_ERROR) } } export async function getVehicleCounts(req: DailyApiRequest, res: DailyApiResponse) { async function fail(err: Error | string): Promise { - await log.error('/admin/vehicle_counts fail', err) + logger.error('/admin/vehicle_counts fail', err) res.status(500).send({ error: err }) @@ -84,17 +76,13 @@ export async function getVehicleCounts(req: DailyApiRequest, res: DailyApiRespon const rows = await db.getVehicleCountsPerProvider() const finish = now() const timeElapsed = finish - start - await log.info(`MDS-DAILY /admin/vehicle_counts -> db.getVehicleCountsPerProvider() time elapsed: ${timeElapsed}`) + logger.info(`MDS-DAILY /admin/vehicle_counts -> db.getVehicleCountsPerProvider() time elapsed: ${timeElapsed}`) const stats: { provider_id: UUID provider: string count: number status: { [s: string]: number } event_type: { [s: string]: number } - areas: { [s: string]: number } - areas_12h: { [s: string]: number } - areas_24h: { [s: string]: number } - areas_48h: { [s: string]: number } }[] = rows.map(row => { const { provider_id, count } = row return { @@ -102,17 +90,10 @@ export async function getVehicleCounts(req: DailyApiRequest, res: DailyApiRespon provider: providerName(provider_id), count, status: {}, - event_type: {}, - areas: {}, - areas_12h: {}, - areas_24h: {}, - areas_48h: {} + event_type: {} } }) - await log.info('/admin/vehicle_counts', JSON.stringify(stats)) - const HRS_12_AGO = now() - 43200000 - const HRS_24_AGO = now() - 86400000 - const HRS_48_AGO = now() - 172800000 + logger.info('/admin/vehicle_counts', JSON.stringify(stats)) const maps = await getMaps() // TODO reimplement to be more efficient @@ -123,34 +104,18 @@ export async function getVehicleCounts(req: DailyApiRequest, res: DailyApiRespon const items: (Item | undefined)[] = await db.readDeviceIds(stat.provider_id) const finish2 = now() const timeElapsed2 = finish2 - start2 - await log.info( + logger.info( `MDS-DAILY /admin/vehicle_counts -> db.readDeviceIds(${stat.provider_id}) time elapsed: ${timeElapsed2}` ) - items.filter(filterEmptyHelper(true)).map(async item => { + items.filter(filterDefined({ warnOnEmpty: true })).map(async item => { const event = eventMap[item.device_id] inc(stat.event_type, event ? event.event_type : 'default') const status = event ? EVENT_STATUS_MAP[event.event_type] : VEHICLE_STATUSES.removed inc(stat.status, status) - // TODO latest-state should remove service_area_id if it's null - if (event && RIGHT_OF_WAY_STATUSES.includes(status) && event.service_area_id) { - const serviceArea = areas.serviceAreaMap[event.service_area_id] - if (serviceArea) { - inc(stat.areas, serviceArea.description) - if (event.timestamp >= HRS_12_AGO) { - inc(stat.areas_12h, serviceArea.description) - } - if (event.timestamp >= HRS_24_AGO) { - inc(stat.areas_24h, serviceArea.description) - } - if (event.timestamp >= HRS_48_AGO) { - inc(stat.areas_48h, serviceArea.description) - } - } - } }) }) ) - await log.info(JSON.stringify(stats)) + logger.info(JSON.stringify(stats)) res.status(200).send(stats) } catch (err) { await fail(err) @@ -159,7 +124,7 @@ export async function getVehicleCounts(req: DailyApiRequest, res: DailyApiRespon export async function getLastDayTripsByProvider(req: DailyApiRequest, res: DailyApiResponse) { async function fail(err: Error | string): Promise { - await log.error('last_day_trips_by_provider err:', err) + logger.error('last_day_trips_by_provider err:', err) } const { start_time, end_time } = startAndEnd(req.params) @@ -168,7 +133,7 @@ export async function getLastDayTripsByProvider(req: DailyApiRequest, res: Daily const rows = await db.getTripEventsLast24HoursByProvider(start_time, end_time) const finish = now() const timeElapsed = finish - start - await log.info( + logger.info( `MDS-DAILY /admin/last_day_trips_by_provider -> db.getTripEventsLast24HoursByProvider() time elapsed: ${timeElapsed}` ) const perTripId = categorizeTrips( @@ -223,8 +188,177 @@ export async function getLastDayStatsByProvider(req: DailyApiRequest, res: Daily ]) const finish = now() const timeElapsed = finish - start - await log.info( - `MDS-DAILY /admin/last_day_stats_by_provider -> Promise.all(dbHelpers...) time elapsed: ${timeElapsed}` + logger.info(`MDS-DAILY /admin/last_day_stats_by_provider -> Promise.all(dbHelpers...) time elapsed: ${timeElapsed}`) + + Object.keys(provider_info).map(provider_id => { + provider_info[provider_id].name = providerName(provider_id) + }) + res.status(200).send(provider_info) + } catch (err) { + logger.error('unable to fetch data from last 24 hours', err) + res.status(500).send(new ServerError()) + } +} + +export async function getTimeSinceLastEventHandler(req: DailyApiRequest, res: DailyApiResponse) { + const provider_info: ProviderInfo = {} + + const { start_time, end_time } = startAndEnd(req.params) + + try { + const start = now() + const dbHelperArgs = { start_time, end_time, provider_info, fail: dbHelperFail } + await getTimeSinceLastEvent(dbHelperArgs) + const finish = now() + const timeElapsed = finish - start + logger.info( + `MDS-DAILY /admin/time_since_last_event -> getTimeSinceLastEvent(dbHelperArgs) time elapsed: ${timeElapsed}` + ) + + Object.keys(provider_info).map(provider_id => { + provider_info[provider_id].name = providerName(provider_id) + }) + res.status(200).send(provider_info) + } catch (err) { + logger.error('unable to fetch data from last 24 hours', err) + res.status(500).send(new ServerError()) + } +} + +export async function getNumVehiclesRegisteredLast24HoursHandler(req: DailyApiRequest, res: DailyApiResponse) { + const provider_info: ProviderInfo = {} + + const { start_time, end_time } = startAndEnd(req.params) + + try { + const start = now() + const dbHelperArgs = { start_time, end_time, provider_info, fail: dbHelperFail } + await getNumVehiclesRegisteredLast24Hours(dbHelperArgs) + const finish = now() + const timeElapsed = finish - start + logger.info( + `MDS-DAILY /admin/num_vehicles_registered_last_24_hours -> db.getNumVehiclesRegisteredLast24Hours() time elapsed: ${timeElapsed}` + ) + + Object.keys(provider_info).map(provider_id => { + provider_info[provider_id].name = providerName(provider_id) + }) + res.status(200).send(provider_info) + } catch (err) { + logger.error('unable to fetch data from last 24 hours', err) + res.status(500).send(new ServerError()) + } +} + +export async function getNumEventsLast24HoursHandler(req: DailyApiRequest, res: DailyApiResponse) { + const provider_info: ProviderInfo = {} + + const { start_time, end_time } = startAndEnd(req.params) + + try { + const start = now() + const dbHelperArgs = { start_time, end_time, provider_info, fail: dbHelperFail } + await getNumEventsLast24Hours(dbHelperArgs) + const finish = now() + const timeElapsed = finish - start + logger.info(`MDS-DAILY /admin/num_event_last_24_hours -> db.getNumEventsLast24Hours() time elapsed: ${timeElapsed}`) + + Object.keys(provider_info).map(provider_id => { + provider_info[provider_id].name = providerName(provider_id) + }) + res.status(200).send(provider_info) + } catch (err) { + logger.error('unable to fetch data from last 24 hours', err) + res.status(500).send(new ServerError()) + } +} + +export async function getTripCountsSinceHandler(req: DailyApiRequest, res: DailyApiResponse) { + const provider_info: ProviderInfo = {} + + const { start_time, end_time } = startAndEnd(req.params) + + try { + const start = now() + const dbHelperArgs = { start_time, end_time, provider_info, fail: dbHelperFail } + await getTripCountsSince(dbHelperArgs) + const finish = now() + const timeElapsed = finish - start + logger.info(`MDS-DAILY /admin/trip_counts_since -> getTripCountsSince() time elapsed: ${timeElapsed}`) + + Object.keys(provider_info).map(provider_id => { + provider_info[provider_id].name = providerName(provider_id) + }) + res.status(200).send(provider_info) + } catch (err) { + logger.error('unable to fetch data from last 24 hours', err) + res.status(500).send(new ServerError()) + } +} + +export async function getEventCountsPerProviderSinceHandler(req: DailyApiRequest, res: DailyApiResponse) { + const provider_info: ProviderInfo = {} + + const { start_time, end_time } = startAndEnd(req.params) + + try { + const start = now() + const dbHelperArgs = { start_time, end_time, provider_info, fail: dbHelperFail } + await getEventCountsPerProviderSince(dbHelperArgs) + const finish = now() + const timeElapsed = finish - start + logger.info( + `MDS-DAILY /admin/event_counts_per_provider_since -> getEventCountsPerProviderSince() time elapsed: ${timeElapsed}` + ) + + Object.keys(provider_info).map(provider_id => { + provider_info[provider_id].name = providerName(provider_id) + }) + res.status(200).send(provider_info) + } catch (err) { + logger.error('unable to fetch data from last 24 hours', err) + res.status(500).send(new ServerError()) + } +} + +export async function getTelemetryCountsPerProviderSinceHandler(req: DailyApiRequest, res: DailyApiResponse) { + const provider_info: ProviderInfo = {} + + const { start_time, end_time } = startAndEnd(req.params) + + try { + const start = now() + const dbHelperArgs = { start_time, end_time, provider_info, fail: dbHelperFail } + await getTelemetryCountsPerProviderSince(dbHelperArgs) + const finish = now() + const timeElapsed = finish - start + logger.info( + `MDS-DAILY /admin/telemetry_counts_per_provider_since -> getTelemetryCountsPerProviderSince() time elapsed: ${timeElapsed}` + ) + + Object.keys(provider_info).map(provider_id => { + provider_info[provider_id].name = providerName(provider_id) + }) + res.status(200).send(provider_info) + } catch (err) { + logger.error('unable to fetch data from last 24 hours', err) + res.status(500).send(new ServerError()) + } +} + +export async function getConformanceLast24HoursHandler(req: DailyApiRequest, res: DailyApiResponse) { + const provider_info: ProviderInfo = {} + + const { start_time, end_time } = startAndEnd(req.params) + + try { + const start = now() + const dbHelperArgs = { start_time, end_time, provider_info, fail: dbHelperFail } + await getConformanceLast24Hours(dbHelperArgs) + const finish = now() + const timeElapsed = finish - start + logger.info( + `MDS-DAILY /admin/conformance_last_24_hours -> getConformanceLast24Hours() time elapsed: ${timeElapsed}` ) Object.keys(provider_info).map(provider_id => { @@ -232,7 +366,7 @@ export async function getLastDayStatsByProvider(req: DailyApiRequest, res: Daily }) res.status(200).send(provider_info) } catch (err) { - await log.error('unable to fetch data from last 24 hours', err) + logger.error('unable to fetch data from last 24 hours', err) res.status(500).send(new ServerError()) } } diff --git a/packages/mds-daily/server.ts b/packages/mds-daily/server.ts index 981304443..6522dcc12 100644 --- a/packages/mds-daily/server.ts +++ b/packages/mds-daily/server.ts @@ -14,14 +14,7 @@ limitations under the License. */ -// Express local -import { ApiServer } from '@mds-core/mds-api-server' +import { ApiServer, HttpServer } from '@mds-core/mds-api-server' import { api } from './api' -const { - env: { npm_package_name, PORT = 4005 } -} = process - -/* eslint-reason avoids import of logger */ -/* eslint-disable-next-line no-console */ -ApiServer(api).listen(PORT, () => console.log(`${npm_package_name} running on port ${PORT}`)) +HttpServer(ApiServer(api), { port: process.env.DAILY_API_PORT }) diff --git a/packages/mds-daily/tests/test.ts b/packages/mds-daily/tests/daily.spec.ts similarity index 99% rename from packages/mds-daily/tests/test.ts rename to packages/mds-daily/tests/daily.spec.ts index a51dba156..d61cc23b6 100644 --- a/packages/mds-daily/tests/test.ts +++ b/packages/mds-daily/tests/daily.spec.ts @@ -30,7 +30,7 @@ import supertest from 'supertest' import test from 'unit.js' import { VEHICLE_EVENTS, Timestamp, Device, VehicleEvent, Telemetry } from '@mds-core/mds-types' import db from '@mds-core/mds-db' -import cache from '@mds-core/mds-cache' +import cache from '@mds-core/mds-agency-cache' import { makeDevices } from '@mds-core/mds-test-data' import { ApiServer } from '@mds-core/mds-api-server' import { TEST1_PROVIDER_ID } from '@mds-core/mds-providers' diff --git a/packages/mds-daily/tests/db-helpers.test.ts b/packages/mds-daily/tests/db-helpers.spec.ts similarity index 87% rename from packages/mds-daily/tests/db-helpers.test.ts rename to packages/mds-daily/tests/db-helpers.spec.ts index 05d221ba2..b7fc0ce98 100644 --- a/packages/mds-daily/tests/db-helpers.test.ts +++ b/packages/mds-daily/tests/db-helpers.spec.ts @@ -3,14 +3,12 @@ import Sinon from 'sinon' import assert from 'assert' import { getTripCountsSince, - getTimeSinceLastEvent, getEventCountsPerProviderSince, getTelemetryCountsPerProviderSince, getNumVehiclesRegisteredLast24Hours, getNumEventsLast24Hours, getConformanceLast24Hours } from '../db-helpers' -import { ProviderInfo } from '../types' /* eslint-disable promise/avoid-new */ @@ -53,43 +51,6 @@ describe('DB helpers for API', () => { }) }) - describe('getTimeSinceLastEvent()', () => { - it('computes correctly', async () => { - const fakeRows: ReturnType = new Promise(resolve => { - resolve([ - { - provider_id: 'fake-provider-id', - max: 10 - } - ]) - }) - Sinon.replace(db, 'getMostRecentEventByProvider', Sinon.fake.returns(fakeRows)) - const provider_info: ProviderInfo = {} - await getTimeSinceLastEvent({ - start_time: 10, - end_time: 20, - provider_info, - fail: Sinon.fake.returns('fake') - }) - assert.equal(provider_info['fake-provider-id'].ms_since_last_event > 0, true) - Sinon.restore() - }) - - it('fails gracefully', async () => { - Sinon.replace(db, 'getMostRecentEventByProvider', Sinon.fake.rejects('fake-error')) - const fail = Sinon.fake.returns('fake') - const provider_info = {} - await getTimeSinceLastEvent({ - start_time: 10, - end_time: 20, - provider_info, - fail - }) - assert.equal(fail.called, true) - Sinon.restore() - }) - }) - describe('getEventCountsPerProviderSince()', () => { it('computes correctly', async () => { const fakeRows: ReturnType = new Promise(resolve => { diff --git a/packages/mds-daily/tests/request-handlers.test.ts b/packages/mds-daily/tests/request-handlers.spec.ts similarity index 92% rename from packages/mds-daily/tests/request-handlers.test.ts rename to packages/mds-daily/tests/request-handlers.spec.ts index a23538812..1c5d01b60 100644 --- a/packages/mds-daily/tests/request-handlers.test.ts +++ b/packages/mds-daily/tests/request-handlers.spec.ts @@ -16,9 +16,9 @@ describe('Request handlers', () => { } as any } await getRawTripData( - ({ + { params: { trip_id: 'fake-trip-id' } - } as unknown) as DailyApiRequest, + } as DailyApiRequest<{ trip_id: string }>, res as DailyApiResponse ) Sinon.restore() diff --git a/packages/mds-daily/tests/utils.test.ts b/packages/mds-daily/tests/utils.spec.ts similarity index 97% rename from packages/mds-daily/tests/utils.test.ts rename to packages/mds-daily/tests/utils.spec.ts index 7ff5878df..d86c184db 100644 --- a/packages/mds-daily/tests/utils.test.ts +++ b/packages/mds-daily/tests/utils.spec.ts @@ -1,5 +1,5 @@ import assert from 'assert' -import cache from '@mds-core/mds-cache' +import cache from '@mds-core/mds-agency-cache' import Sinon from 'sinon' import { categorizeTrips, TripsData, asInt, getMaps } from '../utils' diff --git a/packages/mds-daily/tsconfig.build.json b/packages/mds-daily/tsconfig.build.json index 5d07e40f1..d27efcdce 100644 --- a/packages/mds-daily/tsconfig.build.json +++ b/packages/mds-daily/tsconfig.build.json @@ -5,7 +5,7 @@ }, "references": [ { "path": "../../packages/mds-api-server/tsconfig.build.json" }, - { "path": "../../packages/mds-cache/tsconfig.build.json" }, + { "path": "../../packages/mds-agency-cache/tsconfig.build.json" }, { "path": "../../packages/mds-db/tsconfig.build.json" }, { "path": "../../packages/mds-logger/tsconfig.build.json" }, { "path": "../../packages/mds-providers/tsconfig.build.json" }, diff --git a/packages/mds-daily/tsconfig.eslint.json b/packages/mds-daily/tsconfig.eslint.json new file mode 100644 index 000000000..2a400942a --- /dev/null +++ b/packages/mds-daily/tsconfig.eslint.json @@ -0,0 +1 @@ +{ "extends": "../../tsconfig.eslint.json" } diff --git a/packages/mds-daily/types.ts b/packages/mds-daily/types.ts index 178fae5d2..7f22343d4 100644 --- a/packages/mds-daily/types.ts +++ b/packages/mds-daily/types.ts @@ -1,9 +1,13 @@ import { UUID } from '@mds-core/mds-types' import { MultiPolygon } from 'geojson' -import { ApiRequest, ApiResponse } from '@mds-core/mds-api-server' +import { ApiRequest, ApiResponse, ApiClaims } from '@mds-core/mds-api-server' +import { Params, ParamsDictionary } from 'express-serve-static-core' -export type DailyApiRequest = ApiRequest -export type DailyApiResponse = ApiResponse +export type DailyApiRequest

= ApiRequest

+ +export type DailyApiAccessTokenScopes = 'admin:all' + +export type DailyApiResponse = ApiResponse> export interface ServiceArea { service_area_id: UUID diff --git a/packages/mds-daily/utils.ts b/packages/mds-daily/utils.ts index 09d47dc6b..8b7c78b4d 100644 --- a/packages/mds-daily/utils.ts +++ b/packages/mds-daily/utils.ts @@ -1,7 +1,7 @@ import { isTimestamp, now, days, inc, head, tail } from '@mds-core/mds-utils' import { UUID, CountMap, TripsStats, VEHICLE_EVENTS, VehicleEvent } from '@mds-core/mds-types' -import cache from '@mds-core/mds-cache' -import log from '@mds-core/mds-logger' +import cache from '@mds-core/mds-agency-cache' +import logger from '@mds-core/mds-logger' // TODO move to utils? export function asInt(n: string | number | undefined): number | undefined { @@ -108,9 +108,9 @@ export async function getMaps(): Promise<{ }> { try { // const telemetry: Telemetry[] = await cache.readAllTelemetry() - // log.info('read telemetry') + // logger.info('read telemetry') const events = await cache.readAllEvents() - log.info('read events') + logger.info('read events') const eventSeed: { [s: string]: VehicleEvent } = {} const eventMap: { [s: string]: VehicleEvent } = events.reduce((map, event) => { return event ? Object.assign(map, { [event.device_id]: event }) : map diff --git a/packages/mds-db/attachments.ts b/packages/mds-db/attachments.ts new file mode 100644 index 000000000..429888ea2 --- /dev/null +++ b/packages/mds-db/attachments.ts @@ -0,0 +1,69 @@ +import { Attachment, AuditAttachment, Recorded, UUID } from '@mds-core/mds-types' +import { NotFoundError, now } from '@mds-core/mds-utils' +import schema from './schema' +import { vals_sql, cols_sql, vals_list, logSql } from './sql-utils' +import { getReadOnlyClient, getWriteableClient } from './client' + +export async function writeAttachment(attachment: Attachment): Promise> { + const client = await getWriteableClient() + const sql = `INSERT INTO ${schema.TABLE.attachments} (${cols_sql( + schema.TABLE_COLUMNS.attachments + )}) VALUES (${vals_sql(schema.TABLE_COLUMNS.attachments)}) RETURNING *` + const values = vals_list(schema.TABLE_COLUMNS.attachments, { ...attachment, recorded: now() }) + await logSql(sql, values) + const { + rows: [recordedAttachment] + }: { rows: Recorded[] } = await client.query(sql, values) + return { ...attachment, ...recordedAttachment } +} + +export async function readAttachmentsForAudit(audit_trip_id: UUID): Promise[]> { + const client = await getReadOnlyClient() + const sql = `SELECT * FROM ${schema.TABLE.attachments} a JOIN ${schema.TABLE.audit_attachments} aa + ON a.attachment_id = aa.attachment_id where aa.audit_trip_id = '${audit_trip_id}'` + const { rows } = await client.query(sql) + return rows +} + +export async function readAuditAttachments(attachment_id: UUID): Promise { + const client = await getWriteableClient() + const sql = `SELECT * FROM ${schema.TABLE.audit_attachments} WHERE attachment_id=$1` + const res = await client.query(sql, [attachment_id]) + return res.rows +} + +export async function writeAuditAttachment(auditAttachment: AuditAttachment): Promise> { + const client = await getWriteableClient() + const sql = `INSERT INTO ${schema.TABLE.audit_attachments} (${cols_sql( + schema.TABLE_COLUMNS.audit_attachments + )}) VALUES (${vals_sql(schema.TABLE_COLUMNS.audit_attachments)}) RETURNING *` + const values = vals_list(schema.TABLE_COLUMNS.audit_attachments, { ...auditAttachment, recorded: now() }) + await logSql(sql, values) + const { + rows: [recordedAuditAttachment] + }: { rows: Recorded[] } = await client.query(sql, values) + return { ...auditAttachment, ...recordedAuditAttachment } +} + +export async function deleteAttachment(attachment_id: UUID): Promise { + const client = await getWriteableClient() + const sql = `DELETE FROM ${schema.TABLE.attachments} WHERE attachment_id=$1 RETURNING *` + const res = await client.query(sql, [attachment_id]) + if (res.rows.length > 0) { + return { ...res.rows[0] } as Attachment + } + throw new NotFoundError(`Attachment ${attachment_id} not found`) +} + +export async function deleteAuditAttachment( + audit_trip_id: UUID, + attachment_id: UUID +): Promise { + const client = await getWriteableClient() + const sql = `DELETE FROM ${schema.TABLE.audit_attachments} WHERE attachment_id=$1 AND audit_trip_id=$2 RETURNING *` + const res = await client.query(sql, [attachment_id, audit_trip_id]) + if (res.rows.length > 0) { + return { ...res.rows[0] } as AuditAttachment + } + throw new NotFoundError(`Audit attachment ${audit_trip_id} ${attachment_id} not found`) +} diff --git a/packages/mds-db/audits.ts b/packages/mds-db/audits.ts index 6a39d2a42..1fb5325e1 100644 --- a/packages/mds-db/audits.ts +++ b/packages/mds-db/audits.ts @@ -1,6 +1,6 @@ import { Audit, AuditEvent, UUID, Recorded } from '@mds-core/mds-types' import { now } from '@mds-core/mds-utils' -import log from '@mds-core/mds-logger' +import logger from '@mds-core/mds-logger' import { ReadAuditsQueryParams } from './types' @@ -20,7 +20,7 @@ export async function readAudit(audit_trip_id: UUID) { return result.rows[0] } const error = `readAudit db failed for ${audit_trip_id}: rows=${result.rows.length}` - await log.warn(error) + logger.warn(error) throw new Error(error) } @@ -64,13 +64,14 @@ export async function readAudits(query: ReadAuditsQueryParams) { audits: selectResult.rows } } catch (err) { - await log.error('readAudits error', err.stack || err) + logger.error('readAudits error', err.stack || err) throw err } } export async function writeAudit(audit: Audit): Promise> { // write pg + const start = now() const client = await getWriteableClient() const sql = `INSERT INTO ${schema.TABLE.audits} (${cols_sql(schema.TABLE_COLUMNS.audits)}) VALUES (${vals_sql( schema.TABLE_COLUMNS.audits @@ -80,6 +81,8 @@ export async function writeAudit(audit: Audit): Promise> { const { rows: [recorded_audit] }: { rows: Recorded[] } = await client.query(sql, values) + const finish = now() + logger.info(`MDS-DB writeAudit time elapsed: ${finish - start}ms`) return { ...audit, ...recorded_audit } } @@ -104,12 +107,13 @@ export async function readAuditEvents(audit_trip_id: UUID): Promise> { + const start = now() const client = await getWriteableClient() const sql = `INSERT INTO ${schema.TABLE.audit_events} (${cols_sql( schema.TABLE_COLUMNS.audit_events @@ -119,5 +123,7 @@ export async function writeAuditEvent(audit_event: AuditEvent): Promise[] } = await client.query(sql, values) + const finish = now() + logger.info(`MDS-DB writeAuditEvent time elapsed: ${finish - start}ms`) return { ...audit_event, ...recorded_audit_event } } diff --git a/packages/mds-db/client.ts b/packages/mds-db/client.ts index 66937ff98..49778d5e5 100644 --- a/packages/mds-db/client.ts +++ b/packages/mds-db/client.ts @@ -1,7 +1,7 @@ -import log from '@mds-core/mds-logger' +import logger from '@mds-core/mds-logger' import { updateSchema } from './migration' -import { logSql, configureClient, MDSPostgresClient } from './sql-utils' +import { logSql, configureClient, MDSPostgresClient, SqlVals } from './sql-utils' const { env } = process @@ -29,7 +29,7 @@ async function setupClient(useWriteable: boolean): Promise { port: Number(PG_PORT) || 5432 } - await log.info('connecting to postgres:', ...Object.keys(info).map(key => (info as { [x: string]: unknown })[key])) + logger.info('connecting to postgres:', ...Object.keys(info).map(key => (info as { [x: string]: unknown })[key])) const client = configureClient({ ...info, password: PG_PASS }) @@ -43,7 +43,7 @@ async function setupClient(useWriteable: boolean): Promise { client.setConnected(true) return client } catch (err) { - await log.error('postgres connection error', err.stack) + logger.error('postgres connection error', err.stack) client.setConnected(false) throw err } @@ -59,7 +59,7 @@ export async function getReadOnlyClient(): Promise { return readOnlyCachedClient } catch (err) { readOnlyCachedClient = null - await log.error('postgres connection error', err) + logger.error('postgres connection error', err) throw err } } @@ -74,7 +74,7 @@ export async function getWriteableClient(): Promise { return writeableCachedClient } catch (err) { writeableCachedClient = null - await log.error('postgres connection error', err) + logger.error('postgres connection error', err) throw err } } @@ -84,14 +84,15 @@ export async function getWriteableClient(): Promise { /* eslint-reason ambigous helper function that wraps a query as Readonly */ /* eslint-disable-next-line @typescript-eslint/no-explicit-any */ -export async function makeReadOnlyQuery(sql: string): Promise { +export async function makeReadOnlyQuery(sql: string, vals?: SqlVals): Promise { try { + const values = vals?.values() const client = await getReadOnlyClient() await logSql(sql) - const result = await client.query(sql) + const result = await client.query(sql, values) return result.rows } catch (err) { - await log.error(`error with SQL query ${sql}`, err.stack || err) + logger.error(`error with SQL query ${sql}`, err.stack || err) throw err } } diff --git a/packages/mds-db/devices.ts b/packages/mds-db/devices.ts index 1d0a887a6..827108a69 100644 --- a/packages/mds-db/devices.ts +++ b/packages/mds-db/devices.ts @@ -1,7 +1,7 @@ import { QueryResult } from 'pg' import { UUID, Device, Recorded, DeviceID } from '@mds-core/mds-types' -import { now, yesterday, isUUID, csv } from '@mds-core/mds-utils' -import log from '@mds-core/mds-logger' +import { now, yesterday, isUUID, csv, NotFoundError } from '@mds-core/mds-utils' +import logger from '@mds-core/mds-logger' import schema from './schema' @@ -9,11 +9,11 @@ import { vals_sql, cols_sql, vals_list, logSql, SqlVals, MDSPostgresClient } fro import { getReadOnlyClient, getWriteableClient, makeReadOnlyQuery } from './client' -export async function readDeviceByVehicleId( +export async function readDevicesByVehicleId( provider_id: UUID, vehicle_id: UUID, ...alternate_vehicle_ids: UUID[] -): Promise> { +): Promise[]> { const client = await getReadOnlyClient() const vehicle_ids = [...new Set([vehicle_id, ...alternate_vehicle_ids])] const vals = new SqlVals() @@ -21,19 +21,21 @@ export async function readDeviceByVehicleId( provider_id )} AND translate(vehicle_id, translate(lower(vehicle_id), 'abcdefghijklmnopqrstuvwxyz1234567890', ''), '') ILIKE ANY(ARRAY[${vehicle_ids .map(id => vals.add(id)) - .join(', ')}])` + .join(', ')}]) ORDER BY "id" DESC` const values = vals.values() await logSql(sql, values) const result = await client.query(sql, values) - if (result.rows.length === 1) { - return result.rows[0] as Recorded + if (result.rows.length !== 1) { + const error = `device associated with vehicle ${ + vehicle_ids.length === 1 ? vehicle_id : `(${csv(vehicle_ids)})` + } for provider ${provider_id}: rows=${result.rows.length}` + logger.warn(error) + } + if (result.rows.length === 0) { + throw new NotFoundError('No device found', { provider_id, vehicle_ids }) } - const error = `device associated with vehicle ${ - vehicle_ids.length === 1 ? vehicle_id : `(${csv(vehicle_ids)})` - } for provider ${provider_id}: rows=${result.rows.length}` - await log.warn(error) - throw Error(error) + return result.rows as Recorded[] } export async function readDeviceIds(provider_id?: UUID, skip?: number, take?: number): Promise { @@ -74,7 +76,7 @@ export async function readDevice( if (res.rows.length === 1) { return res.rows[0] } - await log.info(`readDevice db failed for ${device_id}: rows=${res.rows.length}`) + logger.info(`readDevice db failed for ${device_id}: rows=${res.rows.length}`) throw new Error(`device_id ${device_id} not found`) } @@ -144,6 +146,9 @@ export async function getNumVehiclesRegisteredLast24HoursByProvider( start = yesterday(), stop = now() ): Promise<{ provider_id: UUID; count: number }[]> { - const sql = `select provider_id, count(device_id) from ${schema.TABLE.devices} where recorded > ${start} and recorded < ${stop} group by provider_id` - return makeReadOnlyQuery(sql) + const vals = new SqlVals() + const sql = `select provider_id, count(device_id) from ${schema.TABLE.devices} where recorded > ${vals.add( + start + )} and recorded < ${vals.add(stop)} group by provider_id` + return makeReadOnlyQuery(sql, vals) } diff --git a/packages/mds-db/events.ts b/packages/mds-db/events.ts index adb79a497..4cee52eca 100644 --- a/packages/mds-db/events.ts +++ b/packages/mds-db/events.ts @@ -1,6 +1,6 @@ import { VehicleEvent, UUID, Timestamp, Recorded } from '@mds-core/mds-types' import { now, isUUID, isTimestamp, seconds, yesterday } from '@mds-core/mds-utils' -import log from '@mds-core/mds-logger' +import logger from '@mds-core/mds-logger' import { ReadEventsResult, ReadEventsQueryParams, ReadHistoricalEventsQueryParams } from './types' import schema from './schema' @@ -43,7 +43,7 @@ export async function readEvent(device_id: UUID, timestamp?: Timestamp): Promise if (res.rows.length === 1) { return res.rows[0] } - log.info(`readEvent failed for ${device_id}:${timestamp || 'latest'}`) + logger.info(`readEvent failed for ${device_id}:${timestamp || 'latest'}`) throw new Error(`event for ${device_id}:${timestamp} not found`) } @@ -98,7 +98,7 @@ export async function readEvents(params: ReadEventsQueryParams): Promise { const { provider_id: query_provider_id, end_date } = params const client = await getReadOnlyClient() const vals = new SqlVals() @@ -198,27 +198,39 @@ export async function getEventCountsPerProviderSince( stop = now() ): Promise<{ provider_id: UUID; event_type: string; count: number; slacount: number }[]> { const thirty_sec = seconds(30) - const sql = `select provider_id, event_type, count(*), count(case when (recorded-timestamp) > ${thirty_sec} then 1 else null end) as slacount from events where recorded > ${start} and recorded < ${stop} group by provider_id, event_type` - return makeReadOnlyQuery(sql) + const vals = new SqlVals() + const sql = `select provider_id, event_type, count(*), count(case when (recorded-timestamp) > ${vals.add( + thirty_sec + )} then 1 else null end) as slacount from events where recorded > ${vals.add(start)} and recorded < ${vals.add( + stop + )} group by provider_id, event_type` + return makeReadOnlyQuery(sql, vals) } export async function getEventsLast24HoursPerProvider(start = yesterday(), stop = now()): Promise { - const sql = `select provider_id, device_id, event_type, recorded, timestamp from ${schema.TABLE.events} where recorded > ${start} and recorded < ${stop} order by "timestamp" ASC` - return makeReadOnlyQuery(sql) + const vals = new SqlVals() + const sql = `select provider_id, device_id, event_type, recorded, timestamp from ${ + schema.TABLE.events + } where recorded > ${vals.add(start)} and recorded < ${vals.add(stop)} order by "timestamp" ASC` + return makeReadOnlyQuery(sql, vals) } export async function getNumEventsLast24HoursByProvider( start = yesterday(), stop = now() ): Promise<{ provider_id: UUID; count: number }[]> { - const sql = `select provider_id, count(*) from ${schema.TABLE.events} where recorded > ${start} and recorded < ${stop} group by provider_id` - return makeReadOnlyQuery(sql) + const vals = new SqlVals() + const sql = `select provider_id, count(*) from ${schema.TABLE.events} where recorded > ${vals.add( + start + )} and recorded < ${vals.add(stop)} group by provider_id` + return makeReadOnlyQuery(sql, vals) } export async function readEventsWithTelemetry({ device_id, provider_id, start_time, end_time, + order_by = 'id', last_id = 0, limit = 1000 }: Partial<{ @@ -226,6 +238,7 @@ export async function readEventsWithTelemetry({ provider_id: UUID start_time: Timestamp end_time: Timestamp + order_by: string last_id: number limit: number }>): Promise[]> { @@ -272,10 +285,10 @@ export async function readEventsWithTelemetry({ const { rows } = await exec( `SELECT E.*, T.lat, T.lng, T.speed, T.heading, T.accuracy, T.altitude, T.charge, T.timestamp AS telemetry_timestamp FROM (SELECT * FROM ${ schema.TABLE.events - }${where} ORDER BY id LIMIT ${vals.add(limit)} + }${where} ORDER BY ${order_by} LIMIT ${vals.add(limit)} ) AS E LEFT JOIN ${ schema.TABLE.telemetry - } T ON E.device_id = T.device_id AND CASE WHEN E.telemetry_timestamp IS NULL THEN E.timestamp ELSE E.telemetry_timestamp END = T.timestamp ORDER BY id`, + } T ON E.device_id = T.device_id AND CASE WHEN E.telemetry_timestamp IS NULL THEN E.timestamp ELSE E.telemetry_timestamp END = T.timestamp ORDER BY ${order_by}`, vals.values() ) @@ -291,7 +304,7 @@ export async function readEventsWithTelemetry({ })) } -// TODO way too slow to be useful -- move into mds-cache +// TODO way too slow to be useful -- move into mds-agency-cache export async function getMostRecentEventByProvider(): Promise<{ provider_id: UUID; max: number }[]> { const sql = `select provider_id, max(recorded) from ${schema.TABLE.events} group by provider_id` return makeReadOnlyQuery(sql) diff --git a/packages/mds-db/geographies.ts b/packages/mds-db/geographies.ts index be0d90288..d672fc157 100644 --- a/packages/mds-db/geographies.ts +++ b/packages/mds-db/geographies.ts @@ -1,6 +1,6 @@ import { Geography, GeographySummary, UUID, Recorded, GeographyMetadata } from '@mds-core/mds-types' -import { NotFoundError } from '@mds-core/mds-utils' -import log from '@mds-core/mds-logger' +import { BadParamsError, NotFoundError, DependencyMissingError, AlreadyPublishedError } from '@mds-core/mds-utils' +import logger from '@mds-core/mds-logger' import schema from './schema' @@ -10,35 +10,53 @@ import { getReadOnlyClient, getWriteableClient } from './client' import { ReadGeographiesParams, PublishGeographiesParams } from './types' export async function readSingleGeography(geography_id: UUID): Promise { - try { - const client = await getReadOnlyClient() + const client = await getReadOnlyClient() - const sql = `select * from ${schema.TABLE.geographies} where geography_id = '${geography_id}'` - const { rows } = await client.query(sql) + const sql = `select * from ${schema.TABLE.geographies} where geography_id = '${geography_id}'` + const { rows } = await client.query(sql) - const { id, ...geography } = rows[0] - return geography - } catch (err) { - await log.error('readSingleGeography', err) - throw new NotFoundError(`could not find geography ${geography_id}`) + if (rows.length === 0) { + logger.info(`readSingleGeography failed for ${geography_id}`) + throw new NotFoundError(`geography of id ${geography_id} not found`) } + + const { id, ...geography } = rows[0] + return geography } export async function readGeographies(params: Partial = {}): Promise { try { const client = await getReadOnlyClient() - const { get_read_only } = { get_read_only: false, ...params } + const { get_published, get_unpublished, geography_ids } = { + get_published: false, + get_unpublished: false, + ...params + } + if (get_published && get_unpublished) { + throw new BadParamsError('cannot have get_unpublished and get_published both be true') + } let sql = `SELECT * FROM ${schema.TABLE.geographies}` const conditions = [] const vals = new SqlVals() - if (get_read_only) { + if (get_published) { conditions.push(`publish_date IS NOT NULL`) } + if (get_unpublished) { + conditions.push(`publish_date IS NULL`) + } + + if (geography_ids) { + const SQLified_geography_ids = geography_ids.map(id => { + return `'${id}'` + }) + conditions.push(`geography_id in (${SQLified_geography_ids.join(',')})`) + } + if (conditions.length) { sql += ` WHERE ${conditions.join(' AND ')}` } @@ -53,12 +71,12 @@ export async function readGeographies(params: Partial = { return geography }) } catch (err) { - await log.error('readGeographies', err) + logger.error('readGeographies', err) throw err } } -export async function readGeographySummaries(params?: { get_read_only?: boolean }): Promise { +export async function readGeographySummaries(params?: ReadGeographiesParams): Promise { const geographies = await readGeographies(params) return geographies.map(geography => { const { geography_json, ...geographySummary } = geography @@ -66,15 +84,14 @@ export async function readGeographySummaries(params?: { get_read_only?: boolean }) } -export async function readBulkGeographyMetadata(params?: { get_read_only?: boolean }): Promise { +export async function readBulkGeographyMetadata(params?: ReadGeographiesParams): Promise { const geographies = await readGeographies(params) - const geography_ids = geographies.map(geography => { - return `'${geography.geography_id}'` - }) + const geography_ids = geographies.map(geography => `'${geography.geography_id}'`) if (geography_ids.length === 0) { return [] } + const sql = `select * from ${schema.TABLE.geography_metadata} where geography_id in (${geography_ids.join(',')})` const client = await getReadOnlyClient() @@ -85,8 +102,6 @@ export async function readBulkGeographyMetadata(params?: { get_read_only?: boole } export async function writeGeography(geography: Geography): Promise> { - // validate TODO - // write const client = await getWriteableClient() const sql = `INSERT INTO ${schema.TABLE.geographies} (${cols_sql( schema.TABLE_COLUMNS.geographies @@ -107,25 +122,37 @@ export async function isGeographyPublished(geography_id: UUID) { if (result.rows.length === 0) { throw new NotFoundError(`geography_id ${geography_id} not found`) } - log.info('is geography published', geography_id, Boolean(result.rows[0].publish_date)) return Boolean(result.rows[0].publish_date) } -export async function editGeography(geography: Geography) { +export async function editGeography(geography: Geography): Promise { // validate TODO if (await isGeographyPublished(geography.geography_id)) { throw new Error('Cannot edit published Geography') } const client = await getWriteableClient() - const sql = `UPDATE ${schema.TABLE.geographies} SET geography_json=$1 WHERE geography_id='${geography.geography_id}' AND publish_date IS NULL` - await client.query(sql, [geography.geography_json]) - return geography + const vals = new SqlVals() + const conditions: string[] = [] + Object.entries(geography).forEach(([key, value]) => { + if (key === 'geography_json') { + conditions.push(`geography_json = ${vals.add(JSON.stringify(geography.geography_json))}`) + } else { + conditions.push(`${key} = ${vals.add(value)}`) + } + }) + const sql = `UPDATE ${schema.TABLE.geographies} SET ${conditions} WHERE geography_id=${vals.add( + geography.geography_id + )} AND publish_date IS NULL` + + await client.query(sql, vals.values()) + return readSingleGeography(geography.geography_id) } export async function deleteGeography(geography_id: UUID) { + await readSingleGeography(geography_id) if (await isGeographyPublished(geography_id)) { - throw new Error('Cannot edit published Geography') + throw new AlreadyPublishedError('Cannot delete published Geography') } const client = await getWriteableClient() @@ -134,8 +161,12 @@ export async function deleteGeography(geography_id: UUID) { return geography_id } -export async function publishGeography(params: PublishGeographiesParams) { - const { geography_id, publish_date } = params +export async function publishGeography(params: PublishGeographiesParams): Promise { + /* publish_date is parameterized, because when a Policy is published, + * we want to be able to set the publish_date of any associated Geography to be + * identical to the publish_date of the Policy. + */ + const { geography_id, publish_date = Date.now() } = params try { const client = await getWriteableClient() @@ -147,52 +178,79 @@ export async function publishGeography(params: PublishGeographiesParams) { const vals = new SqlVals() const conditions = [] conditions.push(`publish_date = ${vals.add(publish_date)}`) - const sql = `UPDATE ${schema.TABLE.geographies} SET ${conditions} where geography_id=${vals.add(geography_id)}` - - await client.query(sql, vals.values()) - return geography_id + const sql = `UPDATE ${schema.TABLE.geographies} SET ${conditions} where geography_id=${vals.add( + geography_id + )} RETURNING *` + const { + rows: [recorded_geography] + }: { rows: Recorded[] } = await client.query(sql, vals.values()) + return { ...recorded_geography } } catch (err) { - await log.error(err) + logger.error(err) throw err } } -export async function writeGeographyMetadata(geography_metadata: GeographyMetadata) { - const client = await getWriteableClient() - const sql = `INSERT INTO ${schema.TABLE.geography_metadata} (${cols_sql( - schema.TABLE_COLUMNS.geography_metadata - )}) VALUES (${vals_sql(schema.TABLE_COLUMNS.geography_metadata)}) RETURNING *` - const values = vals_list(schema.TABLE_COLUMNS.geography_metadata, { - geography_id: geography_metadata.geography_id, - geography_metadata: geography_metadata.geography_metadata - }) - const { - rows: [recorded_metadata] - }: { rows: Recorded[] } = await client.query(sql, values) - return { ...geography_metadata, ...recorded_metadata } +export async function writeGeographyMetadata(geography_metadata: GeographyMetadata): Promise { + try { + await readSingleGeography(geography_metadata.geography_id) + const client = await getWriteableClient() + const sql = `INSERT INTO ${schema.TABLE.geography_metadata} (${cols_sql( + schema.TABLE_COLUMNS.geography_metadata + )}) VALUES (${vals_sql(schema.TABLE_COLUMNS.geography_metadata)}) RETURNING *` + const values = vals_list(schema.TABLE_COLUMNS.geography_metadata, { + geography_id: geography_metadata.geography_id, + geography_metadata: geography_metadata.geography_metadata + }) + const { + rows: [recorded_metadata] + }: { rows: Recorded[] } = await client.query(sql, values) + return { ...geography_metadata, ...recorded_metadata } + } catch (err) { + throw new DependencyMissingError( + `metadata not written, because no geography exists for geography_id ${geography_metadata.geography_id}` + ) + } } export async function readSingleGeographyMetadata(geography_id: UUID): Promise { const client = await getReadOnlyClient() - const sql = `SELECT * FROM ${schema.TABLE.geography_metadata} WHERE geography_id = '${geography_id}'` - const result = await client.query(sql) + const vals = new SqlVals() + const sql = `SELECT * FROM ${schema.TABLE.geography_metadata} WHERE geography_id = ${vals.add(geography_id)}` + const result = await client.query(sql, vals.values()) if (result.rows.length === 0) { throw new NotFoundError(`Metadata for ${geography_id} not found`) } return { geography_id, geography_metadata: result.rows[0].geography_metadata } } -export async function updateGeographyMetadata(geography_metadata: GeographyMetadata) { +export async function updateGeographyMetadata(geography_metadata: GeographyMetadata): Promise { await readSingleGeographyMetadata(geography_metadata.geography_id) const client = await getWriteableClient() + const vals = new SqlVals() const sql = `UPDATE ${schema.TABLE.geography_metadata} - SET geography_metadata = '${JSON.stringify(geography_metadata.geography_metadata)}' - WHERE geography_id = '${geography_metadata.geography_id}'` + SET geography_metadata = ${vals.add(JSON.stringify(geography_metadata.geography_metadata))} + WHERE geography_id = ${vals.add(geography_metadata.geography_id)}` const { rows: [recorded_metadata] - }: { rows: Recorded[] } = await client.query(sql) + }: { rows: Recorded[] } = await client.query(sql, vals.values()) return { ...geography_metadata, ...recorded_metadata } } + +export async function deleteGeographyMetadata(geography_id: UUID) { + const client = await getWriteableClient() + try { + // Putting this DB call in because the SQL won't throw if the metadata isn't there. + await readSingleGeographyMetadata(geography_id) + const vals = new SqlVals() + const sql = `DELETE FROM ${schema.TABLE.geography_metadata} WHERE geography_id = ${vals.add(geography_id)}` + await client.query(sql, vals.values()) + } catch (err) { + logger.error(`deleteGeographyMetadata called on non-existent metadata for ${geography_id}`, err.stack) + throw err + } + return geography_id +} diff --git a/packages/mds-db/index.ts b/packages/mds-db/index.ts index 796e02838..bb59f2ac4 100644 --- a/packages/mds-db/index.ts +++ b/packages/mds-db/index.ts @@ -1,74 +1,47 @@ +/* + Copyright 2019-2020 City of Los Angeles. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + import { VehicleEvent, Device, Telemetry } from '@mds-core/mds-types' -import log from '@mds-core/mds-logger' +import logger from '@mds-core/mds-logger' import { dropTables, updateSchema } from './migration' import { MDSPostgresClient } from './sql-utils' import { getReadOnlyClient, getWriteableClient, makeReadOnlyQuery } from './client' -import { - readDeviceByVehicleId, - readDeviceIds, - readDevice, - readDeviceList, - writeDevice, - updateDevice, - wipeDevice, - getVehicleCountsPerProvider, - getNumVehiclesRegisteredLast24HoursByProvider -} from './devices' - -import { - writeEvent, - readEvent, - readEvents, - readHistoricalEvents, - getEventCountsPerProviderSince, - getEventsLast24HoursPerProvider, - getNumEventsLast24HoursByProvider, - getMostRecentEventByProvider, - readEventsWithTelemetry -} from './events' - -import { - readPolicies, - writePolicy, - readPolicy, - editPolicy, - deletePolicy, - writePolicyMetadata, - updatePolicyMetadata, - readBulkPolicyMetadata, - readSinglePolicyMetadata, - publishPolicy, - readRule, - isPolicyPublished -} from './policies' - -import { - writeGeographyMetadata, - updateGeographyMetadata, - readSingleGeographyMetadata, - readSingleGeography, - readBulkGeographyMetadata, - readGeographies, - readGeographySummaries, - writeGeography, - publishGeography, - deleteGeography, - isGeographyPublished, - editGeography -} from './geographies' - -import { readAudit, readAudits, writeAudit, deleteAudit, readAuditEvents, writeAuditEvent } from './audits' - -import { getTripEventsLast24HoursByProvider, getTripCountsPerProviderSince } from './trips' - -import { - readTelemetry, - writeTelemetry, - getTelemetryCountsPerProviderSince, - getMostRecentTelemetryByProvider -} from './telemetry' +import * as devices from './devices' + +import * as events from './events' + +import * as policies from './policies' + +import * as geographies from './geographies' + +import * as audit from './audits' + +import * as trips from './trips' + +import * as telemetry from './telemetry' + +import * as stops from './stops' + +import * as attachments from './attachments' + +const { writeDevice } = devices +const { writeTelemetry } = telemetry +const { writeEvent } = events async function initialize() { const client: MDSPostgresClient = await getWriteableClient() @@ -93,7 +66,7 @@ async function health(): Promise<{ using: string stats: { current_running_queries: number; cache_hit_result: { heap_read: string; heap_hit: string; ratio: string } } }> { - log.info('postgres health check') + logger.info('postgres health check') const currentQueriesSQL = `SELECT query FROM pg_stat_activity WHERE query <> '' AND query NOT ILIKE '%pg_stat_activity%' AND query <> '' @@ -127,7 +100,7 @@ async function shutdown(): Promise { const readOnlyClient = await getReadOnlyClient() await readOnlyClient.end() } catch (err) { - await log.error('error during disconnection', err.stack) + logger.error('error during disconnection', err.stack) } } @@ -140,80 +113,35 @@ async function seed(data: { telemetry?: Telemetry[] }) { if (data) { - log.info('postgres seed start') + logger.info('postgres seed start') if (data.devices) { await Promise.all(data.devices.map(async (device: Device) => writeDevice(device))) } - log.info('postgres devices seeded') + logger.info('postgres devices seeded') if (data.events) await Promise.all(data.events.map(async (event: VehicleEvent) => writeEvent(event))) - log.info('postgres events seeded') + logger.info('postgres events seeded') if (data.telemetry) { await writeTelemetry(data.telemetry) } - log.info('postgres seed done') + logger.info('postgres seed done') return Promise.resolve() } return Promise.resolve('no data') } -export = { +export default { initialize, health, seed, startup, shutdown, - readDeviceByVehicleId, - readDeviceIds, - readDevice, - readDeviceList, - writeDevice, - updateDevice, - readEvent, - readEvents, - readHistoricalEvents, - writeEvent, - readTelemetry, - writeTelemetry, - wipeDevice, - readAudit, - readAudits, - writeAudit, - deleteAudit, - readAuditEvents, - writeAuditEvent, - readGeographies, - readGeographySummaries, - writeGeography, - publishGeography, - deleteGeography, - isGeographyPublished, - editGeography, - readPolicies, - writePolicy, - readPolicy, - editPolicy, - deletePolicy, - writeGeographyMetadata, - updateGeographyMetadata, - readSingleGeographyMetadata, - readSingleGeography, - readBulkGeographyMetadata, - writePolicyMetadata, - updatePolicyMetadata, - readBulkPolicyMetadata, - readSinglePolicyMetadata, - publishPolicy, - isPolicyPublished, - readRule, - getEventCountsPerProviderSince, - getTelemetryCountsPerProviderSince, - getTripCountsPerProviderSince, - getNumVehiclesRegisteredLast24HoursByProvider, - getMostRecentEventByProvider, - getVehicleCountsPerProvider, - getNumEventsLast24HoursByProvider, - getMostRecentTelemetryByProvider, - getTripEventsLast24HoursByProvider, - getEventsLast24HoursPerProvider, - readEventsWithTelemetry + ...devices, + ...events, + ...policies, + ...geographies, + ...audit, + ...trips, + ...telemetry, + ...stops, + ...attachments } diff --git a/packages/mds-db/migration.ts b/packages/mds-db/migration.ts index 9c5784b26..ede9d08c8 100644 --- a/packages/mds-db/migration.ts +++ b/packages/mds-db/migration.ts @@ -1,6 +1,6 @@ import { csv, now } from '@mds-core/mds-utils' -import log from '@mds-core/mds-logger' +import logger from '@mds-core/mds-logger' import schema, { COLUMN_NAME, TABLE_NAME } from './schema' import { SqlExecuter, MDSPostgresClient, cols_sql, SqlExecuterFunction } from './sql-utils' @@ -10,7 +10,9 @@ const MIGRATIONS = [ 'alterAuditEventsColumns', 'alterPreviousGeographiesColumn', 'dropDeprecatedProviderTables', - 'dropReadOnlyGeographyColumn' + 'dropReadOnlyGeographyColumn', + 'dropAuditEventsColumns', + 'alterReportsTripsMigration' ] as const type MIGRATION = typeof MIGRATIONS[number] @@ -19,7 +21,7 @@ async function dropTables(client: MDSPostgresClient) { const exec = SqlExecuter(client) const drop = csv(schema.DEPRECATED_PROVIDER_TABLES.concat(schema.TABLES)) await exec(`DROP TABLE IF EXISTS ${drop};`) - await log.info(`postgres drop table succeeded: ${drop}`) + logger.info(`postgres drop table succeeded: ${drop}`) } // Add a foreign key if it doesn't already exist @@ -82,7 +84,7 @@ async function createTables(client: MDSPostgresClient) { (table: string) => !existing.rows.find((row: any) => row.table_name === table) ) if (missing.length > 0) { - await log.warn('existing', JSON.stringify(existing.rows), 'missing', JSON.stringify(missing)) + logger.warn('existing', JSON.stringify(existing.rows), 'missing', JSON.stringify(missing)) const create = missing .map( table => @@ -91,9 +93,9 @@ async function createTables(client: MDSPostgresClient) { .join(', ')}, PRIMARY KEY (${csv(schema.TABLE_KEY[table])}));` ) .join('\n') - await log.warn(create) + logger.warn(create) await exec(create) - await log.info('postgres create table suceeded') + logger.info('postgres create table suceeded') await Promise.all(missing.map(table => addIndex(client, table, schema.COLUMN.recorded))) await Promise.all(missing.map(table => addIndex(client, table, schema.COLUMN.id, { unique: true }))) await addForeignKey(client, schema.TABLE.policy_metadata, schema.TABLE.policies, schema.COLUMN.policy_id) @@ -129,11 +131,11 @@ async function doMigration( ) process.env.PG_DEBUG = 'true' try { - await log.warn('Running migration', migration) + logger.warn('Running migration', migration) await migrate(exec) - await log.warn('Migration', migration, 'succeeded') + logger.warn('Migration', migration, 'succeeded') } catch (err) { - await log.error('Migration', migration, 'failed', err) + logger.error('Migration', migration, 'failed', err) } process.env.PG_DEBUG = PG_DEBUG } catch { @@ -157,15 +159,9 @@ async function alterGeographiesColumnsMigration(exec: SqlExecuterFunction) { } async function alterAuditEventsColumnsMigration(exec: SqlExecuterFunction) { - await exec( - `ALTER TABLE ${schema.TABLE.audit_events} ADD COLUMN ${schema.COLUMN.provider_event_id} ${schema.COLUMN_TYPE.provider_event_id}` - ) - await exec( - `ALTER TABLE ${schema.TABLE.audit_events} ADD COLUMN ${schema.COLUMN.provider_event_type} ${schema.COLUMN_TYPE.provider_event_type}` - ) - await exec( - `ALTER TABLE ${schema.TABLE.audit_events} ADD COLUMN ${schema.COLUMN.provider_event_type_reason} ${schema.COLUMN_TYPE.provider_event_type_reason}` - ) + await exec(`ALTER TABLE ${schema.TABLE.audit_events} ADD COLUMN provider_event_id bigint`) + await exec(`ALTER TABLE ${schema.TABLE.audit_events} ADD COLUMN provider_event_type varchar(31)`) + await exec(`ALTER TABLE ${schema.TABLE.audit_events} ADD COLUMN provider_event_type_reason varchar(31)`) } async function alterPreviousGeographiesColumnMigration(exec: SqlExecuterFunction) { @@ -182,16 +178,20 @@ async function dropReadOnlyGeographyColumnMigration(exec: SqlExecuterFunction) { await exec(`ALTER TABLE ${schema.TABLE.geographies} DROP COLUMN read_only`) } +async function dropAuditEventsColumnsMigration(exec: SqlExecuterFunction) { + await exec(`ALTER TABLE ${schema.TABLE.audit_events} DROP COLUMN provider_event_id`) + await exec(`ALTER TABLE ${schema.TABLE.audit_events} DROP COLUMN provider_event_type`) + await exec(`ALTER TABLE ${schema.TABLE.audit_events} DROP COLUMN provider_event_type_reason`) +} + async function doMigrations(client: MDSPostgresClient) { const exec = SqlExecuter(client) - // All migrations go here. createMigrationsTable will never actually run here as it is inserted when the - // migrations table is created, but it is included as it provides a template for how to invoke them. - await doMigration(exec, 'createMigrationsTable', async () => {}) await doMigration(exec, 'alterGeographiesColumns', alterGeographiesColumnsMigration) await doMigration(exec, 'alterAuditEventsColumns', alterAuditEventsColumnsMigration) await doMigration(exec, 'alterPreviousGeographiesColumn', alterPreviousGeographiesColumnMigration) await doMigration(exec, 'dropDeprecatedProviderTables', dropDeprecatedProviderTablesMigration) await doMigration(exec, 'dropReadOnlyGeographyColumn', dropReadOnlyGeographyColumnMigration) + await doMigration(exec, 'dropAuditEventsColumns', dropAuditEventsColumnsMigration) } async function updateSchema(client: MDSPostgresClient) { diff --git a/packages/mds-db/package.json b/packages/mds-db/package.json index 277be0575..1c9f5d1d6 100644 --- a/packages/mds-db/package.json +++ b/packages/mds-db/package.json @@ -1,6 +1,6 @@ { "name": "@mds-core/mds-db", - "version": "0.1.18", + "version": "0.1.26", "description": "Mobility Data Specification database interface", "main": "dist/index.js", "files": [ @@ -13,18 +13,16 @@ "scripts": { "build": "tsc --build tsconfig.build.json", "test": "yarn test:eslint && yarn test:unit", - "test:eslint": "eslint --fix --ignore-path ../../.gitignore '**/*.ts'", - "test:unit": "DOTENV_CONFIG_PATH=../../.env nyc --check-coverage --exclude tests --extension .ts --lines 0 ts-mocha --project ../../tsconfig.json --require tsconfig-paths/register --timeout 6000 --require source-map-support/register --require dotenv/config --recursive --timeout 5000 tests/**/*.ts && nyc report --reporter=html" + "test:eslint": "eslint --ignore-path ../../.gitignore '**/*.ts'", + "test:unit": "DOTENV_CONFIG_PATH=../../.env nyc --lines 65 ts-mocha --project ../../tsconfig.json" }, "author": "City of Los Angeles", "license": "Apache-2.0", "dependencies": { - "@mds-core/mds-logger": "0.1.16", - "@mds-core/mds-types": "0.1.15", - "@mds-core/mds-utils": "0.1.18", - "pg": "7.12.1" - }, - "devDependencies": { - "@mds-core/mds-test-data": "0.1.18" + "@mds-core/mds-logger": "0.1.24", + "@mds-core/mds-types": "0.1.23", + "@mds-core/mds-utils": "0.1.26", + "@types/pg": "7.14.3", + "pg": "8.2.0" } } diff --git a/packages/mds-db/policies.ts b/packages/mds-db/policies.ts index db5fe7f27..d41e73013 100644 --- a/packages/mds-db/policies.ts +++ b/packages/mds-db/policies.ts @@ -1,21 +1,29 @@ -import { UUID, Policy, Timestamp, Recorded, Rule, PolicyMetadata } from '@mds-core/mds-types' -import { now, NotFoundError, BadParamsError, AlreadyPublishedError } from '@mds-core/mds-utils' -import log from '@mds-core/mds-logger' +import { UUID, Policy, Timestamp, Recorded, Rule, PolicyMetadata, Nullable } from '@mds-core/mds-types' +import { + now, + NotFoundError, + BadParamsError, + AlreadyPublishedError, + DependencyMissingError, + ConflictError +} from '@mds-core/mds-utils' +import logger from '@mds-core/mds-logger' import schema from './schema' import { vals_sql, cols_sql, vals_list, SqlVals } from './sql-utils' -import { publishGeography, isGeographyPublished } from './geographies' +import { isGeographyPublished } from './geographies' import { getReadOnlyClient, getWriteableClient } from './client' export async function readPolicies(params?: { policy_id?: UUID + rule_id?: UUID name?: string description?: string start_date?: Timestamp - get_unpublished?: boolean - get_published?: boolean + get_unpublished?: Nullable + get_published?: Nullable }): Promise { // use params to filter // query @@ -31,6 +39,12 @@ export async function readPolicies(params?: { conditions.push(`policy_id = ${vals.add(params.policy_id)}`) } + if (params.rule_id) { + conditions.push( + `EXISTS(SELECT FROM json_array_elements(policy_json->'rules') elem WHERE (elem->'rule_id')::jsonb ? '${params.rule_id}')` + ) + } + if (params.get_unpublished) { conditions.push(`policy_json->>'publish_date' IS NULL`) } @@ -56,13 +70,28 @@ export async function readPolicies(params?: { return res.rows.map(row => row.policy_json) } +export async function readActivePolicies(timestamp: Timestamp = now()): Promise { + const client = await getReadOnlyClient() + const conditions = [] + const vals = new SqlVals() + conditions.push(`policy_json->>'start_date' <= ${vals.add(timestamp)}`) + conditions.push(`(policy_json->>'end_date' >= ${vals.add(timestamp)} OR policy_json->>'end_date' IS NULL)`) + conditions.push( + `(policy_json->>'publish_date' IS NOT NULL AND policy_json->>'publish_date' <= ${vals.add(timestamp)})` + ) + const sql = `select * from ${schema.TABLE.policies} WHERE ${conditions.join(' AND ')}` + const values = vals.values() + const res = await client.query(sql, values) + return res.rows.map(row => row.policy_json) +} + export async function readBulkPolicyMetadata(params?: { policy_id?: UUID name?: string description?: string start_date?: Timestamp - get_unpublished?: boolean - get_published?: boolean + get_unpublished: Nullable + get_published: Nullable }): Promise { const policies = await readPolicies(params) const policy_ids = policies.map(policy => { @@ -90,7 +119,7 @@ export async function readSinglePolicyMetadata(policy_id: UUID): Promise { if (res.rows.length === 1) { return res.rows[0].policy_json } - await log.info(`readPolicy db failed for ${policy_id}: rows=${res.rows.length}`) + logger.info(`readPolicy db failed for ${policy_id}: rows=${res.rows.length}`) throw new NotFoundError(`policy_id ${policy_id} not found`) } +async function throwIfRulesAlreadyExist(policy: Policy) { + const unflattenedPolicies: Policy[][] = await Promise.all( + policy.rules.map(rule => { + return readPolicies({ rule_id: rule.rule_id }) + }) + ) + + unflattenedPolicies.map(policySubArr => { + policySubArr.map(p => { + if (p.policy_id !== policy.policy_id) { + throw new ConflictError(`Policies containing rules with the same id or ids already exist`) + } + }) + }) +} + export async function writePolicy(policy: Policy): Promise> { // validate TODO const client = await getWriteableClient() + await throwIfRulesAlreadyExist(policy) + const sql = `INSERT INTO ${schema.TABLE.policies} (${cols_sql(schema.TABLE_COLUMNS.policies)}) VALUES (${vals_sql( schema.TABLE_COLUMNS.policies )}) RETURNING *` @@ -137,11 +184,11 @@ export async function editPolicy(policy: Policy) { throw new AlreadyPublishedError('Cannot edit published policy') } - const result = await readPolicies({ policy_id, get_unpublished: true }) + const result = await readPolicies({ policy_id, get_unpublished: true, get_published: false }) if (result.length === 0) { throw new NotFoundError(`no policy of id ${policy_id} was found`) } - + await throwIfRulesAlreadyExist(policy) const client = await getWriteableClient() const sql = `UPDATE ${schema.TABLE.policies} SET policy_json=$1 WHERE policy_id='${policy_id}' AND policy_json->>'publish_date' IS NULL` await client.query(sql, [policy]) @@ -166,7 +213,7 @@ export async function publishPolicy(policy_id: UUID) { throw new AlreadyPublishedError('Cannot re-publish existing policy') } - const policy = (await readPolicies({ policy_id, get_unpublished: true }))[0] + const policy = (await readPolicies({ policy_id, get_unpublished: true, get_published: null }))[0] if (!policy) { throw new NotFoundError('cannot publish nonexistent policy') } @@ -179,27 +226,35 @@ export async function publishPolicy(policy_id: UUID) { geographies.push(geography_id) }) }) - await Promise.all( - geographies.map(geography_id => { - log.info('publishing geography', geography_id) - return publishGeography({ geography_id, publish_date }) - }) - ) - await Promise.all( - geographies.map(geography_id => { - const ispublished = isGeographyPublished(geography_id) - log.info('published geography', geography_id, ispublished) + + const unpublishedGeoIDs = await Promise.all( + geographies.map(async geography_id => { + const isPublished = await isGeographyPublished(geography_id) + if (!isPublished) { + return geography_id + } + return null }) ) + unpublishedGeoIDs.forEach(id => { + if (id) { + throw new DependencyMissingError(`Geography with ${id} is not published!`) + } + }) + // Only publish the policy if the geographies are successfully published first - const publishPolicySQL = `UPDATE ${schema.TABLE.policies} SET policy_json = policy_json::jsonb || '{"publish_date": ${publish_date}}' where policy_id='${policy_id}'` - await client.query(publishPolicySQL).catch(err => { + const publishPolicySQL = `UPDATE ${schema.TABLE.policies} + SET policy_json = policy_json::jsonb || '{"publish_date": ${publish_date}}' + where policy_id='${policy_id}' RETURNING *` + const { + rows: [published_policy] + }: { rows: Policy[] } = await client.query(publishPolicySQL).catch(err => { throw err }) - return policy_id + return { ...published_policy } } catch (err) { - await log.error(err) + logger.error(err) throw err } } @@ -237,7 +292,7 @@ export async function updatePolicyMetadata(policy_metadata: PolicyMetadata) { ...recorded_metadata } } catch (err) { - await log.error(err) + logger.error(err) throw err } } @@ -258,3 +313,12 @@ export async function readRule(rule_id: UUID): Promise { return rule } } + +export async function findPoliciesByGeographyID(geography_id: UUID): Promise { + const client = await getReadOnlyClient() + const sql = `select * from ${schema.TABLE.policies} + where ${schema.COLUMN.policy_json}::jsonb + @> '{"rules":[{"geographies":["${geography_id}"]}]}'` + const res = await client.query(sql) + return res.rows.map(row => row.policy_json) +} diff --git a/packages/mds-db/schema.ts b/packages/mds-db/schema.ts index a36eaa4f6..3ebd87f1c 100644 --- a/packages/mds-db/schema.ts +++ b/packages/mds-db/schema.ts @@ -3,6 +3,8 @@ import { Enum } from '@mds-core/mds-types' // TODO providers are in CSV const TABLE = Enum( + 'attachments', + 'audit_attachments', 'audit_events', 'audits', 'devices', @@ -12,6 +14,7 @@ const TABLE = Enum( 'migrations', 'policies', 'policy_metadata', + 'stops', 'telemetry' ) export type TABLE_NAME = keyof typeof TABLE @@ -20,20 +23,27 @@ const DEPRECATED_PROVIDER_TABLES = ['status_changes', 'trips'] const COLUMN = Enum( 'accuracy', + 'address', 'altitude', + 'attachment_filename', + 'attachment_id', 'audit_device_id', 'audit_event_id', 'audit_event_type', 'audit_issue_code', 'audit_subject_id', 'audit_trip_id', + 'base_url', + 'capacity', 'charge', + 'cross_street', 'deleted', 'description', 'device_id', 'effective_date', - 'event_type_reason', + 'end_time', 'event_type', + 'event_type_reason', 'geography_id', 'geography_json', 'geography_metadata', @@ -41,40 +51,66 @@ const COLUMN = Enum( 'id', 'lat', 'lng', - 'migration', + 'location_type', 'mfgr', + 'migration', + 'mimetype', 'model', 'name', 'note', + 'num_spots_available', + 'num_spots_disabled', + 'num_vehicles_available', + 'num_vehicles_disabled', + 'parking_verification_url', + 'platform_code', 'policy_id', 'policy_json', 'policy_metadata', + 'post_code', 'prev_geographies', - 'propulsion_type', 'propulsion', + 'propulsion_type', 'provider_device_id', - 'provider_event_id', - 'provider_event_type', - 'provider_event_type_reason', 'provider_id', 'provider_name', 'provider_vehicle_id', 'publish_date', 'recorded', + 'rental_methods', + 'reservation_cost', 'service_area_id', + 'short_name', 'speed', + 'stop_id', + 'stop_name', 'telemetry_timestamp', + 'thumbnail_filename', + 'thumbnail_mimetype', 'timestamp', + 'timezone', 'trip_id', 'type', 'vehicle_id', - 'vehicle_type', - 'year' + 'wheelchair_boarding', + 'year', + 'zone_id' ) export type COLUMN_NAME = keyof typeof COLUMN const COLUMNS = Object.keys(COLUMN) as COLUMN_NAME[] const TABLE_COLUMNS: { [T in TABLE_NAME]: Readonly } = { + [TABLE.attachments]: [ + COLUMN.id, + COLUMN.attachment_filename, + COLUMN.attachment_id, + COLUMN.base_url, + COLUMN.mimetype, + COLUMN.thumbnail_filename, + COLUMN.thumbnail_mimetype, + COLUMN.recorded + ], + [TABLE.audit_attachments]: [COLUMN.id, COLUMN.attachment_id, COLUMN.audit_trip_id, COLUMN.recorded], [TABLE.audits]: [ COLUMN.id, COLUMN.audit_trip_id, @@ -96,9 +132,6 @@ const TABLE_COLUMNS: { [T in TABLE_NAME]: Readonly } = { COLUMN.audit_issue_code, COLUMN.audit_subject_id, COLUMN.note, - COLUMN.provider_event_id, - COLUMN.provider_event_type, - COLUMN.provider_event_type_reason, COLUMN.timestamp, COLUMN.lat, COLUMN.lng, @@ -160,12 +193,37 @@ const TABLE_COLUMNS: { [T in TABLE_NAME]: Readonly } = { COLUMN.altitude, COLUMN.charge, COLUMN.recorded + ], + [TABLE.stops]: [ + COLUMN.id, + COLUMN.stop_id, + COLUMN.stop_name, + COLUMN.short_name, + COLUMN.platform_code, + COLUMN.geography_id, + COLUMN.zone_id, + COLUMN.address, + COLUMN.post_code, + COLUMN.rental_methods, + COLUMN.capacity, + COLUMN.location_type, + COLUMN.timezone, + COLUMN.cross_street, + COLUMN.num_vehicles_available, + COLUMN.num_vehicles_disabled, + COLUMN.num_spots_available, + COLUMN.num_spots_disabled, + COLUMN.wheelchair_boarding, + COLUMN.reservation_cost, + COLUMN.recorded ] } const TABLE_KEY: { [T in TABLE_NAME]: COLUMN_NAME[] } = { - [TABLE.audits]: [COLUMN.audit_trip_id], + [TABLE.attachments]: [COLUMN.attachment_id], + [TABLE.audit_attachments]: [COLUMN.attachment_id, COLUMN.audit_trip_id], [TABLE.audit_events]: [COLUMN.audit_trip_id, COLUMN.timestamp], + [TABLE.audits]: [COLUMN.audit_trip_id], [TABLE.devices]: [COLUMN.device_id], [TABLE.events]: [COLUMN.device_id, COLUMN.timestamp], [TABLE.geographies]: [COLUMN.geography_id], @@ -173,25 +231,33 @@ const TABLE_KEY: { [T in TABLE_NAME]: COLUMN_NAME[] } = { [TABLE.migrations]: [COLUMN.migration], [TABLE.policies]: [COLUMN.policy_id], [TABLE.policy_metadata]: [COLUMN.policy_id], + [TABLE.stops]: [COLUMN.stop_id], [TABLE.telemetry]: [COLUMN.device_id, COLUMN.timestamp] } const COLUMN_TYPE: { [C in COLUMN_NAME]: string } = { [COLUMN.accuracy]: 'real', + [COLUMN.address]: 'varchar(255)', [COLUMN.altitude]: 'real', + [COLUMN.attachment_filename]: 'varchar(64) NOT NULL', + [COLUMN.attachment_id]: 'uuid NOT NULL', [COLUMN.audit_device_id]: 'uuid NOT NULL', [COLUMN.audit_event_id]: 'uuid NOT NULL', [COLUMN.audit_event_type]: 'varchar(31) NOT NULL', [COLUMN.audit_issue_code]: 'varchar(31)', [COLUMN.audit_subject_id]: 'varchar(255) NOT NULL', [COLUMN.audit_trip_id]: 'uuid NOT NULL', + [COLUMN.base_url]: 'varchar(127) NOT NULL', + [COLUMN.capacity]: 'jsonb', [COLUMN.charge]: 'real', + [COLUMN.cross_street]: 'varchar(255)', [COLUMN.deleted]: 'bigint', [COLUMN.description]: 'varchar(255)', [COLUMN.device_id]: 'uuid NOT NULL', [COLUMN.effective_date]: 'bigint', + [COLUMN.end_time]: 'bigint', + [COLUMN.event_type]: 'varchar(31)', [COLUMN.event_type_reason]: 'varchar(31)', - [COLUMN.event_type]: 'varchar(31) NOT NULL', [COLUMN.geography_id]: 'uuid NOT NULL', [COLUMN.geography_json]: 'json NOT NULL', [COLUMN.geography_metadata]: 'json', @@ -199,35 +265,50 @@ const COLUMN_TYPE: { [C in COLUMN_NAME]: string } = { [COLUMN.id]: 'bigint GENERATED ALWAYS AS IDENTITY', [COLUMN.lat]: 'double precision NOT NULL', [COLUMN.lng]: 'double precision NOT NULL', - [COLUMN.migration]: 'varchar(255) NOT NULL', + [COLUMN.location_type]: 'varchar(255)', [COLUMN.mfgr]: 'varchar(127)', + [COLUMN.migration]: 'varchar(255) NOT NULL', + [COLUMN.mimetype]: 'varchar(255) NOT NULL', [COLUMN.model]: 'varchar(127)', [COLUMN.name]: 'varchar(255)', [COLUMN.note]: 'varchar(255)', + [COLUMN.num_spots_available]: 'jsonb NOT NULL', + [COLUMN.num_spots_disabled]: 'jsonb', + [COLUMN.num_vehicles_available]: 'jsonb NOT NULL', + [COLUMN.num_vehicles_disabled]: 'jsonb', + [COLUMN.parking_verification_url]: 'varchar(255)', + [COLUMN.platform_code]: 'varchar(255)', [COLUMN.policy_id]: 'uuid NOT NULL', [COLUMN.policy_json]: 'json NOT NULL', [COLUMN.policy_metadata]: 'json', - [COLUMN.publish_date]: 'bigint', + [COLUMN.post_code]: 'varchar(255)', [COLUMN.prev_geographies]: 'uuid[]', - [COLUMN.propulsion_type]: 'varchar(31)[] NOT NULL', [COLUMN.propulsion]: 'varchar(31)[] NOT NULL', + [COLUMN.propulsion_type]: 'varchar(31)[] NOT NULL', [COLUMN.provider_device_id]: 'uuid', // May be null if can't find - [COLUMN.provider_event_id]: 'bigint', - [COLUMN.provider_event_type]: 'varchar(31)', - [COLUMN.provider_event_type_reason]: 'varchar(31)', [COLUMN.provider_id]: 'uuid NOT NULL', [COLUMN.provider_name]: 'varchar(127) NOT NULL', [COLUMN.provider_vehicle_id]: 'varchar(255) NOT NULL', + [COLUMN.publish_date]: 'bigint', [COLUMN.recorded]: 'bigint NOT NULL', // timestamp of when record was created + [COLUMN.rental_methods]: 'varchar(255)', + [COLUMN.reservation_cost]: 'jsonb', [COLUMN.service_area_id]: 'uuid', + [COLUMN.short_name]: 'varchar(31)', [COLUMN.speed]: 'real', + [COLUMN.stop_id]: 'uuid NOT NULL', + [COLUMN.stop_name]: 'varchar(255) NOT NULL', [COLUMN.telemetry_timestamp]: 'bigint', + [COLUMN.thumbnail_filename]: 'varchar(64)', + [COLUMN.thumbnail_mimetype]: 'varchar(64)', [COLUMN.timestamp]: 'bigint NOT NULL', + [COLUMN.timezone]: 'varchar(255)', [COLUMN.trip_id]: 'uuid', [COLUMN.type]: 'varchar(31) NOT NULL', [COLUMN.vehicle_id]: 'varchar(255) NOT NULL', - [COLUMN.vehicle_type]: 'varchar(31) NOT NULL', - [COLUMN.year]: 'smallint' + [COLUMN.wheelchair_boarding]: 'bool DEFAULT FALSE', + [COLUMN.year]: 'smallint', + [COLUMN.zone_id]: 'varchar(255)' } export default { diff --git a/packages/mds-db/sql-utils.ts b/packages/mds-db/sql-utils.ts index 6c90d6230..90dcc1123 100644 --- a/packages/mds-db/sql-utils.ts +++ b/packages/mds-db/sql-utils.ts @@ -1,7 +1,7 @@ // /////////////////////////////// SQL-related utilities ///////////////////////////// import { Client as PostgresClient, types as PostgresTypes } from 'pg' import { csv } from '@mds-core/mds-utils' -import log from '@mds-core/mds-logger' +import logger from '@mds-core/mds-logger' import schema from './schema' export interface PGInfo { @@ -61,15 +61,15 @@ export function configureClient(pg_info: PGInfo) { client.on('end', () => { client.setConnected(false) - log.info('disconnected', client.client_type, 'client from postgres') + logger.info('disconnected', client.client_type, 'client from postgres') }) client.on('error', async err => { - await log.error('pg client error event', err.stack) + logger.error('pg client error event', err.stack) }) client.on('notice', async msg => { - await log.warn('notice:', msg) + logger.warn('notice:', msg) }) if (!client) { @@ -156,7 +156,7 @@ export async function logSql(sql: string, ...values: unknown[]): Promise { out = values } - log.info('sql>', sql, out) + logger.info('sql>', sql, out) } export class SqlVals { @@ -187,3 +187,7 @@ export const SqlExecuter = (client: MDSPostgresClient) => async (command: string } export type SqlExecuterFunction = ReturnType + +export function arrayToInQueryFormat(arr: unknown[]) { + return `(${arr.map(currElem => `"${currElem}"`)})` +} diff --git a/packages/mds-db/stops.ts b/packages/mds-db/stops.ts new file mode 100644 index 000000000..e29deef34 --- /dev/null +++ b/packages/mds-db/stops.ts @@ -0,0 +1,42 @@ +import { Recorded, Stop, UUID } from '@mds-core/mds-types' +import { now } from '@mds-core/mds-utils' + +import schema from './schema' + +import { vals_sql, cols_sql, vals_list, logSql, SqlVals } from './sql-utils' + +import { getWriteableClient, getReadOnlyClient } from './client' + +export async function writeStop(stop: Stop): Promise> { + const client = await getWriteableClient() + const sql = `INSERT INTO ${schema.TABLE.stops} (${cols_sql(schema.TABLE_COLUMNS.stops)}) VALUES (${vals_sql( + schema.TABLE_COLUMNS.stops + )}) RETURNING *` + const values = vals_list(schema.TABLE_COLUMNS.stops, { ...stop, recorded: now() }) + await logSql(sql, values) + const { + rows: [recorded_stop] + }: { rows: Recorded[] } = await client.query(sql, values) + return { ...stop, ...recorded_stop } +} + +export async function readStop(stop_id: UUID): Promise> { + const client = await getReadOnlyClient() + const vals = new SqlVals() + const sql = `SELECT * FROM ${schema.TABLE.stops} WHERE ${schema.COLUMN.stop_id} = ${vals.add(stop_id)}` + const values = vals.values() + await logSql(sql, values) + const { + rows: [recorded_stop] + }: { rows: Recorded[] } = await client.query(sql, values) + return recorded_stop +} + +export async function readStops(): Promise[]> { + const client = await getReadOnlyClient() + const sql = `SELECT * FROM ${schema.TABLE.stops}` + await logSql(sql) + const { rows } = await client.query(sql) + + return rows +} diff --git a/packages/mds-db/telemetry.ts b/packages/mds-db/telemetry.ts index 71f1363a4..fca43b422 100644 --- a/packages/mds-db/telemetry.ts +++ b/packages/mds-db/telemetry.ts @@ -1,13 +1,6 @@ import { UUID, Recorded, Telemetry, Timestamp } from '@mds-core/mds-types' -import { - convertTelemetryToTelemetryRecord, - convertTelemetryRecordToTelemetry, - now, - csv, - days, - yesterday -} from '@mds-core/mds-utils' -import log from '@mds-core/mds-logger' +import { now, csv, days, yesterday } from '@mds-core/mds-utils' +import logger from '@mds-core/mds-logger' import { TelemetryRecord } from './types' import schema from './schema' @@ -16,6 +9,32 @@ import { cols_sql, vals_list, SqlVals, logSql, to_sql } from './sql-utils' import { getReadOnlyClient, getWriteableClient, makeReadOnlyQuery } from './client' +export function convertTelemetryToTelemetryRecord(telemetry: Telemetry): TelemetryRecord { + const { + gps: { lat, lng, altitude, heading, speed, accuracy }, + recorded = now(), + ...props + } = telemetry + return { + ...props, + lat, + lng, + altitude, + heading, + speed, + accuracy, + recorded + } +} + +export function convertTelemetryRecordToTelemetry(telemetryRecord: TelemetryRecord): Telemetry { + const { lat, lng, altitude, heading, speed, accuracy, ...props } = telemetryRecord + return { + ...props, + gps: { lat, lng, altitude, heading, speed, accuracy } + } +} + export async function writeTelemetry(telemetries: Telemetry[]): Promise[]> { if (telemetries.length === 0) { return [] @@ -40,7 +59,7 @@ export async function writeTelemetry(telemetries: Telemetry[]): Promise= 300) { - await log.info( + logger.info( `pg db writeTelemetry ${telemetries.length} rows, success in ${delta} ms with ${recorded_telemetries.length} unique` ) } @@ -56,7 +75,7 @@ export async function writeTelemetry(telemetries: Telemetry[]): Promise ) } catch (err) { - await log.error('pg write telemetry error', err) + logger.error('pg write telemetry error', err) throw err } } @@ -88,7 +107,7 @@ export async function readTelemetry( return convertTelemetryRecordToTelemetry(row) as Recorded }) } catch (err) { - await log.error('read telemetry error', err) + logger.error('read telemetry error', err) throw err } } @@ -98,11 +117,16 @@ export async function getTelemetryCountsPerProviderSince( stop = now() ): Promise<{ provider_id: UUID; count: number; slacount: number }[]> { const one_day = days(1) - const sql = `select provider_id, count(*), count(case when ((recorded-timestamp) > ${one_day}) then 1 else null end) as slacount from telemetry where recorded > ${start} and recorded < ${stop} group by provider_id` - return makeReadOnlyQuery(sql) + const vals = new SqlVals() + const sql = `select provider_id, count(*), count(case when ((recorded-timestamp) > ${vals.add( + one_day + )}) then 1 else null end) as slacount from telemetry where recorded > ${vals.add(start)} and recorded < ${vals.add( + stop + )} group by provider_id` + return makeReadOnlyQuery(sql, vals) } -// TODO way too slow to be useful -- move into mds-cache +// TODO way too slow to be useful -- move into mds-agency-cache export async function getMostRecentTelemetryByProvider(): Promise<{ provider_id: UUID; max: number }[]> { const sql = `select provider_id, max(recorded) from ${schema.TABLE.telemetry} group by provider_id` return makeReadOnlyQuery(sql) diff --git a/packages/mds-db/tests/mds-db.spec.ts b/packages/mds-db/tests/mds-db.spec.ts index 1530a0582..7745229ac 100644 --- a/packages/mds-db/tests/mds-db.spec.ts +++ b/packages/mds-db/tests/mds-db.spec.ts @@ -2,7 +2,6 @@ import assert from 'assert' /* eslint-reason extends object.prototype */ /* eslint-disable-next-line @typescript-eslint/no-unused-vars */ import should from 'should' - import { FeatureCollection } from 'geojson' import { Telemetry, Recorded, VehicleEvent, Device, VEHICLE_EVENTS, Geography } from '@mds-core/mds-types' import { @@ -10,7 +9,6 @@ import { makeDevices, makeEventsWithTelemetry, makeEvents, - makeTrip, JUMP_PROVIDER_ID, POLICY_JSON, POLICY2_JSON, @@ -18,13 +16,14 @@ import { GEOGRAPHY_UUID, GEOGRAPHY2_UUID, LA_CITY_BOUNDARY, - DISTRICT_SEVEN + DISTRICT_SEVEN, + START_ONE_MONTH_AGO, + POLICY_WITH_DUPE_RULE, + PUBLISHED_POLICY } from '@mds-core/mds-test-data' -import { now, clone, NotFoundError } from '@mds-core/mds-utils' - +import { now, clone, NotFoundError, rangeRandomInt, uuid, ConflictError } from '@mds-core/mds-utils' import { isNullOrUndefined } from 'util' import MDSDBPostgres from '../index' - import { dropTables, createTables, updateSchema } from '../migration' import { Trip } from '../types' import { configureClient, MDSPostgresClient, PGInfo } from '../sql-utils' @@ -52,6 +51,42 @@ const DistrictSeven: Geography = { geography_json: DISTRICT_SEVEN } +function makeTrip(device: Device): Trip { + return { + provider_id: device.provider_id, + provider_name: device.provider_id, + device_id: device.device_id, + vehicle_id: device.vehicle_id, + vehicle_type: device.type, + propulsion_type: device.propulsion, + provider_trip_id: uuid(), + trip_duration: rangeRandomInt(5), + trip_distance: rangeRandomInt(5), + route: { + type: 'FeatureCollection', + features: [ + { + type: 'Feature', + properties: { + timestamp: now() + }, + geometry: { + type: 'Point', + coordinates: [Math.random() * 10, Math.random() * 10] + } + } + ] + }, + accuracy: Math.random() * 3, + trip_start: now() - 1000 * Math.random(), + trip_end: now(), + parking_verification_url: 'http://iamverified.com', + standard_cost: rangeRandomInt(5), + actual_cost: rangeRandomInt(5), + recorded: now() + } +} + /* You'll need postgres running and the env variable PG_NAME * to be set to run these tests. */ @@ -257,7 +292,11 @@ if (pg_info.database) { await MDSDBPostgres.writePolicy(POLICY_JSON) assert(!(await MDSDBPostgres.isPolicyPublished(policy_id))) await MDSDBPostgres.deletePolicy(policy_id) - const policy_result = await MDSDBPostgres.readPolicies({ policy_id }) + const policy_result = await MDSDBPostgres.readPolicies({ + policy_id, + get_published: null, + get_unpublished: null + }) assert.deepEqual(policy_result, []) }) @@ -268,23 +307,43 @@ if (pg_info.database) { await MDSDBPostgres.writePolicy(POLICY3_JSON) await MDSDBPostgres.writeGeography(LAGeography) + await MDSDBPostgres.publishGeography({ geography_id: LAGeography.geography_id }) await MDSDBPostgres.publishPolicy(POLICY_JSON.policy_id) // Read all policies, no matter whether published or not. const policies = await MDSDBPostgres.readPolicies() assert.deepEqual(policies.length, 3) - const unpublishedPolicies = await MDSDBPostgres.readPolicies({ get_unpublished: true }) + const unpublishedPolicies = await MDSDBPostgres.readPolicies({ get_unpublished: true, get_published: null }) assert.deepEqual(unpublishedPolicies.length, 2) - const publishedPolicies = await MDSDBPostgres.readPolicies({ get_published: true }) + const publishedPolicies = await MDSDBPostgres.readPolicies({ get_published: true, get_unpublished: null }) assert.deepEqual(publishedPolicies.length, 1) }) + it('can retrieve Policies that were active at a particular date', async () => { + await MDSDBPostgres.writePolicy(PUBLISHED_POLICY) + const monthAgoPolicies = await MDSDBPostgres.readActivePolicies(START_ONE_MONTH_AGO) + assert.deepEqual(monthAgoPolicies.length, 1) + + const currentlyActivePolicies = await MDSDBPostgres.readActivePolicies() + assert.deepEqual(currentlyActivePolicies.length, 2) + }) + it('can read a single Policy', async () => { const policy = await MDSDBPostgres.readPolicy(POLICY_JSON.policy_id) assert.deepEqual(policy.policy_id, POLICY_JSON.policy_id) assert.deepEqual(policy.name, POLICY_JSON.name) }) + it('can find Policies by rule id', async () => { + const rule_id = '7ea0d16e-ad15-4337-9722-9924e3af9146' + const policies = await MDSDBPostgres.readPolicies({ rule_id }) + assert(policies[0].rules.map(rule => rule.rule_id).includes(rule_id)) + }) + + it('ensures rules are unique when writing new policy', async () => { + await MDSDBPostgres.writePolicy(POLICY_WITH_DUPE_RULE).should.be.rejectedWith(ConflictError) + }) + it('cannot find a nonexistent Policy', async () => { await MDSDBPostgres.readPolicy('incrediblefailure').should.be.rejected() }) @@ -292,18 +351,28 @@ if (pg_info.database) { it('can tell a Policy is published', async () => { const publishedResult = await MDSDBPostgres.isPolicyPublished(POLICY_JSON.policy_id) assert.deepEqual(publishedResult, true) - const unpublishedResult = await MDSDBPostgres.isPolicyPublished(POLICY2_JSON.policy_id) + const unpublishedResult = await MDSDBPostgres.isPolicyPublished(POLICY3_JSON.policy_id) assert.deepEqual(unpublishedResult, false) }) it('can edit a Policy', async () => { - const policy = clone(POLICY2_JSON) + const policy = clone(POLICY3_JSON) policy.name = 'a shiny new name' await MDSDBPostgres.editPolicy(policy) - const result = await MDSDBPostgres.readPolicies({ policy_id: POLICY2_JSON.policy_id, get_unpublished: true }) + const result = await MDSDBPostgres.readPolicies({ + policy_id: POLICY3_JSON.policy_id, + get_unpublished: true, + get_published: null + }) assert.deepEqual(result[0].name, 'a shiny new name') }) + it('cannot add a rule that already exists in some other policy', async () => { + const policy = clone(POLICY3_JSON) + policy.rules[0].rule_id = POLICY_JSON.rules[0].rule_id + await MDSDBPostgres.editPolicy(policy).should.be.rejectedWith(ConflictError) + }) + it('will not edit or delete a published Policy', async () => { const publishedPolicy = clone(POLICY_JSON) publishedPolicy.name = 'a shiny new name' @@ -347,7 +416,11 @@ if (pg_info.database) { const noParamsResult = await MDSDBPostgres.readBulkPolicyMetadata() assert.deepEqual(noParamsResult.length, 3) - const withStartDateResult = await MDSDBPostgres.readBulkPolicyMetadata({ start_date: now() }) + const withStartDateResult = await MDSDBPostgres.readBulkPolicyMetadata({ + start_date: now(), + get_published: null, + get_unpublished: null + }) assert.deepEqual(withStartDateResult.length, 1) assert.deepEqual(withStartDateResult[0].policy_metadata.name, 'policy3_json') }) @@ -380,11 +453,14 @@ if (pg_info.database) { assert.deepEqual(result.geography_json, LAGeography.geography_json) assert.deepEqual(result.geography_id, LAGeography.geography_id) - const noGeos = await MDSDBPostgres.readGeographies({ get_read_only: true }) + const noGeos = await MDSDBPostgres.readGeographies({ get_published: true }) assert.deepEqual(noGeos.length, 0) - await MDSDBPostgres.publishGeography({ geography_id: LAGeography.geography_id, publish_date: now() }) - const writeableGeographies = await MDSDBPostgres.readGeographies({ get_read_only: false }) + await MDSDBPostgres.publishGeography({ + geography_id: LAGeography.geography_id, + publish_date: now() + }) + const writeableGeographies = await MDSDBPostgres.readGeographies({ get_published: false }) assert.deepEqual(writeableGeographies.length, 1) }) @@ -396,17 +472,30 @@ if (pg_info.database) { assert.deepEqual(unpublishedResult, false) }) + it('.readGeographies understands all its parameters', async () => { + const publishedResult = await MDSDBPostgres.readGeographies({ get_published: true }) + assert.deepEqual(publishedResult.length, 1) + assert.deepEqual(!!publishedResult[0].publish_date, true) + const unpublishedResult = await MDSDBPostgres.readGeographies({ get_unpublished: true }) + assert.deepEqual(unpublishedResult.length, 1) + assert.deepEqual(!!unpublishedResult[0].publish_date, false) + const withIDsResult = await MDSDBPostgres.readGeographies({ geography_ids: [LAGeography.geography_id] }) + assert.deepEqual(withIDsResult.length, 1) + assert.deepEqual(withIDsResult[0].geography_id, LAGeography.geography_id) + }) + it('can edit a Geography', async () => { const geography_json = clone(DistrictSeven.geography_json) const numFeatures = geography_json.features.length geography_json.features = [] await MDSDBPostgres.editGeography({ - name: 'District Seven', + name: 'District Seven Updated Name', geography_id: DistrictSeven.geography_id, geography_json }) const result = await MDSDBPostgres.readSingleGeography(GEOGRAPHY2_UUID) assert.notEqual(result.geography_json.features.length, numFeatures) + assert.equal(result.name, 'District Seven Updated Name') assert.equal(result.geography_json.features.length, 0) }) @@ -439,21 +528,33 @@ if (pg_info.database) { await MDSDBPostgres.shutdown() }) - it('will publish a Geography if a Policy is published', async () => { + it('will throw an error if an attempt is made to publish a Policy but the Geography is unpublished', async () => { await MDSDBPostgres.writeGeography(LAGeography) await MDSDBPostgres.writeGeography(DistrictSeven) assert(!(await MDSDBPostgres.isGeographyPublished(DistrictSeven.geography_id))) assert(!(await MDSDBPostgres.isGeographyPublished(LAGeography.geography_id))) - await MDSDBPostgres.writePolicy(POLICY3_JSON) - await MDSDBPostgres.publishPolicy(POLICY3_JSON.policy_id) - assert(await MDSDBPostgres.isGeographyPublished(DistrictSeven.geography_id)) - assert(await MDSDBPostgres.isGeographyPublished(LAGeography.geography_id)) - const policy = await MDSDBPostgres.readPolicy(POLICY3_JSON.policy_id) - const geography = await MDSDBPostgres.readSingleGeography(DistrictSeven.geography_id) + await assert.rejects( + async () => { + await MDSDBPostgres.publishPolicy(POLICY3_JSON.policy_id) + }, + { name: 'DependencyMissingError' } + ) + }) + + it('can find policies using geographies by geography ID', async () => { + const policies = await MDSDBPostgres.findPoliciesByGeographyID(LAGeography.geography_id) + assert.deepEqual(policies[0].policy_id, POLICY3_JSON.policy_id) + }) - assert.deepEqual(policy.publish_date, geography.publish_date) + it('throws if both get_published and get_unpublished are true for bulk geo reads', async () => { + await assert.rejects( + async () => { + await MDSDBPostgres.readGeographies({ get_published: true, get_unpublished: true }) + }, + { name: 'BadParamsError' } + ) }) }) @@ -471,25 +572,88 @@ if (pg_info.database) { geography_id: GEOGRAPHY_UUID, geography_metadata: { foo: 'afoo' } } - try { - await MDSDBPostgres.writeGeographyMetadata(geographyMetadata) - throw new Error('Should have thrown') - } catch (err) { - await MDSDBPostgres.writeGeography(LAGeography) - await MDSDBPostgres.writeGeographyMetadata(geographyMetadata) - const geographyMetadataResult = await MDSDBPostgres.readSingleGeographyMetadata(GEOGRAPHY_UUID) - assert.deepEqual(geographyMetadataResult, geographyMetadata) - } + await assert.rejects( + async () => { + await MDSDBPostgres.writeGeographyMetadata(geographyMetadata) + }, + { name: 'DependencyMissingError' } + ) + await MDSDBPostgres.writeGeography(LAGeography) + await MDSDBPostgres.writeGeographyMetadata(geographyMetadata) + const geographyMetadataResult = await MDSDBPostgres.readSingleGeographyMetadata(GEOGRAPHY_UUID) + assert.deepEqual(geographyMetadataResult, geographyMetadata) }) it('can do bulk GeographyMetadata reads', async () => { const all = await MDSDBPostgres.readBulkGeographyMetadata() assert.deepEqual(all.length, 1) - const readOnlyResult = await MDSDBPostgres.readBulkGeographyMetadata({ get_read_only: true }) + const readOnlyResult = await MDSDBPostgres.readBulkGeographyMetadata({ + get_published: true, + get_unpublished: false + }) assert.deepEqual(readOnlyResult.length, 0) - const notReadOnlyResult = await MDSDBPostgres.readBulkGeographyMetadata({ get_read_only: false }) + const notReadOnlyResult = await MDSDBPostgres.readBulkGeographyMetadata({ + get_published: null, + get_unpublished: null + }) assert.deepEqual(notReadOnlyResult.length, 1) }) + + it('updates GeographyMetadata', async () => { + const geographyMetadata = { + geography_id: GEOGRAPHY_UUID, + geography_metadata: { foo: 'notafoo' } + } + const res = await MDSDBPostgres.updateGeographyMetadata(geographyMetadata) + assert.deepEqual(res.geography_metadata.foo, 'notafoo') + }) + + it('deletes GeographyMetadata', async () => { + await MDSDBPostgres.deleteGeographyMetadata(GEOGRAPHY_UUID) + await assert.rejects( + async () => { + await MDSDBPostgres.readSingleGeographyMetadata(GEOGRAPHY_UUID) + }, + { name: 'NotFoundError' } + ) + }) }) }) + + /* + TODO: finalize query semantics, then re-enable + it('Queries metrics correctly', async () => { + const fakeReadOnly = Sinon.fake.returns('boop') + Sinon.replace(dbClient, 'makeReadOnlyQuery', fakeReadOnly) + const start_time = 42 + const end_time = 50 + const provider_id: UUID[] = [] + const geography_id = null + const vehicle_type: VEHICLE_TYPE[] = [] + await getAllMetrics({ start_time, end_time, provider_id, geography_id, vehicle_type }) + assert.strictEqual( + fakeReadOnly.args[0][0], + `SELECT * FROM reports_providers WHERE start_time BETWEEN ${start_time} AND ${end_time}` + ) + Sinon.restore() + }) + + it('Queries optional fields correctly', async () => { + const fakeReadOnly = Sinon.fake.returns('boop') + Sinon.replace(dbClient, 'makeReadOnlyQuery', fakeReadOnly) + const start_time = 42 + const end_time = 50 + const provider_id = [uuid(), uuid()] + const geography_id = uuid() + const vehicle_type = [VEHICLE_TYPES.scooter] + await getAllMetrics({ start_time, end_time, provider_id, geography_id, vehicle_type }) + assert.strictEqual( + fakeReadOnly.args[0][0], + `SELECT * FROM reports_providers WHERE start_time BETWEEN ${start_time} AND ${end_time} AND provider_id IN ${arrayToInQueryFormat( + provider_id + )} AND geography_id = "${geography_id}" AND vehicle_type IN ${arrayToInQueryFormat(vehicle_type)} ` + ) + Sinon.restore() + }) + */ } diff --git a/packages/mds-db/tests/migration.spec.ts b/packages/mds-db/tests/migration.spec.ts index daea0ba5d..58f80ebf3 100644 --- a/packages/mds-db/tests/migration.spec.ts +++ b/packages/mds-db/tests/migration.spec.ts @@ -2,7 +2,7 @@ import sinon from 'sinon' import test from 'unit.js' import assert from 'assert' -import log from '@mds-core/mds-logger' +import logger from '@mds-core/mds-logger' import { updateSchema, createTables, dropTables } from '../migration' import schema from '../schema' import { PGInfo, configureClient } from '../sql-utils' @@ -67,7 +67,10 @@ if (pg_info.database) { .sort() test.array(table_names).is(schema.TABLES.sort()) const indices_result = await client.query(`SELECT tablename FROM pg_indexes WHERE indexdef like '%idx_recorded%'`) - const indices = indices_result.rows.map((row: DBRow) => row.tablename).sort() + const indices = indices_result.rows + .map((row: DBRow) => row.tablename) + .filter(table => (schema.TABLES as string[]).includes(table)) + .sort() test .array(indices) .is(schema.TABLES.sort().filter(table => schema.TABLE_COLUMNS[table].includes(schema.COLUMN.recorded))) @@ -135,9 +138,9 @@ if (pg_info.database) { assert.ok(err instanceof Error) assert.deepStrictEqual(err.message, 'err') } - log.info('hangingon') + logger.info('hangingon') await client.end() - log.info('hangingon') + logger.info('hangingon') }) }) } diff --git a/packages/mds-db/trips.ts b/packages/mds-db/trips.ts index 7cc95c5c2..3be4ad732 100644 --- a/packages/mds-db/trips.ts +++ b/packages/mds-db/trips.ts @@ -1,22 +1,92 @@ -import { UUID, VEHICLE_EVENT } from '@mds-core/mds-types' +import { UUID, Timestamp, VEHICLE_EVENT } from '@mds-core/mds-types' import { now, yesterday } from '@mds-core/mds-utils' +import logger from '@mds-core/mds-logger' import schema from './schema' -import { makeReadOnlyQuery } from './client' +import { logSql, SqlVals } from './sql-utils' + +import { getReadOnlyClient, makeReadOnlyQuery } from './client' + +export interface ReadTripIdsResult { + count: number + tripIds: UUID[] +} + +export interface ReadTripIdsQueryParams { + skip: number + take: number + device_id: UUID + min_end_time: Timestamp + max_end_time: Timestamp +} + +export async function readTripIds(params: Partial = {}): Promise { + const { skip, take, device_id, min_end_time, max_end_time } = params + + const client = await getReadOnlyClient() + + if (typeof skip !== 'number' || skip < 0) { + throw new Error('requires integer skip') + } + if (typeof take !== 'number' || skip < 0) { + throw new Error('requires integer take') + } + + const vals = new SqlVals() + const conditions = [`event_type = 'trip_end'`] + if (max_end_time) { + conditions.push(`"timestamp" <= ${vals.add(Number(max_end_time))}`) + } + if (min_end_time) { + conditions.push(`"timestamp" >= ${vals.add(Number(min_end_time))}`) + } + if (device_id) { + conditions.push(`device_id = ${vals.add(device_id)}`) + } + + const condSql = conditions.join(' AND ') + + try { + const countSql = `SELECT COUNT(*) FROM ${schema.TABLE.events} WHERE ${condSql}` + const countVals = vals.values() + await logSql(countSql, countVals) + const res = await client.query(countSql, countVals) + const count = parseInt(res.rows[0].count) + const selectSql = `SELECT * FROM ${schema.TABLE.events} WHERE ${condSql} ORDER BY "timestamp" OFFSET ${vals.add( + skip + )} LIMIT ${vals.add(take)}` + const selectVals = vals.values() + await logSql(selectSql, selectVals) + const res2 = await client.query(selectSql, selectVals) + return { + tripIds: res2.rows.map(row => row.trip_id), + count + } + } catch (err) { + logger.error('readTripIds error', err) + throw err + } +} export async function getTripCountsPerProviderSince( start = yesterday(), stop = now() ): Promise<{ provider_id: string; count: number }[]> { - const sql = `select provider_id, count(event_type) from events where event_type='trip_end' and recorded > ${start} and recorded < ${stop} group by provider_id, event_type` - return makeReadOnlyQuery(sql) + const vals = new SqlVals() + const sql = `select provider_id, count(event_type) from events where event_type='trip_end' and recorded > ${vals.add( + start + )} and recorded < ${vals.add(stop)} group by provider_id, event_type` + return makeReadOnlyQuery(sql, vals) } export async function getTripEventsLast24HoursByProvider( start = yesterday(), stop = now() ): Promise<{ provider_id: UUID; trip_id: UUID; event_type: VEHICLE_EVENT; recorded: number; timestamp: number }[]> { - const sql = `select provider_id, trip_id, event_type, recorded, timestamp from ${schema.TABLE.events} where trip_id is not null and recorded > ${start} and recorded < ${stop} order by "timestamp"` - return makeReadOnlyQuery(sql) + const vals = new SqlVals() + const sql = `select provider_id, trip_id, event_type, recorded, timestamp from ${ + schema.TABLE.events + } where trip_id is not null and recorded > ${vals.add(start)} and recorded < ${vals.add(stop)} order by "timestamp"` + return makeReadOnlyQuery(sql, vals) } diff --git a/packages/mds-db/tsconfig.eslint.json b/packages/mds-db/tsconfig.eslint.json new file mode 100644 index 000000000..2a400942a --- /dev/null +++ b/packages/mds-db/tsconfig.eslint.json @@ -0,0 +1 @@ +{ "extends": "../../tsconfig.eslint.json" } diff --git a/packages/mds-db/types.ts b/packages/mds-db/types.ts index 7135e02b9..d04b0d0f4 100644 --- a/packages/mds-db/types.ts +++ b/packages/mds-db/types.ts @@ -6,10 +6,9 @@ import { TelemetryData, VEHICLE_TYPE, PROPULSION_TYPE, - PROVIDER_EVENT, - PROVIDER_REASON + Nullable } from '@mds-core/mds-types' -import { Feature, FeatureCollection } from 'geojson' +import { FeatureCollection } from 'geojson' export interface ReadEventsResult { events: Recorded[] @@ -38,24 +37,6 @@ export interface Trip { recorded: Timestamp } -export interface StatusChange { - provider_id: UUID - provider_name: string - device_id: UUID - vehicle_id: string - vehicle_type: VEHICLE_TYPE - propulsion_type: PROPULSION_TYPE[] - event_type: PROVIDER_EVENT - event_type_reason: PROVIDER_REASON - event_time: Timestamp - event_location: Feature | null - battery_pct: number | null - associated_trip: UUID | null - recorded: Timestamp -} - -export type StatusChangeEvent = Pick - // Represents a row in the "telemetry" table export interface TelemetryRecord extends TelemetryData { device_id: UUID @@ -76,7 +57,7 @@ export interface ReadEventsQueryParams { } export interface ReadHistoricalEventsQueryParams { - provider_id: UUID + provider_id?: UUID end_date: number } @@ -96,10 +77,12 @@ export interface VehicleEventCountResult { } export interface ReadGeographiesParams { - get_read_only: boolean + get_published: Nullable + get_unpublished: Nullable + geography_ids?: UUID[] } export interface PublishGeographiesParams { - publish_date: Timestamp + publish_date?: Timestamp geography_id: UUID } diff --git a/packages/mds-geography-author/LICENSE b/packages/mds-geography-author/LICENSE new file mode 100644 index 000000000..261eeb9e9 --- /dev/null +++ b/packages/mds-geography-author/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/packages/mds-geography-author/README.md b/packages/mds-geography-author/README.md new file mode 100644 index 000000000..c4be49142 --- /dev/null +++ b/packages/mds-geography-author/README.md @@ -0,0 +1,4 @@ +# mds-geography-author +Read and write for MDS Geography objects + +A note on scopes: a published Geography is one that should be visible to all possible users. An unpublished Geography is more private. Therefore, a user with the `geographies:read:unpublished` scope can read everything, while a user with the `geographies:read:published` scope must be restricted to viewing only published Geographies. \ No newline at end of file diff --git a/packages/mds-geography-author/api.ts b/packages/mds-geography-author/api.ts new file mode 100644 index 000000000..61bfe97d3 --- /dev/null +++ b/packages/mds-geography-author/api.ts @@ -0,0 +1,264 @@ +import express from 'express' +import db from '@mds-core/mds-db' + +import { + pathsFor, + ServerError, + NotFoundError, + DependencyMissingError, + ValidationError, + AlreadyPublishedError, + InsufficientPermissionsError, + BadParamsError +} from '@mds-core/mds-utils' +import { geographyValidationDetails } from '@mds-core/mds-schema-validators' +import logger from '@mds-core/mds-logger' + +import { checkAccess, AccessTokenScopeValidator, ApiResponse, ApiRequest } from '@mds-core/mds-api-server' +import { GeographyAuthorApiVersionMiddleware } from './middleware' +import { + GeographyAuthorApiRequest, + GeographyAuthorApiAccessTokenScopes, + GetGeographyMetadataResponse, + DeleteGeographyResponse, + PostGeographyResponse, + PutGeographyResponse, + PutGeographyMetadataResponse, + GetGeographyMetadatumResponse +} from './types' + +const checkGeographyAuthorApiAccess = (validator: AccessTokenScopeValidator) => + checkAccess(validator) + +function api(app: express.Express): express.Express { + app.use(GeographyAuthorApiVersionMiddleware) + + app.get( + pathsFor('/geographies/meta/'), + checkGeographyAuthorApiAccess(scopes => { + return scopes.includes('geographies:read:published') || scopes.includes('geographies:read:unpublished') + }), + async (req: GeographyAuthorApiRequest, res: GetGeographyMetadataResponse, next: express.NextFunction) => { + const { scopes } = res.locals + const { get_published, get_unpublished } = req.query + const params = { + get_published: get_published ? get_published === 'true' : null, + get_unpublished: get_unpublished ? get_unpublished === 'true' : null + } + + /* If the user can only read published geos, and all they want is the unpublished metadata, + * throw a permissions error. + */ + try { + if (!scopes.includes('geographies:read:unpublished') && params.get_unpublished) { + throw new InsufficientPermissionsError( + 'Cannot require unpublished geo metadata without geography:read:unpublished scope' + ) + } + + /* If the user has only the read:published scope, they should not be allowed to see + * unpublished geos. If they didn't supply any params, we modify them here so as to + * filter only for published geo metadata. We have to monkey with the params here + * in a way that we don't for the bulk read of the geographies since we can't filter + * the DB results in this layer, since metadata has no idea if the geo it's associated + * with is published or not. + */ + if ( + !scopes.includes('geographies:read:unpublished') && + params.get_unpublished === null && + params.get_published === null + ) { + params.get_published = true + } + const geography_metadata = await db.readBulkGeographyMetadata(params) + return res.status(200).send({ version: res.locals.version, data: { geography_metadata } }) + } catch (error) { + logger.error('failed to read geography metadata', error) + /* This error is thrown if both get_published and get_unpublished are set. + * To get all geos, neither parameter should be set. + */ + if (error instanceof BadParamsError) { + return res.status(400).send({ error }) + } + if (error instanceof InsufficientPermissionsError) { + return res.status(403).send({ error }) + } + return next(new ServerError()) + } + } + ) + + app.post( + pathsFor('/geographies/'), + checkGeographyAuthorApiAccess(scopes => scopes.includes('geographies:write')), + async (req: GeographyAuthorApiRequest, res: PostGeographyResponse, next: express.NextFunction) => { + const geography = req.body + + try { + const details = geographyValidationDetails(geography) + if (details) { + throw new ValidationError(JSON.stringify(details)) + } + + const recorded_geography = await db.writeGeography(geography) + return res.status(201).send({ version: res.locals.version, data: { geography: recorded_geography } }) + } catch (err) { + logger.error('POST /geographies failed', err.stack) + if (err.code === '23505') { + return res + .status(409) + .send({ error: `geography ${geography.geography_id} already exists! Did you mean to PUT?` }) + } + if (err instanceof ValidationError) { + return res.status(400).send({ error: err }) + } + /* istanbul ignore next */ + /* istanbul ignore next */ + return next(new ServerError(err)) + } + } + ) + + app.put( + pathsFor('/geographies/:geography_id'), + checkGeographyAuthorApiAccess(scopes => scopes.includes('geographies:write')), + async (req: GeographyAuthorApiRequest, res: PutGeographyResponse, next: express.NextFunction) => { + const geography = req.body + try { + const details = geographyValidationDetails(geography) + if (details) { + throw new ValidationError(JSON.stringify(details)) + } + await db.editGeography(geography) + return res.status(201).send({ version: res.locals.version, data: { geography } }) + } catch (error) { + logger.error('failed to edit geography', error.stack) + if (error instanceof NotFoundError) { + return res.status(404).send({ error }) + } + if (error instanceof ValidationError) { + return res.status(400).send({ error }) + } + return next(new ServerError()) + } + } + ) + + app.delete( + pathsFor('/geographies/:geography_id'), + checkGeographyAuthorApiAccess(scopes => scopes.includes('geographies:write')), + async (req: GeographyAuthorApiRequest, res: DeleteGeographyResponse, next: express.NextFunction) => { + const { geography_id } = req.params + try { + const isPublished = await db.isGeographyPublished(geography_id) + if (isPublished) { + throw new AlreadyPublishedError('Cannot delete an already published geography') + } + try { + await db.deleteGeographyMetadata(geography_id) + } catch (err) { + /* No reason to let this bubble up. It's legit for metadata to not exist, and it + * seems wrong to throw an error for deleting metadata when this endpoint is mainly + * about deleting geographies. + */ + logger.info(`Unable to delete nonexistent metadata for ${geography_id}`) + } + await db.deleteGeography(geography_id) + return res.status(200).send({ + version: res.locals.version, + data: { geography_id } + }) + } catch (err) { + logger.error('failed to delete geography', err.stack) + if (err instanceof NotFoundError) { + return res.status(404).send({ error: err }) + } + if (err instanceof AlreadyPublishedError) { + return res.status(405).send({ error: err }) + } + return next(new ServerError()) + } + } + ) + + app.get( + pathsFor('/geographies/:geography_id/meta'), + checkGeographyAuthorApiAccess(scopes => { + return scopes.includes('geographies:read:published') || scopes.includes('geographies:read:unpublished') + }), + async (req: GeographyAuthorApiRequest, res: GetGeographyMetadatumResponse, next: express.NextFunction) => { + const { geography_id } = req.params + try { + const geography_metadata = await db.readSingleGeographyMetadata(geography_id) + const geography = await db.readSingleGeography(geography_id) + if (!geography.publish_date && !res.locals.scopes.includes('geographies:read:unpublished')) { + throw new InsufficientPermissionsError('permission to read metadata of unpublished geographies missing') + } + return res.status(200).send({ version: res.locals.version, data: { geography_metadata } }) + } catch (err) { + logger.error('failed to read geography metadata', err.stack) + if (err instanceof NotFoundError) { + return res.status(404).send({ error: err }) + } + if (err instanceof InsufficientPermissionsError) { + return res.status(403).send({ error: err }) + } + return next(new ServerError()) + } + } + ) + + app.put( + pathsFor('/geographies/:geography_id/meta'), + checkGeographyAuthorApiAccess(scopes => scopes.includes('geographies:write')), + async (req: GeographyAuthorApiRequest, res: PutGeographyMetadataResponse, next: express.NextFunction) => { + const geography_metadata = req.body + try { + await db.updateGeographyMetadata(geography_metadata) + return res.status(200).send(geography_metadata) + } catch (updateErr) { + if (updateErr instanceof NotFoundError) { + try { + await db.writeGeographyMetadata(geography_metadata) + return res.status(201).send({ version: res.locals.version, data: { geography_metadata } }) + } catch (writeErr) { + logger.error('failed to write geography metadata', writeErr.stack) + if (writeErr instanceof DependencyMissingError) { + return res.status(404).send({ error: writeErr }) + } + return next(new ServerError()) + } + } else { + return next(new ServerError(updateErr)) + } + } + } + ) + + app.put( + pathsFor('/geographies/:geography_id/publish'), + checkGeographyAuthorApiAccess(scopes => scopes.includes('geographies:publish')), + async (req: GeographyAuthorApiRequest, res: PutGeographyResponse, next: express.NextFunction) => { + const { geography_id } = req.params + try { + await db.publishGeography({ geography_id }) + const published_geo = await db.readSingleGeography(geography_id) + return res.status(200).send({ version: res.locals.version, data: { geography: published_geo } }) + } catch (updateErr) { + if (updateErr instanceof NotFoundError) { + return res.status(404).send({ error: `unable to find geography of ${geography_id}` }) + } + return next(new ServerError(updateErr)) + } + } + ) + + app.use(async (error: Error, req: ApiRequest, res: ApiResponse) => { + await logger.error(req.method, req.originalUrl, error) + return res.status(500).send({ error }) + }) + + return app +} + +export { api } diff --git a/packages/mds-geography-author/index.ts b/packages/mds-geography-author/index.ts new file mode 100644 index 000000000..20950e624 --- /dev/null +++ b/packages/mds-geography-author/index.ts @@ -0,0 +1 @@ +export { api } from './api' diff --git a/packages/mds-geography-author/middleware/geography-author-api-version.ts b/packages/mds-geography-author/middleware/geography-author-api-version.ts new file mode 100644 index 000000000..b28a0254a --- /dev/null +++ b/packages/mds-geography-author/middleware/geography-author-api-version.ts @@ -0,0 +1,7 @@ +import { ApiVersionMiddleware } from '@mds-core/mds-api-server' +import { GEOGRAPHY_AUTHOR_API_SUPPORTED_VERSIONS, GEOGRAPHY_AUTHOR_API_DEFAULT_VERSION } from '../types' + +export const GeographyAuthorApiVersionMiddleware = ApiVersionMiddleware( + 'application/vnd.mds.geography-author+json', + GEOGRAPHY_AUTHOR_API_SUPPORTED_VERSIONS +).withDefaultVersion(GEOGRAPHY_AUTHOR_API_DEFAULT_VERSION) diff --git a/packages/mds-geography-author/middleware/index.ts b/packages/mds-geography-author/middleware/index.ts new file mode 100644 index 000000000..c33c7779d --- /dev/null +++ b/packages/mds-geography-author/middleware/index.ts @@ -0,0 +1 @@ +export * from './geography-author-api-version' diff --git a/packages/mds-geography-author/package.json b/packages/mds-geography-author/package.json new file mode 100644 index 000000000..47bbd9484 --- /dev/null +++ b/packages/mds-geography-author/package.json @@ -0,0 +1,31 @@ +{ + "name": "@mds-core/mds-geography-author", + "description": "MDS Geography Author API", + "version": "0.0.1", + "private": true, + "author": "City of Los Angeles", + "licence": "Apache-2.0", + "dependencies": { + "@mds-core/mds-api-helpers": "0.1.26", + "@mds-core/mds-api-server": "0.1.26", + "@mds-core/mds-db": "0.1.26", + "@mds-core/mds-logger": "0.1.24", + "@mds-core/mds-providers": "0.1.26", + "@mds-core/mds-schema-validators": "0.1.2", + "@mds-core/mds-types": "0.1.23", + "@mds-core/mds-utils": "0.1.26", + "@types/express": "4.17.6", + "express": "4.17.1" + }, + "main": "dist/index.js", + "types": "dist/index.d.ts", + "scripts": { + "build": "tsc --build tsconfig.build.json", + "start": "PATH_PREFIX=/geography-author yarn watch server", + "test": "yarn test:eslint && yarn test:unit", + "test:eslint": "eslint --ignore-path ../../.gitignore '**/*.ts'", + "test:unit": "PATH_PREFIX=/geography-author DOTENV_CONFIG_PATH=../../.env nyc ts-mocha --project ../../tsconfig.json", + "ts-node": "yarn build && DOTENV_CONFIG_PATH=../../.env ts-node -r dotenv/config", + "watch": "nodemon --watch '../../packages' --ext 'ts' --ignore '*.d.ts' --exec yarn ts-node --" + } +} diff --git a/packages/mds-native/server.ts b/packages/mds-geography-author/server.ts similarity index 64% rename from packages/mds-native/server.ts rename to packages/mds-geography-author/server.ts index 9d2dd5cd1..a7f62a77e 100644 --- a/packages/mds-native/server.ts +++ b/packages/mds-geography-author/server.ts @@ -1,12 +1,9 @@ /* - Copyright 2019 City of Los Angeles. - + Copyright 2020 City of Los Angeles. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at - http://www.apache.org/licenses/LICENSE-2.0 - Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -15,13 +12,7 @@ */ // Express local -import { ApiServer } from '@mds-core/mds-api-server' +import { ApiServer, HttpServer } from '@mds-core/mds-api-server' import { api } from './api' -const { - env: { npm_package_name, PORT = 4006 } -} = process - -/* eslint-reason avoids import of logger */ -/* eslint-disable-next-line no-console */ -ApiServer(api).listen(PORT, () => console.log(`${npm_package_name} running on port ${PORT}`)) +HttpServer(ApiServer(api), { port: process.env.GEOGRAPHY_AUTHOR_API_PORT }) diff --git a/packages/mds-geography-author/tests/api.spec.ts b/packages/mds-geography-author/tests/api.spec.ts new file mode 100644 index 000000000..6cf14b4ff --- /dev/null +++ b/packages/mds-geography-author/tests/api.spec.ts @@ -0,0 +1,521 @@ +/* eslint-disable @typescript-eslint/no-floating-promises */ +/* + Copyright 2019 City of Los Angeles. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +// eslint directives: +/* eslint-disable no-plusplus */ +/* eslint-disable no-useless-concat */ +/* eslint-disable prefer-destructuring */ +/* eslint-disable promise/prefer-await-to-callbacks */ + +/* eslint-reason extends object.prototype */ +/* eslint-disable-next-line @typescript-eslint/no-unused-vars */ +import assert from 'assert' +import sinon from 'sinon' +import supertest from 'supertest' +import test from 'unit.js' +import db from '@mds-core/mds-db' +import { uuid } from '@mds-core/mds-utils' +import { ApiServer } from '@mds-core/mds-api-server' +import { + POLICY_UUID, + GEOGRAPHY_UUID, + GEOGRAPHY2_UUID, + LA_CITY_BOUNDARY, + DISTRICT_SEVEN, + SCOPED_AUTH +} from '@mds-core/mds-test-data' +import { api } from '../api' +import { GEOGRAPHY_AUTHOR_API_DEFAULT_VERSION } from '../types' + +const request = supertest(ApiServer(api)) + +const APP_JSON = 'application/vnd.mds.geography-author+json; charset=utf-8; version=0.4' +const EMPTY_SCOPE = SCOPED_AUTH([], '') +const EVENTS_READ_SCOPE = SCOPED_AUTH(['events:read']) +const GEOGRAPHIES_WRITE_SCOPE = SCOPED_AUTH(['geographies:write']) +const GEOGRAPHIES_READ_PUBLISHED_SCOPE = SCOPED_AUTH(['geographies:read:published']) +const GEOGRAPHIES_READ_UNPUBLISHED_SCOPE = SCOPED_AUTH(['geographies:read:unpublished']) +const GEOGRAPHIES_BOTH_READ_SCOPES = SCOPED_AUTH(['geographies:read:published', 'geographies:read:unpublished']) +const GEOGRAPHIES_PUBLISH_SCOPE = SCOPED_AUTH(['geographies:publish']) +const sandbox = sinon.createSandbox() + +describe('Tests app', () => { + describe('Geography endpoint tests', () => { + afterEach(() => { + sandbox.restore() + }) + + before(async () => { + await db.initialize() + }) + + after(async () => { + await db.shutdown() + }) + + // Geography endpoints + it('cannot POST one current geography (no auth)', done => { + const geography = { geography_id: GEOGRAPHY_UUID, geography_json: LA_CITY_BOUNDARY } + request + .post(`/geographies`) + .set('Authorization', EMPTY_SCOPE) + .send(geography) + .expect(403) + .end(err => { + done(err) + }) + }) + + it('cannot POST one current geography (wrong auth)', done => { + const geography = { geography_id: GEOGRAPHY_UUID, geography_json: LA_CITY_BOUNDARY } + request + .post(`/geographies`) + .set('Authorization', EVENTS_READ_SCOPE) + .send(geography) + .expect(403) + .end(err => { + done(err) + }) + }) + + it('creates one current geography', done => { + const geography = { name: 'LA', geography_id: GEOGRAPHY_UUID, geography_json: LA_CITY_BOUNDARY } + request + .post(`/geographies`) + .set('Authorization', GEOGRAPHIES_WRITE_SCOPE) + .send(geography) + .expect(201) + .end((err, result) => { + test.value(result).hasHeader('content-type', APP_JSON) + done(err) + }) + }) + + it('cannot update one geography (no auth)', done => { + const geography = { geography_id: GEOGRAPHY_UUID, geography_json: DISTRICT_SEVEN } + request + .put(`/geographies/${GEOGRAPHY_UUID}`) + .set('Authorization', EMPTY_SCOPE) + .send(geography) + .expect(403) + .end(err => { + done(err) + }) + }) + + it('cannot update one geography (wrong auth)', done => { + const geography = { geography_id: GEOGRAPHY_UUID, geography_json: DISTRICT_SEVEN } + request + .put(`/geographies/${GEOGRAPHY_UUID}`) + .set('Authorization', EVENTS_READ_SCOPE) + .send(geography) + .expect(403) + .end(err => { + done(err) + }) + }) + + it('verifies updating one geography', done => { + const geography = { name: 'LA', geography_id: GEOGRAPHY_UUID, geography_json: DISTRICT_SEVEN } + request + .put(`/geographies/${GEOGRAPHY_UUID}`) + .set('Authorization', GEOGRAPHIES_WRITE_SCOPE) + .send(geography) + .expect(201) + .end((err, result) => { + test.value(result).hasHeader('content-type', APP_JSON) + done(err) + }) + }) + + it('cannot PUT geography (no auth)', done => { + const geography = { geography_id: GEOGRAPHY_UUID, geography_json: 'garbage_json' } + request + .put(`/geographies/${GEOGRAPHY_UUID}`) + .set('Authorization', EMPTY_SCOPE) + .send(geography) + .expect(403) + .end(err => { + done(err) + }) + }) + + it('cannot PUT geography (wrong auth)', done => { + const geography = { geography_id: GEOGRAPHY_UUID, geography_json: 'garbage_json' } + request + .put(`/geographies/${GEOGRAPHY_UUID}`) + .set('Authorization', EVENTS_READ_SCOPE) + .send(geography) + .expect(403) + .end(err => { + done(err) + }) + }) + + it('verifies cannot PUT bad geography', done => { + const geography = { name: 'LA', geography_id: GEOGRAPHY_UUID, geography_json: 'garbage_json' } + request + .put(`/geographies/${GEOGRAPHY_UUID}`) + .set('Authorization', GEOGRAPHIES_WRITE_SCOPE) + .send(geography) + .expect(400) + .end((err, result) => { + test.value(result).hasHeader('content-type', APP_JSON) + done(err) + }) + }) + + it('verifies cannot PUT non-existent geography', done => { + const geography = { name: 'LA', geography_id: POLICY_UUID, geography_json: DISTRICT_SEVEN } + request + .put(`/geographies/${POLICY_UUID}`) + .set('Authorization', GEOGRAPHIES_WRITE_SCOPE) + .send(geography) + .expect(404) + .end((err, result) => { + test.value(result).hasHeader('content-type', APP_JSON) + done(err) + }) + }) + + it('verifies cannot POST invalid geography', done => { + const geography = { name: 'LA', geography_id: GEOGRAPHY_UUID, geography_json: 'garbage_json' } + request + .post(`/geographies`) + .set('Authorization', GEOGRAPHIES_WRITE_SCOPE) + .send(geography) + .expect(400) + .end((err, result) => { + test.value(result).hasHeader('content-type', APP_JSON) + done(err) + }) + }) + + it('cannot POST duplicate geography', done => { + const geography = { name: 'LA', geography_id: GEOGRAPHY_UUID, geography_json: LA_CITY_BOUNDARY } + request + .post(`/geographies`) + .set('Authorization', GEOGRAPHIES_WRITE_SCOPE) + .send(geography) + .expect(409) + .end((err, result) => { + test.value(result).hasHeader('content-type', APP_JSON) + done(err) + }) + }) + + it('can publish a geography (correct auth)', async () => { + await db.writeGeography({ name: 'Geography 2', geography_id: GEOGRAPHY2_UUID, geography_json: DISTRICT_SEVEN }) + const result = await request + .put(`/geographies/${GEOGRAPHY2_UUID}/publish`) + .set('Authorization', GEOGRAPHIES_PUBLISH_SCOPE) + .expect(200) + test.value(result).hasHeader('content-type', APP_JSON) + test.assert(result.body.version === GEOGRAPHY_AUTHOR_API_DEFAULT_VERSION) + test.assert(result.body.data.geography.geography_id === GEOGRAPHY2_UUID) + test.assert(result.body.data.geography.publish_date) + }) + + it('cannot publish a geography (wrong auth)', async () => { + await request.put(`/geographies/${GEOGRAPHY2_UUID}/publish`).set('Authorization', EMPTY_SCOPE).expect(403) + }) + + it('cannot delete a geography (incorrect auth)', async () => { + await request.delete(`/geographies/${GEOGRAPHY2_UUID}`).set('Authorization', EMPTY_SCOPE).expect(403) + }) + + it('can delete a geography (correct auth)', async () => { + const testUUID = uuid() + await db.writeGeography({ geography_id: testUUID, geography_json: LA_CITY_BOUNDARY, name: 'testafoo' }) + await db.writeGeographyMetadata({ geography_id: testUUID, geography_metadata: { foo: 'afoo' } }) + await request.delete(`/geographies/${testUUID}`).set('Authorization', GEOGRAPHIES_WRITE_SCOPE).expect(200) + await assert.rejects( + async () => { + await db.readSingleGeography(testUUID) + }, + { name: 'NotFoundError' } + ) + await assert.rejects( + async () => { + await db.readSingleGeographyMetadata(testUUID) + }, + { name: 'NotFoundError' } + ) + }) + + it('cannot delete a published geography (correct auth)', async () => { + await request.delete(`/geographies/${GEOGRAPHY2_UUID}`).set('Authorization', GEOGRAPHIES_WRITE_SCOPE).expect(405) + }) + + it('sends the correct error code if something blows up on the backend during delete', async () => { + sandbox.stub(db, 'deleteGeography').callsFake(function stubAThrow() { + throw new Error('random backend err') + }) + await request.delete(`/geographies/${GEOGRAPHY_UUID}`).set('Authorization', GEOGRAPHIES_WRITE_SCOPE).expect(500) + }) + }) + + // Geography metadata endpoints + + describe('Geography metadata endpoint tests', () => { + afterEach(() => { + sandbox.restore() + }) + + before(async () => { + await db.initialize() + await db.writeGeography({ name: 'Geography 1', geography_id: GEOGRAPHY_UUID, geography_json: LA_CITY_BOUNDARY }) + await db.writeGeography({ name: 'Geography 2', geography_id: GEOGRAPHY2_UUID, geography_json: DISTRICT_SEVEN }) + await db.publishGeography({ geography_id: GEOGRAPHY2_UUID }) + }) + + after(async () => { + await db.shutdown() + }) + + it('cannot GET geography metadata (no auth)', done => { + request + .get(`/geographies/${GEOGRAPHY_UUID}/meta`) + .set('Authorization', EMPTY_SCOPE) + .expect(403) + .end(err => { + done(err) + }) + }) + + it('cannot GET geography metadata (wrong auth)', done => { + request + .get(`/geographies/${GEOGRAPHY_UUID}/meta`) + .set('Authorization', EVENTS_READ_SCOPE) + .expect(403) + .end(err => { + done(err) + }) + }) + + it('cannot PUT geography metadata to create (no auth)', async () => { + const metadata = { some_arbitrary_thing: 'boop' } + await request + .put(`/geographies/${GEOGRAPHY_UUID}/meta`) + .set('Authorization', EMPTY_SCOPE) + .send({ geography_id: GEOGRAPHY_UUID, geography_metadata: metadata }) + .expect(403) + }) + + it('cannot PUT geography metadata to create (wrong auth)', async () => { + const metadata = { some_arbitrary_thing: 'boop' } + await request + .put(`/geographies/${GEOGRAPHY_UUID}/meta`) + .set('Authorization', EVENTS_READ_SCOPE) + .send({ geography_id: GEOGRAPHY_UUID, geography_metadata: metadata }) + .expect(403) + }) + + it('sends the correct error code if it cannot retrieve the metadata', async () => { + sandbox.stub(db, 'readBulkGeographyMetadata').callsFake(function stubAThrow() { + throw new Error('err') + }) + await request + .get(`/geographies/${GEOGRAPHY_UUID}/meta`) + .set('Authorization', GEOGRAPHIES_READ_PUBLISHED_SCOPE) + .expect(404) + }) + + it('verifies PUTing geography metadata to create', async () => { + const metadata = { some_arbitrary_thing: 'boop' } + const requestResult = await request + .put(`/geographies/${GEOGRAPHY_UUID}/meta`) + .set('Authorization', GEOGRAPHIES_WRITE_SCOPE) + .send({ geography_id: GEOGRAPHY_UUID, geography_metadata: metadata }) + .expect(201) + test.assert(requestResult.body.version === GEOGRAPHY_AUTHOR_API_DEFAULT_VERSION) + const result = await db.readSingleGeographyMetadata(GEOGRAPHY_UUID) + test.assert(result.geography_metadata.some_arbitrary_thing === 'boop') + }) + + it('verifies PUTing geography metadata to edit', async () => { + const metadata = { some_arbitrary_thing: 'beep' } + await request + .put(`/geographies/${GEOGRAPHY_UUID}/meta`) + .set('Authorization', GEOGRAPHIES_WRITE_SCOPE) + .send({ geography_id: GEOGRAPHY_UUID, geography_metadata: metadata }) + .expect(200) + const result = await db.readSingleGeographyMetadata(GEOGRAPHY_UUID) + test.assert(result.geography_metadata.some_arbitrary_thing === 'beep') + }) + + it('verifies that metadata cannot be created without a preexisting geography', async () => { + const metadata = { some_arbitrary_thing: 'beep' } + const nonexistentGeoUUID = uuid() + await request + .put(`/geographies/${nonexistentGeoUUID}/meta`) + .set('Authorization', GEOGRAPHIES_WRITE_SCOPE) + .send({ geography_id: nonexistentGeoUUID, geography_metadata: metadata }) + .expect(404) + }) + + it('correctly retrieves all geography metadata when using only the unpublished scope', async () => { + await db.writeGeographyMetadata({ geography_id: GEOGRAPHY2_UUID, geography_metadata: { earth: 'isround' } }) + + const result = await request + .get(`/geographies/meta`) + .set('Authorization', GEOGRAPHIES_READ_UNPUBLISHED_SCOPE) + .expect(200) + test.assert(result.body.data.geography_metadata.length === 2) + test.assert(result.body.version === GEOGRAPHY_AUTHOR_API_DEFAULT_VERSION) + test.value(result).hasHeader('content-type', APP_JSON) + }) + + it('retrieves only metadata for published geographies, with the unpublished scope and get_published param', async () => { + const result = await request + .get(`/geographies/meta?get_published=true`) + .set('Authorization', GEOGRAPHIES_READ_UNPUBLISHED_SCOPE) + .expect(200) + test.assert(result.body.data.geography_metadata.length === 1) + test.assert(result.body.version === GEOGRAPHY_AUTHOR_API_DEFAULT_VERSION) + test.value(result).hasHeader('content-type', APP_JSON) + }) + + it('correctly retrieves geography metadata when using the param get_unpublished', async () => { + const result = await request + .get(`/geographies/meta?get_unpublished=true`) + .set('Authorization', GEOGRAPHIES_READ_UNPUBLISHED_SCOPE) + .expect(200) + test.assert(result.body.data.geography_metadata.length === 1) + test.assert(result.body.version === GEOGRAPHY_AUTHOR_API_DEFAULT_VERSION) + test.value(result).hasHeader('content-type', APP_JSON) + }) + + it('retrieves all metadata when both scopes are used', async () => { + const result = await request + .get(`/geographies/meta`) + .set('Authorization', GEOGRAPHIES_BOTH_READ_SCOPES) + .expect(200) + test.assert(result.body.data.geography_metadata.length === 2) + test.assert(result.body.version === GEOGRAPHY_AUTHOR_API_DEFAULT_VERSION) + test.value(result).hasHeader('content-type', APP_JSON) + }) + + it('retrieves only metadata for published geographies with both read scopes and the get_published param', async () => { + const result = await request + .get(`/geographies/meta?get_published=true`) + .set('Authorization', GEOGRAPHIES_BOTH_READ_SCOPES) + .expect(200) + test.assert(result.body.data.geography_metadata.length === 1) + test.assert(result.body.version === GEOGRAPHY_AUTHOR_API_DEFAULT_VERSION) + test.value(result).hasHeader('content-type', APP_JSON) + }) + + it('filters out unpublished geo metadata if only the get_published scope is set', async () => { + const result = await request + .get(`/geographies/meta`) + .set('Authorization', GEOGRAPHIES_READ_PUBLISHED_SCOPE) + .expect(200) + test.assert(result.body.version === GEOGRAPHY_AUTHOR_API_DEFAULT_VERSION) + test.assert(result.body.data.geography_metadata.length === 1) + test.value(result).hasHeader('content-type', APP_JSON) + }) + + it('throws an error if only the get_published scope is set and get_unpublished param is set', async () => { + await request + .get(`/geographies/meta?get_unpublished=true`) + .set('Authorization', GEOGRAPHIES_READ_PUBLISHED_SCOPE) + .expect(403) + }) + + it('cannot do bulk geography metadata reads (wrong auth)', async () => { + await request.get(`/geographies/meta?get_unpublished=false`).set('Authorization', EVENTS_READ_SCOPE).expect(403) + }) + + it('cannot do bulk geography metadata reads (no auth)', async () => { + await request.get(`/geographies/meta?get_published=false`).set('Authorization', EMPTY_SCOPE).expect(403) + }) + + it('verifies GETing a published geography metadata throws a permission error if the scope is wrong', done => { + request + .get(`/geographies/${GEOGRAPHY_UUID}/meta`) + .set('Authorization', GEOGRAPHIES_READ_PUBLISHED_SCOPE) + .expect(403) + .end(err => { + done(err) + }) + }) + + it('verifies GETing a published geography metadata if the scope is geographies:read:unpublished', done => { + request + .get(`/geographies/${GEOGRAPHY_UUID}/meta`) + .set('Authorization', GEOGRAPHIES_READ_UNPUBLISHED_SCOPE) + .expect(200) + .end((err, result) => { + done(err) + }) + }) + + it('verifies cannot GET non-existent geography metadata', done => { + const nonexistentID = uuid() + request + .get(`/geographies/${nonexistentID}/meta`) + .set('Authorization', GEOGRAPHIES_READ_PUBLISHED_SCOPE) + .expect(404) + .end((err, result) => { + test.assert(result.body.error.name === `NotFoundError`) + test.value(result).hasHeader('content-type', APP_JSON) + done(err) + }) + }) + + it('verifies cannot PUT geography with a publish_date', done => { + const geography = { + name: 'foo', + geography_id: GEOGRAPHY_UUID, + publish_date: 1589817834000, + geography_json: LA_CITY_BOUNDARY + } + request + .put(`/geographies/${geography.geography_id}`) + .set('Authorization', GEOGRAPHIES_WRITE_SCOPE) + .send(geography) + .expect(400) + .end((err, result) => { + test.assert(result.body.error.name === `ValidationError`) + test.assert(result.body.error.reason.includes('publish_date')) + test.value(result).hasHeader('content-type', APP_JSON) + done(err) + }) + }) + + it('verifies cannot POST geography with a publish_date', done => { + const geography = { + name: 'foo', + geography_id: GEOGRAPHY_UUID, + publish_date: 1589817834000, + geography_json: LA_CITY_BOUNDARY + } + request + .post(`/geographies`) + .set('Authorization', GEOGRAPHIES_WRITE_SCOPE) + .send(geography) + .expect(400) + .end((err, result) => { + test.assert(result.body.error.name === `ValidationError`) + test.assert(result.body.error.reason.includes('publish_date')) + test.value(result).hasHeader('content-type', APP_JSON) + done(err) + }) + }) + }) +}) diff --git a/packages/mds-native/tsconfig.build.json b/packages/mds-geography-author/tsconfig.build.json similarity index 88% rename from packages/mds-native/tsconfig.build.json rename to packages/mds-geography-author/tsconfig.build.json index d4a2e11c7..7a9871e24 100644 --- a/packages/mds-native/tsconfig.build.json +++ b/packages/mds-geography-author/tsconfig.build.json @@ -9,6 +9,7 @@ { "path": "../../packages/mds-db/tsconfig.build.json" }, { "path": "../../packages/mds-logger/tsconfig.build.json" }, { "path": "../../packages/mds-providers/tsconfig.build.json" }, + { "path": "../../packages/mds-schema-validators/tsconfig.build.json" }, { "path": "../../packages/mds-types/tsconfig.build.json" }, { "path": "../../packages/mds-utils/tsconfig.build.json" } ] diff --git a/packages/mds-geography-author/tsconfig.eslint.json b/packages/mds-geography-author/tsconfig.eslint.json new file mode 100644 index 000000000..2a400942a --- /dev/null +++ b/packages/mds-geography-author/tsconfig.eslint.json @@ -0,0 +1 @@ +{ "extends": "../../tsconfig.eslint.json" } diff --git a/packages/mds-geography-author/types.ts b/packages/mds-geography-author/types.ts new file mode 100644 index 000000000..9a57b876d --- /dev/null +++ b/packages/mds-geography-author/types.ts @@ -0,0 +1,55 @@ +/* + Copyright 2019 City of Los Angeles. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +import { ApiRequest, ApiVersionedResponse, ApiClaims } from '@mds-core/mds-api-server' +import { GeographyMetadata, Geography, UUID } from '@mds-core/mds-types' + +export const GEOGRAPHY_AUTHOR_API_SUPPORTED_VERSIONS = ['0.4.1'] as const +export type GEOGRAPHY_AUTHOR_API_SUPPORTED_VERSION = typeof GEOGRAPHY_AUTHOR_API_SUPPORTED_VERSIONS[number] +export const [GEOGRAPHY_AUTHOR_API_DEFAULT_VERSION] = GEOGRAPHY_AUTHOR_API_SUPPORTED_VERSIONS + +export type GeographyAuthorApiRequest = ApiRequest + +export type GeographyAuthorApiAccessTokenScopes = + | 'geographies:read' + | 'geographies:read:unpublished' + | 'geographies:read:published' + | 'geographies:write' + | 'geographies:publish' + +export type GeographyAuthorApiResponse = ApiVersionedResponse< + GEOGRAPHY_AUTHOR_API_SUPPORTED_VERSION, + ApiClaims, + TBody +> + +export type GetGeographyMetadatumResponse = GeographyAuthorApiResponse<{ + data: { geography_metadata: GeographyMetadata } +}> + +export type GetGeographyMetadataResponse = GeographyAuthorApiResponse<{ + data: { geography_metadata: GeographyMetadata[] } +}> + +export type PostGeographyResponse = GeographyAuthorApiResponse<{ data: { geography: Geography } }> + +export type PutGeographyResponse = GeographyAuthorApiResponse<{ data: { geography: Geography } }> +export type PublishGeographyResponse = GeographyAuthorApiResponse<{ data: { geography: Geography } }> +export type PutGeographyMetadataResponse = GeographyAuthorApiResponse<{ + data: { geography_metadata: GeographyMetadata } +}> + +export type DeleteGeographyResponse = GeographyAuthorApiResponse<{ data: { geography_id: UUID } }> diff --git a/packages/mds-geography/LICENSE b/packages/mds-geography/LICENSE new file mode 100644 index 000000000..261eeb9e9 --- /dev/null +++ b/packages/mds-geography/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/packages/mds-geography/README.md b/packages/mds-geography/README.md new file mode 100644 index 000000000..94610f94f --- /dev/null +++ b/packages/mds-geography/README.md @@ -0,0 +1,4 @@ +# mds-geography +Read-only endpoints for MDS Geography objects + +A note on scopes: a published Geography is one that should be visible to all possible users. An unpublished Geography is more private. Therefore, a user with the `geographies:read:unpublished` scope can read everything, while a user with the `geographies:read:published` scope must be restricted to viewing only published Geographies. \ No newline at end of file diff --git a/packages/mds-geography/api.ts b/packages/mds-geography/api.ts new file mode 100644 index 000000000..a19bade38 --- /dev/null +++ b/packages/mds-geography/api.ts @@ -0,0 +1,100 @@ +import express from 'express' +import db from '@mds-core/mds-db' + +import { pathsFor, ServerError, NotFoundError, InsufficientPermissionsError } from '@mds-core/mds-utils' +import logger from '@mds-core/mds-logger' + +import { checkAccess, AccessTokenScopeValidator, ApiResponse, ApiRequest } from '@mds-core/mds-api-server' +import { GeographyApiVersionMiddleware } from './middleware' +import { + GeographyApiAccessTokenScopes, + GeographyApiRequest, + GetGeographyResponse, + GetGeographiesResponse +} from './types' + +const checkGeographyApiAccess = (validator: AccessTokenScopeValidator) => + checkAccess(validator) + +function api(app: express.Express): express.Express { + app.use(GeographyApiVersionMiddleware) + + app.get( + pathsFor('/geographies/:geography_id'), + checkGeographyApiAccess(scopes => { + return scopes.includes('geographies:read:published') || scopes.includes('geographies:read:unpublished') + }), + async (req: GeographyApiRequest, res: GetGeographyResponse, next: express.NextFunction) => { + const { geography_id } = req.params + try { + const geography = await db.readSingleGeography(geography_id) + if (!geography.publish_date && !res.locals.scopes.includes('geographies:read:unpublished')) { + throw new InsufficientPermissionsError('permission to read unpublished geographies missing') + } + return res.status(200).send({ version: res.locals.version, data: { geography } }) + } catch (error) { + logger.error('failed to read geography', error.stack) + if (error instanceof NotFoundError) { + return res.status(404).send({ error }) + } + + if (error instanceof InsufficientPermissionsError) { + return res.status(403).send({ error }) + } + + return next(new ServerError(error)) + } + } + ) + + app.get( + pathsFor('/geographies'), + checkGeographyApiAccess(scopes => { + return scopes.includes('geographies:read:published') || scopes.includes('geographies:read:unpublished') + }), + async (req: GeographyApiRequest, res: GetGeographiesResponse, next: express.NextFunction) => { + const summary = req.query.summary === 'true' + const { get_published, get_unpublished } = req.query + const params = { + get_published: get_published ? get_published === 'true' : null, + get_unpublished: get_unpublished ? get_unpublished === 'true' : null + } + + try { + if (!res.locals.scopes.includes('geographies:read:unpublished') && params.get_unpublished) { + throw new InsufficientPermissionsError( + 'Cannot require unpublished geos without geography:read:unpublished scope' + ) + } + + const geographies = summary ? await db.readGeographySummaries(params) : await db.readGeographies(params) + if (!res.locals.scopes.includes('geographies:read:unpublished')) { + const filteredGeos = geographies.filter(geo => !!geo.publish_date) + return res.status(200).send({ version: res.locals.version, data: { geographies: filteredGeos } }) + } + return res.status(200).send({ version: res.locals.version, data: { geographies } }) + } catch (error) { + /* We don't throw a NotFoundError if the number of results is zero, so the error handling + * doesn't need to consider it here. + */ + if (error instanceof InsufficientPermissionsError) { + return res.status(403).send({ error }) + } + logger.error('failed to read geographies', error.stack) + return next(new ServerError(error)) + } + } + ) + + /* eslint-reason global error handling middleware */ + /* istanbul ignore next */ + /* eslint-disable-next-line @typescript-eslint/no-unused-vars */ + app.use(async (error: Error, req: ApiRequest, res: ApiResponse) => { + await logger.error(req.method, req.originalUrl, error) + return res.status(500).send({ error }) + }) + + return app +} + +export { api } diff --git a/packages/mds-geography/index.ts b/packages/mds-geography/index.ts new file mode 100644 index 000000000..20950e624 --- /dev/null +++ b/packages/mds-geography/index.ts @@ -0,0 +1 @@ +export { api } from './api' diff --git a/packages/mds-geography/middleware/geography-api-version.ts b/packages/mds-geography/middleware/geography-api-version.ts new file mode 100644 index 000000000..2d8c7c358 --- /dev/null +++ b/packages/mds-geography/middleware/geography-api-version.ts @@ -0,0 +1,7 @@ +import { ApiVersionMiddleware } from '@mds-core/mds-api-server' +import { GEOGRAPHY_API_SUPPORTED_VERSIONS, GEOGRAPHY_API_DEFAULT_VERSION } from '../types' + +export const GeographyApiVersionMiddleware = ApiVersionMiddleware( + 'application/vnd.mds-geography+json', + GEOGRAPHY_API_SUPPORTED_VERSIONS +).withDefaultVersion(GEOGRAPHY_API_DEFAULT_VERSION) diff --git a/packages/mds-geography/middleware/index.ts b/packages/mds-geography/middleware/index.ts new file mode 100644 index 000000000..6ab06bd0f --- /dev/null +++ b/packages/mds-geography/middleware/index.ts @@ -0,0 +1 @@ +export * from './geography-api-version' diff --git a/packages/mds-geography/package.json b/packages/mds-geography/package.json new file mode 100644 index 000000000..0971a1432 --- /dev/null +++ b/packages/mds-geography/package.json @@ -0,0 +1,31 @@ +{ + "name": "@mds-core/mds-geography", + "description": "MDS Geography API", + "version": "0.0.1", + "private": true, + "author": "City of Los Anngeles", + "licence": "Apache-2.0", + "dependencies": { + "@mds-core/mds-api-helpers": "0.1.26", + "@mds-core/mds-api-server": "0.1.26", + "@mds-core/mds-db": "0.1.26", + "@mds-core/mds-logger": "0.1.24", + "@mds-core/mds-providers": "0.1.26", + "@mds-core/mds-schema-validators": "0.1.2", + "@mds-core/mds-types": "0.1.23", + "@mds-core/mds-utils": "0.1.26", + "@types/express": "4.17.6", + "express": "4.17.1" + }, + "main": "dist/index.js", + "types": "dist/index.d.ts", + "scripts": { + "build": "tsc --build tsconfig.build.json", + "start": "PATH_PREFIX=/geography yarn watch server", + "test": "yarn test:eslint && yarn test:unit", + "test:eslint": "eslint --ignore-path ../../.gitignore '**/*.ts'", + "test:unit": "PATH_PREFIX=/geography DOTENV_CONFIG_PATH=../../.env nyc ts-mocha --project ../../tsconfig.json", + "ts-node": "yarn build && DOTENV_CONFIG_PATH=../../.env ts-node -r dotenv/config", + "watch": "nodemon --watch '../../packages' --ext 'ts' --ignore '*.d.ts' --exec yarn ts-node --" + } +} diff --git a/packages/mds-geography/server.ts b/packages/mds-geography/server.ts new file mode 100644 index 000000000..e01b6a0d9 --- /dev/null +++ b/packages/mds-geography/server.ts @@ -0,0 +1,17 @@ +/* + Copyright 2020 City of Los Angeles. + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +import { ApiServer, HttpServer } from '@mds-core/mds-api-server' +import { api } from './api' + +HttpServer(ApiServer(api), { port: process.env.GEOGRAPHY_API_PORT }) diff --git a/packages/mds-geography/tests/api.spec.ts b/packages/mds-geography/tests/api.spec.ts new file mode 100644 index 000000000..6c2be4d6d --- /dev/null +++ b/packages/mds-geography/tests/api.spec.ts @@ -0,0 +1,219 @@ +/* eslint-disable @typescript-eslint/no-floating-promises */ +/* + Copyright 2019 City of Los Angeles. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +// eslint directives: +/* eslint-disable no-plusplus */ +/* eslint-disable no-useless-concat */ +/* eslint-disable prefer-destructuring */ +/* eslint-disable promise/prefer-await-to-callbacks */ + +/* eslint-reason extends object.prototype */ +/* eslint-disable-next-line @typescript-eslint/no-unused-vars */ +import assert from 'assert' +import supertest from 'supertest' +import test from 'unit.js' +import db from '@mds-core/mds-db' +import { Geography } from '@mds-core/mds-types' +import { ApiServer } from '@mds-core/mds-api-server' +import { + POLICY_UUID, + GEOGRAPHY_UUID, + GEOGRAPHY2_UUID, + LA_CITY_BOUNDARY, + DISTRICT_SEVEN, + SCOPED_AUTH +} from '@mds-core/mds-test-data' +import { api } from '../api' +import { GEOGRAPHY_API_DEFAULT_VERSION } from '../types' + +const request = supertest(ApiServer(api)) + +const APP_JSON = 'application/vnd.mds-geography+json; charset=utf-8; version=0.4' +const EMPTY_SCOPE = SCOPED_AUTH([], '') +const EVENTS_READ_SCOPE = SCOPED_AUTH(['events:read']) +const GEOGRAPHIES_READ_PUBLISHED_SCOPE = SCOPED_AUTH(['geographies:read:published']) +const GEOGRAPHIES_READ_UNPUBLISHED_SCOPE = SCOPED_AUTH(['geographies:read:unpublished']) +const GEOGRAPHIES_BOTH_READ_SCOPES = SCOPED_AUTH(['geographies:read:published', 'geographies:read:unpublished']) + +describe('Tests app', () => { + describe('Geography endpoint tests', () => { + before(async () => { + await db.initialize() + }) + + after(async () => { + await db.shutdown() + }) + + it('GETs one unpublished geography with unpublished scope', async () => { + const geography = { name: 'LA', geography_id: GEOGRAPHY_UUID, geography_json: LA_CITY_BOUNDARY } + await db.writeGeography(geography) + const result = await request + .get(`/geographies/${GEOGRAPHY_UUID}`) + .set('Authorization', GEOGRAPHIES_READ_UNPUBLISHED_SCOPE) + .expect(200) + test.assert(result.body.data.geography.geography_id === GEOGRAPHY_UUID) + test.assert(result.body.version === GEOGRAPHY_API_DEFAULT_VERSION) + test.value(result).hasHeader('content-type', APP_JSON) + }) + + it('cannot GET an unpublished geography with the published scope', done => { + request + .get(`/geographies/${GEOGRAPHY_UUID}`) + .set('Authorization', GEOGRAPHIES_READ_PUBLISHED_SCOPE) + .expect(403) + .end(err => { + done(err) + }) + }) + + it('cannot GET a nonexistent geography', done => { + request + .get(`/geographies/${POLICY_UUID}`) + .set('Authorization', GEOGRAPHIES_READ_PUBLISHED_SCOPE) + .expect(404) + .end(err => { + done(err) + }) + }) + + it('cannot GET geographies (no auth)', done => { + request + .get(`/geographies/`) + .set('Authorization', EMPTY_SCOPE) + .expect(403) + .end(err => { + done(err) + }) + }) + + it('cannot GET geographies (wrong auth)', done => { + request + .get(`/geographies/`) + .set('Authorization', EVENTS_READ_SCOPE) + .expect(403) + .end(err => { + done(err) + }) + }) + + it('can GET geographies, full version', done => { + request + .get(`/geographies/`) + .set('Authorization', GEOGRAPHIES_READ_PUBLISHED_SCOPE) + .expect(200) + .end((err, result) => { + result.body.data.geographies.forEach((item: Geography) => { + test.assert(item.geography_json) + }) + test.assert(result.body.version === GEOGRAPHY_API_DEFAULT_VERSION) + test.value(result).hasHeader('content-type', APP_JSON) + done(err) + }) + }) + + it('can GET geographies, summarized version', done => { + request + .get(`/geographies?summary=true`) + .set('Authorization', GEOGRAPHIES_READ_PUBLISHED_SCOPE) + .expect(200) + .end((err, result) => { + result.body.data.geographies.forEach((item: Geography) => { + test.assert(!item.geography_json) + }) + test.assert(result.body.version === GEOGRAPHY_API_DEFAULT_VERSION) + done(err) + }) + }) + + it('can read a published geography with both read scopes', async () => { + await db.writeGeography({ name: 'Geography 2', geography_id: GEOGRAPHY2_UUID, geography_json: DISTRICT_SEVEN }) + await db.publishGeography({ geography_id: GEOGRAPHY2_UUID }) + await request + .get(`/geographies/${GEOGRAPHY2_UUID}`) + .set('Authorization', GEOGRAPHIES_BOTH_READ_SCOPES) + .expect(200) + }) + + it('can GET one unpublished geography with unpublished scope', done => { + request + .get(`/geographies/${GEOGRAPHY_UUID}`) + .set('Authorization', GEOGRAPHIES_READ_UNPUBLISHED_SCOPE) + .expect(200) + .end((err, result) => { + test.assert(result.body.data.geography.geography_id === GEOGRAPHY_UUID) + test.value(result).hasHeader('content-type', APP_JSON) + done(err) + }) + }) + + it('can get all geographies, with the unpublished scope', done => { + request + .get(`/geographies`) + .set('Authorization', GEOGRAPHIES_READ_UNPUBLISHED_SCOPE) + .expect(200) + .end((err, result) => { + test.assert(result.body.data.geographies.length === 2) + test.assert(result.body.version === GEOGRAPHY_API_DEFAULT_VERSION) + done(err) + }) + }) + + it('can filter for published geographies, with the unpublished scope and get_published parameter', done => { + request + .get(`/geographies?get_published=true`) + .set('Authorization', GEOGRAPHIES_READ_UNPUBLISHED_SCOPE) + .expect(200) + .end((err, result) => { + test.assert(result.body.data.geographies.length === 1) + test.assert(result.body.version === GEOGRAPHY_API_DEFAULT_VERSION) + done(err) + }) + }) + + it('can filter for unpublished geographies, with the unpublished scope and get_unpublished parameter', done => { + request + .get(`/geographies?get_unpublished=true`) + .set('Authorization', GEOGRAPHIES_READ_UNPUBLISHED_SCOPE) + .expect(200) + .end((err, result) => { + assert(result.body.data.geographies.length === 1) + test.assert(result.body.version === GEOGRAPHY_API_DEFAULT_VERSION) + done(err) + }) + }) + + it('can only GET published geographies, with only the published scope', done => { + request + .get(`/geographies`) + .set('Authorization', GEOGRAPHIES_READ_PUBLISHED_SCOPE) + .expect(200) + .end((err, result) => { + assert(result.body.data.geographies.length === 1) + test.assert(result.body.version === GEOGRAPHY_API_DEFAULT_VERSION) + done(err) + }) + }) + + it('throws an error if only the get_published scope is set and get_unpublished param is set', async () => { + await request + .get(`/geographies?get_unpublished=true`) + .set('Authorization', GEOGRAPHIES_READ_PUBLISHED_SCOPE) + .expect(403) + }) + }) +}) diff --git a/packages/mds-geography/tsconfig.build.json b/packages/mds-geography/tsconfig.build.json new file mode 100644 index 000000000..7a9871e24 --- /dev/null +++ b/packages/mds-geography/tsconfig.build.json @@ -0,0 +1,16 @@ +{ + "extends": "../../tsconfig.build.json", + "compilerOptions": { + "outDir": "dist" + }, + "references": [ + { "path": "../../packages/mds-api-helpers/tsconfig.build.json" }, + { "path": "../../packages/mds-api-server/tsconfig.build.json" }, + { "path": "../../packages/mds-db/tsconfig.build.json" }, + { "path": "../../packages/mds-logger/tsconfig.build.json" }, + { "path": "../../packages/mds-providers/tsconfig.build.json" }, + { "path": "../../packages/mds-schema-validators/tsconfig.build.json" }, + { "path": "../../packages/mds-types/tsconfig.build.json" }, + { "path": "../../packages/mds-utils/tsconfig.build.json" } + ] +} diff --git a/packages/mds-geography/tsconfig.eslint.json b/packages/mds-geography/tsconfig.eslint.json new file mode 100644 index 000000000..2a400942a --- /dev/null +++ b/packages/mds-geography/tsconfig.eslint.json @@ -0,0 +1 @@ +{ "extends": "../../tsconfig.eslint.json" } diff --git a/packages/mds-geography/types.ts b/packages/mds-geography/types.ts new file mode 100644 index 000000000..ab2de32e0 --- /dev/null +++ b/packages/mds-geography/types.ts @@ -0,0 +1,43 @@ +/* + Copyright 2019 City of Los Angeles. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +import { ApiRequest, ApiVersionedResponse, ApiClaims } from '@mds-core/mds-api-server' +import { Geography, GeographySummary } from '@mds-core/mds-types' + +export const GEOGRAPHY_API_SUPPORTED_VERSIONS = ['0.4.1'] as const +export type GEOGRAPHY_API_SUPPORTED_VERSION = typeof GEOGRAPHY_API_SUPPORTED_VERSIONS[number] +export const [GEOGRAPHY_API_DEFAULT_VERSION] = GEOGRAPHY_API_SUPPORTED_VERSIONS + +export type GeographyApiRequest = ApiRequest + +export type GeographyApiAccessTokenScopes = + | 'geographies:read' + | 'geographies:read:unpublished' + | 'geographies:read:published' + +export type GeographyApiResponse = ApiVersionedResponse< + GEOGRAPHY_API_SUPPORTED_VERSION, + ApiClaims, + TBody +> + +export type GetGeographyResponse = GeographyApiResponse<{ + data: { geographies: Geography[] | GeographySummary[] } | { geography: Geography | GeographySummary } +}> + +export type GetGeographiesResponse = GeographyApiResponse<{ + data: { geographies: Geography[] | GeographySummary[] } +}> diff --git a/packages/mds-jurisdiction-service/@types/index.ts b/packages/mds-jurisdiction-service/@types/index.ts new file mode 100644 index 000000000..0a587857a --- /dev/null +++ b/packages/mds-jurisdiction-service/@types/index.ts @@ -0,0 +1,47 @@ +/* + Copyright 2019-2020 City of Los Angeles. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +import { UUID, Timestamp, Optional } from '@mds-core/mds-types' + +export interface JurisdictionDomainModel { + jurisdiction_id: UUID + agency_key: string + agency_name: string + geography_id: UUID + timestamp: Timestamp +} + +export type JurisdictionIdType = JurisdictionDomainModel['jurisdiction_id'] + +export type CreateJurisdictionDomainModel = Optional + +export type UpdateJurisdictionDomainModel = Partial + +export type GetJurisdictionsOptions = Partial<{ + effective: Timestamp +}> + +export interface JurisdictionService { + createJurisdiction: (jurisdiction: CreateJurisdictionDomainModel) => JurisdictionDomainModel + createJurisdictions: (jurisdictions: CreateJurisdictionDomainModel[]) => JurisdictionDomainModel[] + updateJurisdiction: ( + jurisdiction_id: JurisdictionIdType, + update: UpdateJurisdictionDomainModel + ) => JurisdictionDomainModel + deleteJurisdiction: (jurisdiction_id: JurisdictionIdType) => Pick + getJurisdictions: (options?: GetJurisdictionsOptions) => JurisdictionDomainModel[] + getJurisdiction: (jurisdiction_id: JurisdictionIdType, options?: GetJurisdictionsOptions) => JurisdictionDomainModel +} diff --git a/packages/mds-jurisdiction-service/client/index.ts b/packages/mds-jurisdiction-service/client/index.ts new file mode 100644 index 000000000..d94dcb0f3 --- /dev/null +++ b/packages/mds-jurisdiction-service/client/index.ts @@ -0,0 +1,30 @@ +/* + Copyright 2019-2020 City of Los Angeles. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +import { UnwrapServiceResult, ServiceClient } from '@mds-core/mds-service-helpers' +import { JurisdictionServiceProvider } from '../service/provider' +import { JurisdictionService } from '../@types' + +const { start, stop, ...service } = JurisdictionServiceProvider + +export const JurisdictionServiceClient: ServiceClient = { + createJurisdiction: UnwrapServiceResult(service.createJurisdiction), + createJurisdictions: UnwrapServiceResult(service.createJurisdictions), + deleteJurisdiction: UnwrapServiceResult(service.deleteJurisdiction), + getJurisdiction: UnwrapServiceResult(service.getJurisdiction), + getJurisdictions: UnwrapServiceResult(service.getJurisdictions), + updateJurisdiction: UnwrapServiceResult(service.updateJurisdiction) +} diff --git a/packages/mds-jurisdiction-service/index.ts b/packages/mds-jurisdiction-service/index.ts new file mode 100644 index 000000000..2aee62f8c --- /dev/null +++ b/packages/mds-jurisdiction-service/index.ts @@ -0,0 +1,18 @@ +/* + Copyright 2019-2020 City of Los Angeles. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +export * from './@types' +export * from './client' diff --git a/packages/mds-jurisdiction-service/ormconfig.ts b/packages/mds-jurisdiction-service/ormconfig.ts new file mode 100644 index 000000000..a18fe0ec6 --- /dev/null +++ b/packages/mds-jurisdiction-service/ormconfig.ts @@ -0,0 +1,20 @@ +/* + Copyright 2019-2020 City of Los Angeles. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +import { JurisdictionRepository } from './service/repository' + +// Make connection options available to TypeORM CLI +module.exports = JurisdictionRepository.cli({ migrationsDir: 'service/repository/migrations' }) diff --git a/packages/mds-jurisdiction-service/package.json b/packages/mds-jurisdiction-service/package.json new file mode 100644 index 000000000..bde96d0bf --- /dev/null +++ b/packages/mds-jurisdiction-service/package.json @@ -0,0 +1,35 @@ +{ + "name": "@mds-core/mds-jurisdiction-service", + "version": "0.1.0", + "description": "Mobility Data Specification Jurisdiction Service", + "main": "dist/index.js", + "types": "dist/index.d.ts", + "files": [ + "dist/" + ], + "keywords": [ + "mds", + "database" + ], + "scripts": { + "build": "tsc --build tsconfig.build.json", + "start": "yarn watch server", + "test": "yarn test:eslint && yarn test:unit", + "test:eslint": "eslint --ignore-path ../../.gitignore '**/*.ts'", + "test:unit": "yarn typeorm schema:drop && DOTENV_CONFIG_PATH=../../.env nyc ts-mocha --project ../../tsconfig.json && yarn typeorm schema:drop", + "ts-node": "yarn build && DOTENV_CONFIG_PATH=../../.env ts-node -r dotenv/config", + "typeorm": "yarn ts-node ./node_modules/.bin/typeorm", + "watch": "nodemon --watch '../../packages' --ext 'ts' --ignore '*.d.ts' --exec yarn ts-node --" + }, + "author": "City of Los Angeles", + "license": "Apache-2.0", + "dependencies": { + "@mds-core/mds-logger": "0.1.24", + "@mds-core/mds-repository": "0.1.0", + "@mds-core/mds-schema-validators": "0.1.2", + "@mds-core/mds-service-helpers": "0.1.0", + "@mds-core/mds-types": "0.1.23", + "@mds-core/mds-utils": "0.1.26", + "typeorm": "0.2.24" + } +} diff --git a/packages/mds-jurisdiction-service/server/index.ts b/packages/mds-jurisdiction-service/server/index.ts new file mode 100644 index 000000000..b67d64836 --- /dev/null +++ b/packages/mds-jurisdiction-service/server/index.ts @@ -0,0 +1,20 @@ +/* + Copyright 2019-2020 City of Los Angeles. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +import { ProcessManager } from '@mds-core/mds-service-helpers' +import { JurisdictionServiceProvider } from '../service/provider' + +ProcessManager(JurisdictionServiceProvider).monitor() diff --git a/packages/mds-jurisdiction-service/service/handlers/create-jurisdiction-handler.ts b/packages/mds-jurisdiction-service/service/handlers/create-jurisdiction-handler.ts new file mode 100644 index 000000000..a31acef53 --- /dev/null +++ b/packages/mds-jurisdiction-service/service/handlers/create-jurisdiction-handler.ts @@ -0,0 +1,35 @@ +/* + Copyright 2019-2020 City of Los Angeles. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +import { ServiceResponse, ServiceResult, ServiceException } from '@mds-core/mds-service-helpers' +import logger from '@mds-core/mds-logger' +import { RepositoryError } from '@mds-core/mds-repository' +import { CreateJurisdictionDomainModel, JurisdictionDomainModel } from '../../@types' +import { JurisdictionRepository } from '../repository' +import { ValidateJurisdictionForCreate } from '../validators' + +export const createJurisdiction = async ( + model: CreateJurisdictionDomainModel +): Promise> => { + try { + const [jurisdiction] = await JurisdictionRepository.createJurisdictions([model].map(ValidateJurisdictionForCreate)) + return ServiceResult(jurisdiction) + } catch (error) /* istanbul ignore next */ { + const exception = ServiceException('Error Creating Jurisdiction', RepositoryError(error)) + logger.error(exception, error) + return exception + } +} diff --git a/packages/mds-jurisdiction-service/service/handlers/create-jurisdictions-handler.ts b/packages/mds-jurisdiction-service/service/handlers/create-jurisdictions-handler.ts new file mode 100644 index 000000000..4e0ef44f3 --- /dev/null +++ b/packages/mds-jurisdiction-service/service/handlers/create-jurisdictions-handler.ts @@ -0,0 +1,35 @@ +/* + Copyright 2019-2020 City of Los Angeles. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +import { ServiceResponse, ServiceResult, ServiceException } from '@mds-core/mds-service-helpers' +import logger from '@mds-core/mds-logger' +import { RepositoryError } from '@mds-core/mds-repository' +import { CreateJurisdictionDomainModel, JurisdictionDomainModel } from '../../@types' +import { JurisdictionRepository } from '../repository' +import { ValidateJurisdictionForCreate } from '../validators' + +export const createJurisdictions = async ( + models: CreateJurisdictionDomainModel[] +): Promise> => { + try { + const jurisdictions = await JurisdictionRepository.createJurisdictions(models.map(ValidateJurisdictionForCreate)) + return ServiceResult(jurisdictions) + } catch (error) /* istanbul ignore next */ { + const exception = ServiceException('Error Creating Jurisdictions', RepositoryError(error)) + logger.error(exception, error) + return exception + } +} diff --git a/packages/mds-jurisdiction-service/service/handlers/delete-jurisdiction-handler.ts b/packages/mds-jurisdiction-service/service/handlers/delete-jurisdiction-handler.ts new file mode 100644 index 000000000..0498751c1 --- /dev/null +++ b/packages/mds-jurisdiction-service/service/handlers/delete-jurisdiction-handler.ts @@ -0,0 +1,34 @@ +/* + Copyright 2019-2020 City of Los Angeles. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +import { ServiceResponse, ServiceResult, ServiceException } from '@mds-core/mds-service-helpers' +import logger from '@mds-core/mds-logger' +import { RepositoryError } from '@mds-core/mds-repository' +import { JurisdictionRepository } from '../repository' +import { JurisdictionDomainModel, JurisdictionIdType } from '../../@types' + +export const deleteJurisdiction = async ( + jurisdiction_id: JurisdictionIdType +): Promise>> => { + try { + const deleted = await JurisdictionRepository.deleteJurisdiction(jurisdiction_id) + return ServiceResult(deleted) + } catch (error) /* istanbul ignore next */ { + const exception = ServiceException('Error Deleting Jurisdiction', RepositoryError(error)) + logger.error(exception, error) + return exception + } +} diff --git a/packages/mds-jurisdiction-service/service/handlers/get-jurisdiction-handler.ts b/packages/mds-jurisdiction-service/service/handlers/get-jurisdiction-handler.ts new file mode 100644 index 000000000..c67925bb1 --- /dev/null +++ b/packages/mds-jurisdiction-service/service/handlers/get-jurisdiction-handler.ts @@ -0,0 +1,35 @@ +/* + Copyright 2019-2020 City of Los Angeles. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +import { ServiceResponse, ServiceResult, ServiceException } from '@mds-core/mds-service-helpers' +import logger from '@mds-core/mds-logger' +import { RepositoryError } from '@mds-core/mds-repository' +import { GetJurisdictionsOptions, JurisdictionDomainModel, JurisdictionIdType } from '../../@types' +import { JurisdictionRepository } from '../repository' + +export const getJurisdiction = async ( + jurisdiction_id: JurisdictionIdType, + options?: GetJurisdictionsOptions +): Promise> => { + try { + const jurisdiction = await JurisdictionRepository.readJurisdiction(jurisdiction_id, options) + return ServiceResult(jurisdiction) + } catch (error) /* istanbul ignore next */ { + const exception = ServiceException('Error Reading Jurisdiction', RepositoryError(error)) + logger.error(exception, error) + return exception + } +} diff --git a/packages/mds-jurisdiction-service/service/handlers/get-jurisdictions-handler.ts b/packages/mds-jurisdiction-service/service/handlers/get-jurisdictions-handler.ts new file mode 100644 index 000000000..bb569310e --- /dev/null +++ b/packages/mds-jurisdiction-service/service/handlers/get-jurisdictions-handler.ts @@ -0,0 +1,34 @@ +/* + Copyright 2019-2020 City of Los Angeles. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +import { ServiceResponse, ServiceResult, ServiceException } from '@mds-core/mds-service-helpers' +import logger from '@mds-core/mds-logger' +import { RepositoryError } from '@mds-core/mds-repository' +import { GetJurisdictionsOptions, JurisdictionDomainModel } from '../../@types' +import { JurisdictionRepository } from '../repository' + +export const getJurisdictions = async ( + options?: GetJurisdictionsOptions +): Promise> => { + try { + const jurisdicitons = await JurisdictionRepository.readJurisdictions(options) + return ServiceResult(jurisdicitons) + } catch (error) /* istanbul ignore next */ { + const exception = ServiceException('Error Reading Jurisdictions', RepositoryError(error)) + logger.error(exception, error) + return exception + } +} diff --git a/packages/mds-jurisdiction-service/service/handlers/index.ts b/packages/mds-jurisdiction-service/service/handlers/index.ts new file mode 100644 index 000000000..6fd119a73 --- /dev/null +++ b/packages/mds-jurisdiction-service/service/handlers/index.ts @@ -0,0 +1,22 @@ +/* + Copyright 2019-2020 City of Los Angeles. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +export * from './create-jurisdiction-handler' +export * from './create-jurisdictions-handler' +export * from './delete-jurisdiction-handler' +export * from './get-jurisdiction-handler' +export * from './get-jurisdictions-handler' +export * from './update-jurisdiction-handler' diff --git a/packages/mds-jurisdiction-service/service/handlers/update-jurisdiction-handler.ts b/packages/mds-jurisdiction-service/service/handlers/update-jurisdiction-handler.ts new file mode 100644 index 000000000..02b59e5aa --- /dev/null +++ b/packages/mds-jurisdiction-service/service/handlers/update-jurisdiction-handler.ts @@ -0,0 +1,35 @@ +/* + Copyright 2019-2020 City of Los Angeles. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +import { ServiceResponse, ServiceResult, ServiceException } from '@mds-core/mds-service-helpers' +import logger from '@mds-core/mds-logger' +import { RepositoryError } from '@mds-core/mds-repository' +import { UpdateJurisdictionDomainModel, JurisdictionDomainModel, JurisdictionIdType } from '../../@types' +import { JurisdictionRepository } from '../repository' + +export const updateJurisdiction = async ( + jurisdiction_id: JurisdictionIdType, + jurisdiction: UpdateJurisdictionDomainModel +): Promise> => { + try { + const updated = await JurisdictionRepository.updateJurisdiction(jurisdiction_id, jurisdiction) + return ServiceResult(updated) + } catch (error) /* istanbul ignore next */ { + const exception = ServiceException('Error Updating Jurisdiction', RepositoryError(error)) + logger.error(exception, error) + return exception + } +} diff --git a/packages/mds-jurisdiction-service/service/provider.ts b/packages/mds-jurisdiction-service/service/provider.ts new file mode 100644 index 000000000..e03daf36d --- /dev/null +++ b/packages/mds-jurisdiction-service/service/provider.ts @@ -0,0 +1,26 @@ +/* + Copyright 2019-2020 City of Los Angeles. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +import { ServiceProvider, ProcessController } from '@mds-core/mds-service-helpers' +import { JurisdictionRepository } from './repository' +import { JurisdictionService } from '../@types' +import * as handlers from './handlers' + +export const JurisdictionServiceProvider: ServiceProvider & ProcessController = { + start: JurisdictionRepository.initialize, + stop: JurisdictionRepository.shutdown, + ...handlers +} diff --git a/packages/mds-jurisdiction-service/service/repository/entities/index.ts b/packages/mds-jurisdiction-service/service/repository/entities/index.ts new file mode 100644 index 000000000..9219d5d57 --- /dev/null +++ b/packages/mds-jurisdiction-service/service/repository/entities/index.ts @@ -0,0 +1,17 @@ +/* + Copyright 2019-2020 City of Los Angeles. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +export { JurisdictionEntity } from './jurisdiction-entity' diff --git a/packages/mds-jurisdiction-service/service/repository/entities/jurisdiction-entity.ts b/packages/mds-jurisdiction-service/service/repository/entities/jurisdiction-entity.ts new file mode 100644 index 000000000..065ac0d2c --- /dev/null +++ b/packages/mds-jurisdiction-service/service/repository/entities/jurisdiction-entity.ts @@ -0,0 +1,45 @@ +/* + Copyright 2019-2020 City of Los Angeles. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +import { Entity, Column, Index } from 'typeorm' +import { Nullable } from '@mds-core/mds-types' +import { IdentityColumn, RecordedColumn } from '@mds-core/mds-repository' +import { JurisdictionDomainModel } from '../../../@types' + +export interface JurisdictionVersionedProperties { + timestamp: JurisdictionDomainModel['timestamp'] + agency_name: JurisdictionDomainModel['agency_name'] + geography_id: Nullable +} + +export interface JurisdictionEntityModel extends IdentityColumn, RecordedColumn { + jurisdiction_id: JurisdictionDomainModel['jurisdiction_id'] + agency_key: JurisdictionDomainModel['agency_key'] + versions: JurisdictionVersionedProperties[] +} + +@Entity('jurisdictions') +export class JurisdictionEntity extends IdentityColumn(RecordedColumn(class {})) implements JurisdictionEntityModel { + @Column('uuid', { primary: true }) + jurisdiction_id: JurisdictionEntityModel['jurisdiction_id'] + + @Column('varchar', { length: 63 }) + @Index({ unique: true }) + agency_key: JurisdictionEntityModel['agency_key'] + + @Column('json') + versions: JurisdictionVersionedProperties[] +} diff --git a/packages/mds-jurisdiction-service/service/repository/index.ts b/packages/mds-jurisdiction-service/service/repository/index.ts new file mode 100644 index 000000000..4a531f002 --- /dev/null +++ b/packages/mds-jurisdiction-service/service/repository/index.ts @@ -0,0 +1,172 @@ +/* + Copyright 2019-2020 City of Los Angeles. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +import { InsertReturning, UpdateReturning, ReadWriteRepository } from '@mds-core/mds-repository' + +import { ValidationError, ConflictError, NotFoundError, filterDefined } from '@mds-core/mds-utils' + +import { JurisdictionEntity } from './entities' +import * as migrations from './migrations' +import { + JurisdictionDomainModel, + GetJurisdictionsOptions, + UpdateJurisdictionDomainModel, + CreateJurisdictionDomainModel, + JurisdictionIdType +} from '../../@types' +import { JurisdictionEntityToDomain, JurisdictionDomainToEntityCreate } from './mappers' + +class JurisdictionReadWriteRepository extends ReadWriteRepository { + public createJurisdictions = async ( + jurisdictions: CreateJurisdictionDomainModel[] + ): Promise => { + const connection = await this.connect('rw') + + const { raw: entities }: InsertReturning = await connection + .getRepository(JurisdictionEntity) + .createQueryBuilder() + .insert() + .values(jurisdictions.map(JurisdictionDomainToEntityCreate.mapper())) + .returning('*') + .execute() + + return entities.map(JurisdictionEntityToDomain.mapper()).filter(filterDefined()) + } + + public deleteJurisdiction = async ( + jurisdiction_id: JurisdictionIdType + ): Promise> => { + const connection = await this.connect('rw') + + const entity = await connection.getRepository(JurisdictionEntity).findOne({ where: { jurisdiction_id } }) + if (!entity) { + throw new NotFoundError(`Jurisdiction ${jurisdiction_id} Not Found`) + } + const { id, ...current } = entity + + const jurisdiction = JurisdictionEntityToDomain.map(entity) + if (!jurisdiction) { + throw new NotFoundError(`Jurisdiction ${jurisdiction_id} Not Found`) + } + + await connection + .getRepository(JurisdictionEntity) + .createQueryBuilder() + .update() + .set({ + ...current, + versions: [ + { + agency_name: jurisdiction.agency_name, + geography_id: null, + timestamp: Date.now() + }, + ...current.versions + ].sort((a, b) => b.timestamp - a.timestamp) + }) + .where('jurisdiction_id = :jurisdiction_id', { jurisdiction_id }) + .returning('*') + .execute() + + return { jurisdiction_id } + } + + public readJurisdiction = async ( + jurisdiction_id: JurisdictionIdType, + options?: GetJurisdictionsOptions + ): Promise => { + const connection = await this.connect('ro') + const entity = await connection.getRepository(JurisdictionEntity).findOne({ where: { jurisdiction_id } }) + if (!entity) { + throw new NotFoundError(`Jurisdiction ${jurisdiction_id} Not Found`) + } + const jurisdiction = JurisdictionEntityToDomain.map(entity, options) + if (!jurisdiction) { + throw new NotFoundError(`Jurisdiction ${jurisdiction_id} Not Found`) + } + return jurisdiction + } + + public readJurisdictions = async (options?: GetJurisdictionsOptions): Promise => { + const connection = await this.connect('ro') + const entities = await connection.getRepository(JurisdictionEntity).find() + return entities.map(JurisdictionEntityToDomain.mapper(options)).filter(filterDefined()) + } + + public updateJurisdiction = async ( + jurisdiction_id: JurisdictionIdType, + patch: UpdateJurisdictionDomainModel + ): Promise => { + const connection = await this.connect('rw') + + if (patch.jurisdiction_id && patch.jurisdiction_id !== jurisdiction_id) { + throw new ConflictError(`Invalid jurisdiction_id ${patch.jurisdiction_id}. Must match ${jurisdiction_id}.`) + } + + const entity = await connection.getRepository(JurisdictionEntity).findOne({ where: { jurisdiction_id } }) + if (!entity) { + throw new NotFoundError(`Jurisdiction ${jurisdiction_id} Not Found`) + } + const { id, ...current } = entity + + const jurisdiction = JurisdictionEntityToDomain.map(entity) + if (!jurisdiction) { + throw new NotFoundError(`Jurisdiction ${jurisdiction_id} Not Found`) + } + + const timestamp = patch.timestamp ?? Date.now() + if (timestamp <= jurisdiction.timestamp) { + throw new ValidationError(`Invalid timestamp ${timestamp}. Must be greater than ${jurisdiction.timestamp}.`) + } + + const { + raw: [updated] + }: UpdateReturning = await connection + .getRepository(JurisdictionEntity) + .createQueryBuilder() + .update() + .set({ + ...current, + agency_key: patch.agency_key ?? jurisdiction.agency_key, + versions: + (patch.agency_name && patch.agency_name !== jurisdiction.agency_name) || + (patch.geography_id && patch.geography_id !== jurisdiction.geography_id) + ? [ + { + agency_name: patch.agency_name ?? jurisdiction.agency_name, + geography_id: patch.geography_id ?? jurisdiction.geography_id, + timestamp + }, + ...current.versions + ].sort((a, b) => b.timestamp - a.timestamp) + : current.versions + }) + .where('jurisdiction_id = :jurisdiction_id', { jurisdiction_id }) + .returning('*') + .execute() + + return { ...jurisdiction, ...JurisdictionEntityToDomain.map(updated) } + } + + constructor() { + super('jurisdictions', { + entities: [JurisdictionEntity], + migrations: Object.values(migrations) + }) + } +} + +export const JurisdictionRepository = new JurisdictionReadWriteRepository() diff --git a/packages/mds-jurisdiction-service/service/repository/mappers.ts b/packages/mds-jurisdiction-service/service/repository/mappers.ts new file mode 100644 index 000000000..9e7d73d07 --- /dev/null +++ b/packages/mds-jurisdiction-service/service/repository/mappers.ts @@ -0,0 +1,74 @@ +/* + Copyright 2019-2020 City of Los Angeles. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +import { Timestamp, Nullable, Optional } from '@mds-core/mds-types' +import { ModelMapper, IdentityColumn, RecordedColumn } from '@mds-core/mds-repository' +import { uuid } from '@mds-core/mds-utils' +import { JurisdictionDomainModel, CreateJurisdictionDomainModel } from '../../@types' +import { JurisdictionEntityModel } from './entities/jurisdiction-entity' + +type MapJurisdictionEntityToDomainModelOptions = Partial<{ + effective: Timestamp +}> + +export const JurisdictionEntityToDomain = ModelMapper< + JurisdictionEntityModel, + Nullable, + MapJurisdictionEntityToDomainModelOptions +>((entity, options) => { + const { effective = Date.now() } = options ?? {} + const { jurisdiction_id, agency_key, versions } = entity + const version = versions.find(properties => effective >= properties.timestamp) + if (version) { + const { agency_name, geography_id, timestamp } = version + if (geography_id !== null) { + const domain = { + jurisdiction_id, + agency_key, + agency_name, + geography_id, + timestamp + } + return domain + } + } + return null +}) + +type JurisdictionDomainToEntityCreateOptions = Partial<{ + recorded: Timestamp +}> + +type JurisdictionDomainToEntityCreateModel = Omit< + Optional, + keyof IdentityColumn +> + +export const JurisdictionDomainToEntityCreate = ModelMapper< + CreateJurisdictionDomainModel, + JurisdictionDomainToEntityCreateModel, + JurisdictionDomainToEntityCreateOptions +>((domain, options) => { + const { recorded } = options ?? {} + const { jurisdiction_id = uuid(), agency_key, agency_name, geography_id, timestamp = Date.now() } = domain + const entity = { + jurisdiction_id, + agency_key, + versions: [{ timestamp, agency_name, geography_id }], + recorded + } + return entity +}) diff --git a/packages/mds-jurisdiction-service/service/repository/migrations/1582294819607-CreateJurisdictionsTable.ts b/packages/mds-jurisdiction-service/service/repository/migrations/1582294819607-CreateJurisdictionsTable.ts new file mode 100644 index 000000000..4a3607a0c --- /dev/null +++ b/packages/mds-jurisdiction-service/service/repository/migrations/1582294819607-CreateJurisdictionsTable.ts @@ -0,0 +1,41 @@ +/* + Copyright 2019-2020 City of Los Angeles. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +import { MigrationInterface, QueryRunner } from 'typeorm' + +export class CreateJurisdictionsTable1582294819607 implements MigrationInterface { + name = 'CreateJurisdictionsTable1582294819607' + + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query( + `CREATE TABLE "jurisdictions" ("recorded" bigint NOT NULL DEFAULT (extract(epoch from now()) * 1000)::bigint, "id" bigint GENERATED ALWAYS AS IDENTITY, "jurisdiction_id" uuid NOT NULL, "agency_key" character varying(63) NOT NULL, "versions" json NOT NULL, CONSTRAINT "jurisdictions_pkey" PRIMARY KEY ("jurisdiction_id"))`, + undefined + ) + await queryRunner.query(`CREATE UNIQUE INDEX "idx_id_jurisdictions" ON "jurisdictions" ("id") `, undefined) + await queryRunner.query(`CREATE INDEX "idx_recorded_jurisdictions" ON "jurisdictions" ("recorded") `, undefined) + await queryRunner.query( + `CREATE UNIQUE INDEX "idx_agency_key_jurisdictions" ON "jurisdictions" ("agency_key") `, + undefined + ) + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query(`DROP INDEX "idx_agency_key_jurisdictions"`, undefined) + await queryRunner.query(`DROP INDEX "idx_recorded_jurisdictions"`, undefined) + await queryRunner.query(`DROP INDEX "idx_id_jurisdictions"`, undefined) + await queryRunner.query(`DROP TABLE "jurisdictions"`, undefined) + } +} diff --git a/packages/mds-jurisdiction-service/service/repository/migrations/index.ts b/packages/mds-jurisdiction-service/service/repository/migrations/index.ts new file mode 100644 index 000000000..bfe64407d --- /dev/null +++ b/packages/mds-jurisdiction-service/service/repository/migrations/index.ts @@ -0,0 +1,17 @@ +/* + Copyright 2019-2020 City of Los Angeles. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +export { CreateJurisdictionsTable1582294819607 } from './1582294819607-CreateJurisdictionsTable' diff --git a/packages/mds-jurisdiction-service/service/validators.ts b/packages/mds-jurisdiction-service/service/validators.ts new file mode 100644 index 000000000..5b7c46905 --- /dev/null +++ b/packages/mds-jurisdiction-service/service/validators.ts @@ -0,0 +1,44 @@ +/* + Copyright 2019-2020 City of Los Angeles. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +import { + uuidSchema, + stringSchema, + timestampSchema, + ValidateSchema, + ValidationError, + SchemaBuilder +} from '@mds-core/mds-schema-validators' +import { CreateJurisdictionDomainModel } from '../@types' + +const createJurisdictionDomainModelSchema = SchemaBuilder.object().keys({ + jurisdiction_id: uuidSchema.optional(), + agency_key: stringSchema, + agency_name: stringSchema, + geography_id: uuidSchema, + timestamp: timestampSchema.optional() +}) + +export const ValidateJurisdictionForCreate = ( + jurisdiction: CreateJurisdictionDomainModel +): CreateJurisdictionDomainModel => { + try { + ValidateSchema(jurisdiction, createJurisdictionDomainModelSchema) + return jurisdiction + } catch (error) { + throw new ValidationError('Invalid Jurisdiction', { jurisdiction, error }) + } +} diff --git a/packages/mds-jurisdiction-service/tests/index.spec.ts b/packages/mds-jurisdiction-service/tests/index.spec.ts new file mode 100644 index 000000000..1ec924548 --- /dev/null +++ b/packages/mds-jurisdiction-service/tests/index.spec.ts @@ -0,0 +1,236 @@ +/* + Copyright 2019-2020 City of Los Angeles. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +import test from 'unit.js' +import { uuid, days } from '@mds-core/mds-utils' +import { ProcessManager } from '@mds-core/mds-service-helpers' +import { JurisdictionServiceClient } from '../index' +import { JurisdictionServiceProvider } from '../service/provider' + +const records = 5_000 + +const JURISDICTION_ID = uuid() +const TODAY = Date.now() +const YESTERDAY = TODAY - days(1) +const LAST_WEEK = TODAY - days(7) + +const controller = ProcessManager(JurisdictionServiceProvider).controller() + +describe('Write/Read Jurisdictions', () => { + before(async () => { + await controller.start() + }) + + it(`Write ${records} Jurisdiction${records > 1 ? 's' : ''}`, async () => { + try { + const jurisdictions = await JurisdictionServiceClient.createJurisdictions( + Array.from({ length: records }, (_, index) => ({ + jurisdiction_id: index ? uuid() : JURISDICTION_ID, + agency_key: `agency-key-${index}`, + agency_name: `Agency Name ${index}`, + timestamp: YESTERDAY, + geography_id: uuid() + })) + ) + test.value(jurisdictions[0].jurisdiction_id).is(JURISDICTION_ID) + } catch (error) { + test.value(error).is(null) + } + }) + + it(`Read ${records} Jurisdiction${records > 1 ? 's' : ''}`, async () => { + try { + const jurisdictions = await JurisdictionServiceClient.getJurisdictions() + test.value(jurisdictions.length).is(records) + test.value(jurisdictions[0].jurisdiction_id).is(JURISDICTION_ID) + } catch (error) { + test.value(error).is(null) + } + }) + + it('Write One Jurisdiction', async () => { + try { + const jurisdiction = await JurisdictionServiceClient.createJurisdiction({ + agency_key: 'agency-key-one', + agency_name: 'Agency Name One', + geography_id: uuid() + }) + test.value(jurisdiction).isNot(null) + test.value(jurisdiction.jurisdiction_id).isNot(null) + test.value(jurisdiction.timestamp).isNot(null) + } catch (error) { + test.value(error).is(null) + } + }) + + it('Write One Jurisdiction (duplicate id)', async () => { + try { + const result = await JurisdictionServiceClient.createJurisdiction({ + jurisdiction_id: JURISDICTION_ID, + agency_key: 'agency-key-two', + agency_name: 'Agency Name One', + geography_id: uuid() + }) + test.value(result).is(null) + } catch (error) { + test.value(error.type).is('ConflictError') + } + }) + + it('Write One Jurisdiction (duplicate key)', async () => { + try { + const result = await JurisdictionServiceClient.createJurisdiction({ + agency_key: 'agency-key-one', + agency_name: 'Agency Name One', + geography_id: uuid() + }) + test.value(result).is(null) + } catch (error) { + test.value(error.type).is('ConflictError') + } + }) + + it('Write One Jurisdiction (validation error)', async () => { + try { + const result = await JurisdictionServiceClient.createJurisdiction({ + agency_key: '', + agency_name: 'Agency Name One', + geography_id: uuid() + }) + test.value(result).is(null) + } catch (error) { + test.value(error.type).is('ValidationError') + } + }) + + it('Update One Jurisdiction (invalid jurisdiction_id)', async () => { + try { + const result = await JurisdictionServiceClient.updateJurisdiction(JURISDICTION_ID, { + jurisdiction_id: uuid(), + agency_name: 'Some New Name', + timestamp: TODAY + }) + test.value(result).is(null) + } catch (error) { + test.value(error.type).is('ConflictError') + } + }) + + it('Update One Jurisdiction (invalid timestamp)', async () => { + try { + const result = await JurisdictionServiceClient.updateJurisdiction(JURISDICTION_ID, { + agency_name: 'Some New Name', + timestamp: LAST_WEEK + }) + test.value(result).is(null) + } catch (error) { + test.value(error.type).is('ValidationError') + } + }) + + it('Update One Jurisdiction (not found)', async () => { + try { + const result = await JurisdictionServiceClient.updateJurisdiction(uuid(), { + agency_name: 'Some New Name', + timestamp: TODAY + }) + test.value(result).is(null) + } catch (error) { + test.value(error.type).is('NotFoundError') + } + }) + + it('Update One Jurisdiction', async () => { + try { + const jurisdiction = await JurisdictionServiceClient.updateJurisdiction(JURISDICTION_ID, { + agency_name: 'Some New Name', + timestamp: TODAY + }) + test.value(jurisdiction).isNot(null) + test.value(jurisdiction.jurisdiction_id).is(JURISDICTION_ID) + test.value(jurisdiction.timestamp).is(TODAY) + } catch (error) { + test.value(error).is(null) + } + }) + + it('Read Specific Jurisdiction (current version)', async () => { + try { + const jurisdiction = await JurisdictionServiceClient.getJurisdiction(JURISDICTION_ID) + test.value(jurisdiction).isNot(null) + test.value(jurisdiction.jurisdiction_id).is(JURISDICTION_ID) + test.value(jurisdiction.timestamp).is(TODAY) + } catch (error) { + test.value(error).is(null) + } + }) + + it('Read Specific Jurisdiction (prior version)', async () => { + try { + const jurisdiction = await JurisdictionServiceClient.getJurisdiction(JURISDICTION_ID, { + effective: YESTERDAY + }) + test.value(jurisdiction).isNot(null) + test.value(jurisdiction.jurisdiction_id).is(JURISDICTION_ID) + test.value(jurisdiction.timestamp).is(YESTERDAY) + } catch (error) { + test.value(error).is(null) + } + }) + + it('Read Specific Jurisdiction (no version)', async () => { + try { + const result = await JurisdictionServiceClient.getJurisdiction(JURISDICTION_ID, { + effective: LAST_WEEK + }) + test.value(result).is(null) + } catch (error) { + test.value(error.type).is('NotFoundError') + } + }) + + it('Read Missing Jurisdiction', async () => { + try { + const result = await JurisdictionServiceClient.getJurisdiction(uuid()) + test.value(result).is(null) + } catch (error) { + test.value(error.type).is('NotFoundError') + } + }) + + it('Delete One Jurisdiction', async () => { + try { + const jurisdiction = await JurisdictionServiceClient.deleteJurisdiction(JURISDICTION_ID) + test.value(jurisdiction).isNot(null) + test.value(jurisdiction.jurisdiction_id).is(JURISDICTION_ID) + } catch (error) { + test.value(error).is(null) + } + }) + + it('Delete One Jurisdiction (not found)', async () => { + try { + const result = await JurisdictionServiceClient.deleteJurisdiction(JURISDICTION_ID) + test.value(result).is(null) + } catch (error) { + test.value(error.type).is('NotFoundError') + } + }) + + after(async () => { + await controller.stop() + }) +}) diff --git a/packages/mds-jurisdiction-service/tsconfig.build.json b/packages/mds-jurisdiction-service/tsconfig.build.json new file mode 100644 index 000000000..8b43821ae --- /dev/null +++ b/packages/mds-jurisdiction-service/tsconfig.build.json @@ -0,0 +1,14 @@ +{ + "extends": "../../tsconfig.build.json", + "compilerOptions": { + "outDir": "dist" + }, + "references": [ + { "path": "../../packages/mds-logger/tsconfig.build.json" }, + { "path": "../../packages/mds-repository/tsconfig.build.json" }, + { "path": "../../packages/mds-schema-validators/tsconfig.build.json" }, + { "path": "../../packages/mds-service-helpers/tsconfig.build.json" }, + { "path": "../../packages/mds-types/tsconfig.build.json" }, + { "path": "../../packages/mds-utils/tsconfig.build.json" } + ] +} diff --git a/packages/mds-jurisdiction-service/tsconfig.eslint.json b/packages/mds-jurisdiction-service/tsconfig.eslint.json new file mode 100644 index 000000000..2a400942a --- /dev/null +++ b/packages/mds-jurisdiction-service/tsconfig.eslint.json @@ -0,0 +1 @@ +{ "extends": "../../tsconfig.eslint.json" } diff --git a/packages/mds-jurisdiction/@types/index.ts b/packages/mds-jurisdiction/@types/index.ts new file mode 100644 index 000000000..229d836df --- /dev/null +++ b/packages/mds-jurisdiction/@types/index.ts @@ -0,0 +1,33 @@ +/* + Copyright 2019-2020 City of Los Angeles. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +import { ApiRequest, ApiVersionedResponse, ApiClaims } from '@mds-core/mds-api-server' +import { Params, ParamsDictionary } from 'express-serve-static-core' + +export const JURISDICTION_API_SUPPORTED_VERSIONS = ['0.1.0'] as const +export type JURISDICTION_API_SUPPORTED_VERSION = typeof JURISDICTION_API_SUPPORTED_VERSIONS[number] +export const [JURISDICTION_API_DEFAULT_VERSION] = JURISDICTION_API_SUPPORTED_VERSIONS + +// Allow adding type definitions for Express Request objects +export type JurisdictionApiRequest

= ApiRequest

+ +export type JurisdictionApiAccessTokenScopes = 'jurisdictions:read' | 'jurisdictions:read:claim' | 'jurisdictions:write' + +export type JurisdictionApiResponse = ApiVersionedResponse< + JURISDICTION_API_SUPPORTED_VERSION, + ApiClaims, + TBody +> diff --git a/packages/mds-jurisdiction/api.ts b/packages/mds-jurisdiction/api.ts new file mode 100644 index 000000000..5c55c64bb --- /dev/null +++ b/packages/mds-jurisdiction/api.ts @@ -0,0 +1,64 @@ +/* + Copyright 2019-2020 City of Los Angeles. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +import express from 'express' +import { pathsFor } from '@mds-core/mds-utils' +import { checkAccess, AccessTokenScopeValidator } from '@mds-core/mds-api-server' +import { JurisdictionApiVersionMiddleware } from './middleware' +import { + CreateJurisdictionHandler, + DeleteJurisdictionHandler, + GetJurisdictionsHandler, + GetJurisdictionHandler, + UpdateJurisdictionHandler +} from './handlers' +import { JurisdictionApiAccessTokenScopes } from './@types' + +const checkJurisdictionApiAccess = (validator: AccessTokenScopeValidator) => + checkAccess(validator) + +export const api = (app: express.Express): express.Express => + app + .use(JurisdictionApiVersionMiddleware) + .get( + pathsFor('/jurisdictions'), + checkJurisdictionApiAccess( + scopes => scopes.includes('jurisdictions:read') || scopes.includes('jurisdictions:read:claim') + ), + GetJurisdictionsHandler + ) + .get( + pathsFor('/jurisdictions/:jurisdiction_id'), + checkJurisdictionApiAccess( + scopes => scopes.includes('jurisdictions:read') || scopes.includes('jurisdictions:read:claim') + ), + GetJurisdictionHandler + ) + .post( + pathsFor('/jurisdictions'), + checkJurisdictionApiAccess(scopes => scopes.includes('jurisdictions:write')), + CreateJurisdictionHandler + ) + .put( + pathsFor('/jurisdictions/:jurisdiction_id'), + checkJurisdictionApiAccess(scopes => scopes.includes('jurisdictions:write')), + UpdateJurisdictionHandler + ) + .delete( + pathsFor('/jurisdictions/:jurisdiction_id'), + checkJurisdictionApiAccess(scopes => scopes.includes('jurisdictions:write')), + DeleteJurisdictionHandler + ) diff --git a/packages/mds-jurisdiction/handlers/create-jurisdiction.ts b/packages/mds-jurisdiction/handlers/create-jurisdiction.ts new file mode 100644 index 000000000..7715aa6d4 --- /dev/null +++ b/packages/mds-jurisdiction/handlers/create-jurisdiction.ts @@ -0,0 +1,59 @@ +/* + Copyright 2019-2020 City of Los Angeles. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +import { + JurisdictionServiceClient, + CreateJurisdictionDomainModel, + JurisdictionDomainModel +} from '@mds-core/mds-jurisdiction-service' +import { isServiceError } from '@mds-core/mds-service-helpers' +import { JurisdictionApiRequest, JurisdictionApiResponse } from '../@types' + +interface CreateJurisdictionRequest extends JurisdictionApiRequest { + body: CreateJurisdictionDomainModel | CreateJurisdictionDomainModel[] +} + +type CreateJurisdictionResponse = JurisdictionApiResponse< + | { + jurisdiction: JurisdictionDomainModel + } + | { + jurisdictions: JurisdictionDomainModel[] + } +> + +export const CreateJurisdictionHandler = async (req: CreateJurisdictionRequest, res: CreateJurisdictionResponse) => { + try { + const jurisdictions = await JurisdictionServiceClient.createJurisdictions( + Array.isArray(req.body) ? req.body : [req.body] + ) + const { version } = res.locals + if (!Array.isArray(req.body)) { + const [jurisdiction] = jurisdictions + return res.status(201).send({ version, jurisdiction }) + } + return res.status(201).send({ version, jurisdictions }) + } catch (error) { + if (isServiceError(error)) + if (error.type === 'ValidationError') { + return res.status(400).send({ error }) + } + if (error.type === 'ConflictError') { + return res.status(409).send({ error }) + } + return res.status(500).send({ error }) + } +} diff --git a/packages/mds-jurisdiction/handlers/delete-jurisdiction.ts b/packages/mds-jurisdiction/handlers/delete-jurisdiction.ts new file mode 100644 index 000000000..b9dfa07ab --- /dev/null +++ b/packages/mds-jurisdiction/handlers/delete-jurisdiction.ts @@ -0,0 +1,43 @@ +/* + Copyright 2019-2020 City of Los Angeles. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +import { + JurisdictionServiceClient, + JurisdictionDomainModel, + JurisdictionIdType +} from '@mds-core/mds-jurisdiction-service' +import { isServiceError } from '@mds-core/mds-service-helpers' +import { JurisdictionApiRequest, JurisdictionApiResponse } from '../@types' + +type DeleteJurisdictionRequest = JurisdictionApiRequest<{ jurisdiction_id: JurisdictionIdType }> + +type DeleteJurisdictionResponse = JurisdictionApiResponse> + +export const DeleteJurisdictionHandler = async (req: DeleteJurisdictionRequest, res: DeleteJurisdictionResponse) => { + const { jurisdiction_id } = req.params + try { + const result = await JurisdictionServiceClient.deleteJurisdiction(jurisdiction_id) + const { version } = res.locals + return res.status(200).send({ version, ...result }) + } catch (error) { + if (isServiceError(error)) { + if (error.type === 'NotFoundError') { + return res.status(404).send({ error }) + } + } + return res.status(500).send({ error }) + } +} diff --git a/packages/mds-jurisdiction/handlers/get-jurisdiction.ts b/packages/mds-jurisdiction/handlers/get-jurisdiction.ts new file mode 100644 index 000000000..20deddeba --- /dev/null +++ b/packages/mds-jurisdiction/handlers/get-jurisdiction.ts @@ -0,0 +1,52 @@ +/* + Copyright 2019-2020 City of Los Angeles. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +import { + JurisdictionServiceClient, + JurisdictionDomainModel, + JurisdictionIdType +} from '@mds-core/mds-jurisdiction-service' +import { AuthorizationError } from '@mds-core/mds-utils' +import { isServiceError } from '@mds-core/mds-service-helpers' +import { parseRequest } from '@mds-core/mds-api-helpers' +import { ApiQuery } from '@mds-core/mds-api-server' +import { JurisdictionApiResponse, JurisdictionApiRequest } from '../@types' +import { HasJurisdictionClaim } from './utils' + +type GetJurisdictionRequest = JurisdictionApiRequest<{ jurisdiction_id: JurisdictionIdType }> & ApiQuery<'effective'> + +type GetJurisdictionResponse = JurisdictionApiResponse<{ + jurisdiction: JurisdictionDomainModel +}> + +export const GetJurisdictionHandler = async (req: GetJurisdictionRequest, res: GetJurisdictionResponse) => { + try { + const { jurisdiction_id } = req.params + const { effective } = parseRequest(req, { parser: Number }).query('effective') + const jurisdiction = await JurisdictionServiceClient.getJurisdiction(jurisdiction_id, { effective }) + const { version } = res.locals + return HasJurisdictionClaim(res)(jurisdiction) + ? res.status(200).send({ version, jurisdiction }) + : res.status(403).send({ error: new AuthorizationError('Access Denied', { jurisdiction_id }) }) + } catch (error) { + if (isServiceError(error)) { + if (error.type === 'NotFoundError') { + return res.status(404).send({ error }) + } + } + return res.status(500).send({ error }) + } +} diff --git a/packages/mds-jurisdiction/handlers/get-jurisdictions.ts b/packages/mds-jurisdiction/handlers/get-jurisdictions.ts new file mode 100644 index 000000000..367fa7b53 --- /dev/null +++ b/packages/mds-jurisdiction/handlers/get-jurisdictions.ts @@ -0,0 +1,38 @@ +/* + Copyright 2019-2020 City of Los Angeles. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +import { JurisdictionServiceClient, JurisdictionDomainModel } from '@mds-core/mds-jurisdiction-service' +import { parseRequest } from '@mds-core/mds-api-helpers' +import { ApiQuery } from '@mds-core/mds-api-server' +import { HasJurisdictionClaim } from './utils' +import { JurisdictionApiRequest, JurisdictionApiResponse } from '../@types' + +type GetJurisdictionsRequest = JurisdictionApiRequest & ApiQuery<'effective'> + +type GetJurisdictionsResponse = JurisdictionApiResponse<{ + jurisdictions: JurisdictionDomainModel[] +}> + +export const GetJurisdictionsHandler = async (req: GetJurisdictionsRequest, res: GetJurisdictionsResponse) => { + try { + const { effective } = parseRequest(req, { parser: Number }).query('effective') + const jurisdictions = await JurisdictionServiceClient.getJurisdictions({ effective }) + const { version } = res.locals + return res.status(200).send({ version, jurisdictions: jurisdictions.filter(HasJurisdictionClaim(res)) }) + } catch (error) { + return res.status(500).send({ error }) + } +} diff --git a/packages/mds-jurisdiction/handlers/index.ts b/packages/mds-jurisdiction/handlers/index.ts new file mode 100644 index 000000000..b7f2e41f4 --- /dev/null +++ b/packages/mds-jurisdiction/handlers/index.ts @@ -0,0 +1,21 @@ +/* + Copyright 2019-2020 City of Los Angeles. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +export * from './create-jurisdiction' +export * from './delete-jurisdiction' +export * from './get-jurisdiction' +export * from './get-jurisdictions' +export * from './update-jurisdiction' diff --git a/packages/mds-jurisdiction/handlers/update-jurisdiction.ts b/packages/mds-jurisdiction/handlers/update-jurisdiction.ts new file mode 100644 index 000000000..144debecc --- /dev/null +++ b/packages/mds-jurisdiction/handlers/update-jurisdiction.ts @@ -0,0 +1,54 @@ +/* + Copyright 2019-2020 City of Los Angeles. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +import { + UpdateJurisdictionDomainModel, + JurisdictionServiceClient, + JurisdictionDomainModel, + JurisdictionIdType +} from '@mds-core/mds-jurisdiction-service' +import { isServiceError } from '@mds-core/mds-service-helpers' +import { JurisdictionApiRequest, JurisdictionApiResponse } from '../@types' + +interface UpdateJurisdictionRequest extends JurisdictionApiRequest<{ jurisdiction_id: JurisdictionIdType }> { + body: UpdateJurisdictionDomainModel +} + +type UpdateJurisdictionResponse = JurisdictionApiResponse<{ + jurisdiction: JurisdictionDomainModel +}> + +export const UpdateJurisdictionHandler = async (req: UpdateJurisdictionRequest, res: UpdateJurisdictionResponse) => { + try { + const { jurisdiction_id } = req.params + const { version } = res.locals + const jurisdiction = await JurisdictionServiceClient.updateJurisdiction(jurisdiction_id, req.body) + return res.status(200).send({ version, jurisdiction }) + } catch (error) { + if (isServiceError(error)) { + if (error.type === 'ValidationError') { + return res.status(400).send({ error }) + } + if (error.type === 'NotFoundError') { + return res.status(404).send({ error }) + } + if (error.type === 'ConflictError') { + return res.status(409).send({ error }) + } + } + return res.status(500).send({ error }) + } +} diff --git a/packages/mds-jurisdiction/handlers/utils.ts b/packages/mds-jurisdiction/handlers/utils.ts new file mode 100644 index 000000000..cfaefd976 --- /dev/null +++ b/packages/mds-jurisdiction/handlers/utils.ts @@ -0,0 +1,24 @@ +/* + Copyright 2019-2020 City of Los Angeles. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +import { JurisdictionDomainModel } from '@mds-core/mds-jurisdiction-service' +import { JurisdictionApiResponse } from '../@types' + +export const HasJurisdictionClaim = (res: JurisdictionApiResponse) => ( + jurisdiction: JurisdictionDomainModel +): boolean => + res.locals.scopes.includes('jurisdictions:read') || + (res.locals.claims?.jurisdictions?.split(' ') ?? []).includes(jurisdiction.agency_key) diff --git a/packages/mds-native/index.ts b/packages/mds-jurisdiction/index.ts similarity index 92% rename from packages/mds-native/index.ts rename to packages/mds-jurisdiction/index.ts index be0bda2d2..cd1d05462 100644 --- a/packages/mds-native/index.ts +++ b/packages/mds-jurisdiction/index.ts @@ -1,5 +1,5 @@ /* - Copyright 2019 City of Los Angeles. + Copyright 2019-2020 City of Los Angeles. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/packages/mds-jurisdiction/middleware/index.ts b/packages/mds-jurisdiction/middleware/index.ts new file mode 100644 index 000000000..1919c5e56 --- /dev/null +++ b/packages/mds-jurisdiction/middleware/index.ts @@ -0,0 +1,17 @@ +/* + Copyright 2019-2020 City of Los Angeles. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +export * from './jurisdiction-api-version' diff --git a/packages/mds-jurisdiction/middleware/jurisdiction-api-version.ts b/packages/mds-jurisdiction/middleware/jurisdiction-api-version.ts new file mode 100644 index 000000000..265171aca --- /dev/null +++ b/packages/mds-jurisdiction/middleware/jurisdiction-api-version.ts @@ -0,0 +1,23 @@ +/* + Copyright 2019-2020 City of Los Angeles. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +import { ApiVersionMiddleware } from '@mds-core/mds-api-server' +import { JURISDICTION_API_SUPPORTED_VERSIONS, JURISDICTION_API_DEFAULT_VERSION } from '../@types' + +export const JurisdictionApiVersionMiddleware = ApiVersionMiddleware( + 'application/vnd.mds.jurisdiction+json', + JURISDICTION_API_SUPPORTED_VERSIONS +).withDefaultVersion(JURISDICTION_API_DEFAULT_VERSION) diff --git a/packages/mds-jurisdiction/package.json b/packages/mds-jurisdiction/package.json new file mode 100644 index 000000000..b53ccc4ea --- /dev/null +++ b/packages/mds-jurisdiction/package.json @@ -0,0 +1,31 @@ +{ + "name": "@mds-core/mds-jurisdiction", + "description": "MDS Jurisdictions API", + "version": "0.1.0", + "author": "City of Los Angeles", + "license": "Apache-2.0", + "main": "dist/index.js", + "types": "dist/index.d.ts", + "files": [ + "dist/" + ], + "scripts": { + "build": "tsc --build tsconfig.build.json", + "start": "PATH_PREFIX=/jurisdiction yarn watch server", + "test": "yarn test:eslint && yarn test:unit", + "test:eslint": "eslint --ignore-path ../../.gitignore '**/*.ts'", + "test:unit": "PATH_PREFIX=/jurisdiction DOTENV_CONFIG_PATH=../../.env nyc ts-mocha --project ../../tsconfig.json", + "ts-node": "yarn build && DOTENV_CONFIG_PATH=../../.env ts-node -r dotenv/config", + "watch": "nodemon --watch '../../packages' --ext 'ts' --ignore '*.d.ts' --exec yarn ts-node --" + }, + "dependencies": { + "@mds-core/mds-api-helpers": "0.1.26", + "@mds-core/mds-api-server": "0.1.26", + "@mds-core/mds-jurisdiction-service": "0.1.0", + "@mds-core/mds-logger": "0.1.24", + "@mds-core/mds-service-helpers": "0.1.0", + "@mds-core/mds-utils": "0.1.26", + "@types/express": "4.17.6", + "express": "4.17.1" + } +} diff --git a/packages/mds-jurisdiction/server.ts b/packages/mds-jurisdiction/server.ts new file mode 100644 index 000000000..c17cad3e3 --- /dev/null +++ b/packages/mds-jurisdiction/server.ts @@ -0,0 +1,20 @@ +/* + Copyright 2019-2020 City of Los Angeles. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +import { ApiServer, HttpServer } from '@mds-core/mds-api-server' +import { api } from './api' + +HttpServer(ApiServer(api), { port: process.env.JURISDICTION_API_PORT }) diff --git a/packages/mds-jurisdiction/tests/api.spec.ts b/packages/mds-jurisdiction/tests/api.spec.ts new file mode 100644 index 000000000..ff436cadb --- /dev/null +++ b/packages/mds-jurisdiction/tests/api.spec.ts @@ -0,0 +1,219 @@ +/* + Copyright 2019-2020 City of Los Angeles. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +import supertest from 'supertest' +import test from 'unit.js' +import { ApiServer } from '@mds-core/mds-api-server' +import { uuid } from '@mds-core/mds-utils' +import { JurisdictionServiceProvider } from '@mds-core/mds-jurisdiction-service/service/provider' +import { SCOPED_AUTH } from '@mds-core/mds-test-data' +import { JurisdictionDomainModel } from '@mds-core/mds-jurisdiction-service' +import { ProcessManager } from '@mds-core/mds-service-helpers' +import { api } from '../api' +import { JURISDICTION_API_DEFAULT_VERSION } from '../@types' + +const request = supertest(ApiServer(api)) + +const [JURISDICTION0, JURISDICTION1, JURISDICTION2] = [uuid(), uuid(), uuid()].map(jurisdiction_id => ({ + jurisdiction_id, + agency_key: `agency-key-${jurisdiction_id}`, + agency_name: `Agency Name ${jurisdiction_id}`, + geography_id: uuid() +})) + +const controller = ProcessManager(JurisdictionServiceProvider).controller() + +describe('Test Jurisdiction API', () => { + before(async () => { + await controller.start() + }) + + it('Create Single Jurisdiction', async () => { + const result = await request + .post('/jurisdictions') + .set('Authorization', SCOPED_AUTH(['jurisdictions:write'])) + .send(JURISDICTION0) + .expect(201) + test.object(result.body).hasProperty('version', JURISDICTION_API_DEFAULT_VERSION) + test.object(result.body.jurisdiction).hasProperty('timestamp') + test.object(result.body.jurisdiction).hasProperty('jurisdiction_id', JURISDICTION0.jurisdiction_id) + }) + + it('Create Single Jurisdiction (forbidden)', async () => { + await request.post('/jurisdictions').send(JURISDICTION0).expect(403) + }) + + it('Create Single Jurisdiction (conflict)', async () => { + await request + .post('/jurisdictions') + .set('Authorization', SCOPED_AUTH(['jurisdictions:write'])) + .send(JURISDICTION0) + .expect(409) + }) + + it('Create Single Jurisdiction (validation error)', async () => { + const { agency_key, ...dto } = JURISDICTION1 + await request + .post('/jurisdictions') + .set('Authorization', SCOPED_AUTH(['jurisdictions:write'])) + .send(dto) + .expect(400) + }) + + it('Create Multiple Jurisdictions', async () => { + const result = await request + .post('/jurisdictions') + .set('Authorization', SCOPED_AUTH(['jurisdictions:write'])) + .send([JURISDICTION1, JURISDICTION2]) + .expect(201) + test.object(result.body).hasProperty('version', JURISDICTION_API_DEFAULT_VERSION) + test.object(result.body.jurisdictions).hasProperty('length', 2) + test + .value(result.body.jurisdictions.map((jurisdiction: JurisdictionDomainModel) => jurisdiction.jurisdiction_id)) + .is([JURISDICTION1.jurisdiction_id, JURISDICTION2.jurisdiction_id]) + }) + + it('Update Single Jurisdiction (conflict error)', async () => { + await request + .put(`/jurisdictions/${JURISDICTION1.jurisdiction_id}`) + .set('Authorization', SCOPED_AUTH(['jurisdictions:write'])) + .send({ ...JURISDICTION1, jurisdiction_id: uuid() }) + .expect(409) + }) + + it('Update Single Jurisdiction (validation error)', async () => { + await request + .put(`/jurisdictions/${JURISDICTION1.jurisdiction_id}`) + .set('Authorization', SCOPED_AUTH(['jurisdictions:write'])) + .send({ ...JURISDICTION1, timestamp: 0 }) + .expect(400) + }) + + it('Update Single Jurisdiction (not found)', async () => { + await request + .put(`/jurisdictions/${uuid()}`) + .set('Authorization', SCOPED_AUTH(['jurisdictions:write'])) + .expect(404) + }) + + it('Update Single Jurisdiction', async () => { + const updated_agency_key = `${JURISDICTION1.agency_key}-updated` + const result = await request + .put(`/jurisdictions/${JURISDICTION1.jurisdiction_id}`) + .set('Authorization', SCOPED_AUTH(['jurisdictions:write'])) + .send({ ...JURISDICTION1, agency_key: updated_agency_key }) + .expect(200) + test.object(result.body).hasProperty('version', JURISDICTION_API_DEFAULT_VERSION) + test.object(result.body.jurisdiction).hasProperty('jurisdiction_id', JURISDICTION1.jurisdiction_id) + test.object(result.body.jurisdiction).hasProperty('agency_key', updated_agency_key) + }) + + it('Get One Jurisdiction', async () => { + const result = await request + .get(`/jurisdictions/${JURISDICTION2.jurisdiction_id}`) + .set('Authorization', SCOPED_AUTH(['jurisdictions:read'])) + .expect(200) + test.object(result.body).hasProperty('version', JURISDICTION_API_DEFAULT_VERSION) + test.object(result.body.jurisdiction).hasProperty('jurisdiction_id', JURISDICTION2.jurisdiction_id) + }) + + it('Get One Jurisdiction (no scope)', async () => { + await request.get(`/jurisdictions/${JURISDICTION2.jurisdiction_id}`).expect(403) + }) + + it('Get One Jurisdiction (incorrect jurisdiction claim)', async () => { + await request + .get(`/jurisdictions/${JURISDICTION2.jurisdiction_id}`) + .set('Authorization', SCOPED_AUTH(['jurisdictions:read:claim'], JURISDICTION1.agency_key)) + .expect(403) + }) + + it('Get One Jurisdiction (proper jurisdiction claim)', async () => { + const result = await request + .get(`/jurisdictions/${JURISDICTION2.jurisdiction_id}`) + .set('Authorization', SCOPED_AUTH(['jurisdictions:read:claim'], JURISDICTION2.agency_key)) + .expect(200) + test.object(result.body).hasProperty('version', JURISDICTION_API_DEFAULT_VERSION) + test.object(result.body.jurisdiction).hasProperty('jurisdiction_id', JURISDICTION2.jurisdiction_id) + }) + + it('Get Multiple Jurisdictions', async () => { + const result = await request + .get('/jurisdictions') + .set('Authorization', SCOPED_AUTH(['jurisdictions:read'])) + .expect(200) + test.object(result.body).hasProperty('version', JURISDICTION_API_DEFAULT_VERSION) + test + .value( + (result.body.jurisdictions as JurisdictionDomainModel[]) + .map(jurisdiction => jurisdiction.jurisdiction_id) + .filter(jurisdiction_id => + [JURISDICTION0, JURISDICTION1, JURISDICTION2] + .map(jurisdiction => jurisdiction.jurisdiction_id) + .includes(jurisdiction_id) + ).length + ) + .is(3) + }) + + it('Get Multiple Jurisdictions (no scope)', async () => { + await request.get('/jurisdictions').expect(403) + }) + + it('Get Multiple Jurisdictions (no jurisdictions claim)', async () => { + const result = await request + .get('/jurisdictions') + .set('Authorization', SCOPED_AUTH(['jurisdictions:read:claim'])) + .expect(200) + test.object(result.body).hasProperty('version', JURISDICTION_API_DEFAULT_VERSION) + test.value((result.body.jurisdictions as JurisdictionDomainModel[]).length).is(0) + }) + + it('Get Multiple Jurisdictions (jurisdictions claim)', async () => { + const result = await request + .get('/jurisdictions') + .set('Authorization', SCOPED_AUTH(['jurisdictions:read:claim'], JURISDICTION2.agency_key)) + .expect(200) + test.object(result.body).hasProperty('version', JURISDICTION_API_DEFAULT_VERSION) + test.value((result.body.jurisdictions as JurisdictionDomainModel[]).length).is(1) + }) + + it('Delete One Jurisdiction', async () => { + const result = await request + .delete(`/jurisdictions/${JURISDICTION1.jurisdiction_id}`) + .set('Authorization', SCOPED_AUTH(['jurisdictions:write'])) + .expect(200) + test.object(result.body).hasProperty('jurisdiction_id', JURISDICTION1.jurisdiction_id) + }) + + it('Delete One Jurisdiction (not found)', async () => { + await request + .delete(`/jurisdictions/${JURISDICTION1.jurisdiction_id}`) + .set('Authorization', SCOPED_AUTH(['jurisdictions:write'])) + .expect(404) + }) + + it('Get One Jurisdiction (not found)', async () => { + await request + .get(`/jurisdictions/${JURISDICTION1.jurisdiction_id}`) + .set('Authorization', SCOPED_AUTH(['jurisdictions:read'])) + .expect(404) + }) + + after(async () => { + await controller.stop() + }) +}) diff --git a/packages/mds-jurisdiction/tsconfig.build.json b/packages/mds-jurisdiction/tsconfig.build.json new file mode 100644 index 000000000..465c0368f --- /dev/null +++ b/packages/mds-jurisdiction/tsconfig.build.json @@ -0,0 +1,15 @@ +{ + "extends": "../../tsconfig.build.json", + "compilerOptions": { + "outDir": "dist" + }, + "references": [ + { "path": "../../packages/mds-api-helpers/tsconfig.build.json" }, + { "path": "../../packages/mds-api-server/tsconfig.build.json" }, + { "path": "../../packages/mds-jurisdiction-service/tsconfig.build.json" }, + { "path": "../../packages/mds-logger/tsconfig.build.json" }, + { "path": "../../packages/mds-service-helpers/tsconfig.build.json" }, + { "path": "../../packages/mds-test-data/tsconfig.build.json" }, + { "path": "../../packages/mds-utils/tsconfig.build.json" } + ] +} diff --git a/packages/mds-jurisdiction/tsconfig.eslint.json b/packages/mds-jurisdiction/tsconfig.eslint.json new file mode 100644 index 000000000..2a400942a --- /dev/null +++ b/packages/mds-jurisdiction/tsconfig.eslint.json @@ -0,0 +1 @@ +{ "extends": "../../tsconfig.eslint.json" } diff --git a/packages/mds-logger/index.ts b/packages/mds-logger/index.ts index 62d9fb384..e11cf642e 100644 --- a/packages/mds-logger/index.ts +++ b/packages/mds-logger/index.ts @@ -1,5 +1,5 @@ /* - Copyright 2019 City of Los Angeles. + Copyright 2019-2020 City of Los Angeles. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -14,220 +14,26 @@ limitations under the License. */ -// Sorry about all the `any` type declarations, but log messages can be -// arbitrarily nested JS objects. +const logger: Pick = console +type LogLevel = keyof typeof logger -/* eslint no-console: "off" */ - -import PushClient from 'pushover-notifications' - -import { WebClient as SlackClient } from '@slack/client' - -const { env } = process - -interface Datum { - lat?: string - lng?: string - // eslint-disable-next-line @typescript-eslint/no-explicit-any - [propName: string]: any -} -// eslint-disable-next-line @typescript-eslint/no-explicit-any -let pushClient: any - -if (env.PUSHOVER_TOKEN) { - pushClient = new PushClient({ - user: env.PUSHOVER_USER, - token: env.PUSHOVER_TOKEN, - debug: true - }) -} - -async function sendPush(msg: string, priority?: number) { - if (!pushClient) { - return - } - const payload = { - // These values correspond to the parameters detailed on https://pushover.net/api - // 'message' is required. All other values are optional. - message: msg, // required - // title: 'Test Pushover', - // sound: 'magic', - // device: 'devicename', - priority: priority || 0 - } - - const [err, result] = await pushClient.send(payload) - if (err) { - console.error('ERROR pushover fail', err) - throw err - } else { - console.log('INFO pushover success', result) - return result - } -} - -function makeCensoredDatum(datum: Datum) { - if (datum instanceof Error) { - return datum.toString() - } - const censoredDatum: Datum = { ...datum } - if (censoredDatum.lat != null) { - censoredDatum.lat = 'CENSORED_LAT' - } - if (censoredDatum.lng != null) { - censoredDatum.lng = 'CENSORED_LNG' - } - return censoredDatum -} - -// eslint-disable-next-line @typescript-eslint/no-explicit-any -function isAtomic(item: any) { - const type = typeof item - return ['string', 'number', 'boolean', 'undefined'].includes(type) || item == null -} - -// just in case we have to censor something nested like -// { data: [{ lat:1, lng:2 }] } -// eslint-disable-next-line @typescript-eslint/no-explicit-any -function makeCensoredLogMsgRecurse(msg: any): any { - if (isAtomic(msg)) { - return msg - } - if (Array.isArray(msg)) { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - return msg.map((arr_item: any) => makeCensoredLogMsgRecurse(arr_item)) - } - const censoredObject = makeCensoredDatum(msg) - if (typeof censoredObject === 'object') { - Object.keys(censoredObject).forEach((key: string) => { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const val: any = censoredObject[key] - if (!isAtomic(val)) { - censoredObject[key] = makeCensoredLogMsgRecurse(val) - } - }) - } - return censoredObject -} - -// eslint-disable-next-line @typescript-eslint/no-explicit-any -function makeCensoredLogMsg(...msgs: any[]): any[] { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - return makeCensoredLogMsgRecurse(msgs).map((item: any) => - // never print out '[object Object]' - String(item) === '[object Object]' ? JSON.stringify(item) : item +const redact = (args: unknown[]): string[] => + args.map(arg => + JSON.stringify(arg instanceof Error ? arg.toString() : arg, (k, v) => + ['lat', 'lng'].includes(k) ? '[REDACTED]' : v + ) ) -} - -// eslint-disable-next-line @typescript-eslint/no-explicit-any -let slackClient: any = null -if (env.SLACK_TOKEN) { - // An access token (from your Slack app or custom integration - xoxa, xoxp, or xoxb) - slackClient = new SlackClient(env.SLACK_TOKEN) -} - -// eslint-disable-next-line @typescript-eslint/no-explicit-any -async function sendSlack(msg: any, channelParam?: string) { - if (!slackClient) { - return - } - - // See: https://api.slack.com/methods/chat.postMessage - const channel = channelParam || env.SLACK_CHANNEL || '#sandbox' - console.log('INFO sendSlack', channel, msg) - try { - const res = await slackClient.chat.postMessage({ - // This argument can be a channel ID, a DM ID, a MPDM ID, or a group ID - token: env.SLACK_TOKEN, - channel, - text: msg - }) - // `res` contains information about the posted message - // eslint-disable-next-line no-console - console.log('INFO slack message sent: ', res.ts) - return res.ts - } catch (err) { - console.error('ERROR slack message fail: ', err) - throw err +const log = (level: LogLevel, ...args: unknown[]): string[] => { + const redacted = process.env.QUIET === 'true' ? [] : redact(args) + if (redacted.length) { + logger[level](level.toUpperCase(), ...redacted) } + return redacted } -const { argv } = process - -if (argv.length > 3) { - const verb = argv[2] - if (verb === 'slack') { - if (env.SLACK_TOKEN) { - /* eslint-reason can't use async/await in non-function */ - /* eslint-disable-next-line @typescript-eslint/no-floating-promises */ - sendSlack(argv[3]) - } else { - console.error('no SLACK_TOKEN defined') - } - } else if (verb === 'push') { - if (env.PUSHOVER_TOKEN) { - /* eslint-reason can't use async/await in non-function */ - /* eslint-disable-next-line @typescript-eslint/no-floating-promises */ - sendPush(argv[3]) - } else { - console.error('no PUSHOVER_TOKEN defined') - } - } -} - -// eslint-disable-next-line @typescript-eslint/no-explicit-any -function info(...msg: any[]): any[] { - if (env.QUIET) { - return [] - } - - const censoredMsg = makeCensoredLogMsg(...msg) - console.log.apply(console, ['INFO', ...censoredMsg]) - return censoredMsg -} - -// eslint-disable-next-line @typescript-eslint/no-explicit-any -async function warn(...msg: any[]): Promise { - if (env.QUIET) { - return [] - } - - const censoredMsg = makeCensoredLogMsg(...msg) - console.log.apply(console, ['WARN', ...censoredMsg]) - /* eslint-disable-next-line @typescript-eslint/no-floating-promises */ - await sendSlack(censoredMsg.join(' ')) - - /* eslint-disable-next-line @typescript-eslint/no-floating-promises */ - await sendPush(censoredMsg.join(' '), 0) - return censoredMsg -} - -// eslint-disable-next-line @typescript-eslint/no-explicit-any -async function error(...msg: any[]): Promise { - if (env.QUIET) { - return [] - } - const censoredMsg = makeCensoredLogMsg(...msg) - // eslint-disable-next-line no-console - console.error.apply(console, ['ERROR', ...censoredMsg]) - - await sendSlack(censoredMsg.join(' ')) - await sendPush(censoredMsg.join(' '), 1) - return censoredMsg -} - -async function startup() { - // try { - // if (env.PUSHOVER_TOKEN) { - // console.log('INFO sending startup pushover: ' + msg) - // } - // if (env.SLACK_TOKEN) { - // console.log('INFO sending startup slack: ' + msg) - // } - // } catch (err) { - // console.error('ERROR failed to send startup message(s)', err.stack) - // } -} +const info = (...args: unknown[]) => log('info', ...args) +const warn = (...args: unknown[]) => log('warn', ...args) +const error = (...args: unknown[]) => log('error', ...args) -export = { info, warn, error, startup } +export default { log, info, warn, error } diff --git a/packages/mds-logger/package.json b/packages/mds-logger/package.json index a3992d755..1a92739d3 100644 --- a/packages/mds-logger/package.json +++ b/packages/mds-logger/package.json @@ -1,6 +1,6 @@ { "name": "@mds-core/mds-logger", - "version": "0.1.16", + "version": "0.1.24", "description": "Mobility Data Specification logging interface", "main": "dist/index.js", "types": "dist/index.d.ts", @@ -13,14 +13,11 @@ ], "author": "City of Los Angeles", "license": "Apache-2.0", - "dependencies": { - "@slack/client": "5.0.2", - "pushover-notifications": "1.2.0" - }, + "dependencies": {}, "scripts": { "build": "tsc --build tsconfig.build.json", "test": "yarn test:eslint && yarn test:unit", - "test:eslint": "eslint --fix --ignore-path ../../.gitignore '**/*.ts'", - "test:unit": "DOTENV_CONFIG_PATH=../../.env nyc --check-coverage --exclude tests --extension .ts --lines 65 --reporter=text --reporter=html ts-mocha --project ../../tsconfig.json --require tsconfig-paths/register --require source-map-support/register --require dotenv/config --recursive --timeout 5000 tests/**/*.ts" + "test:eslint": "eslint --ignore-path ../../.gitignore '**/*.ts'", + "test:unit": "DOTENV_CONFIG_PATH=../../.env nyc ts-mocha --project ../../tsconfig.json" } } diff --git a/packages/mds-logger/tests/index.spec.ts b/packages/mds-logger/tests/index.spec.ts new file mode 100644 index 000000000..eebb8e375 --- /dev/null +++ b/packages/mds-logger/tests/index.spec.ts @@ -0,0 +1,132 @@ +import * as test from 'unit.js' +import logger from '../index' + +describe('MDS Logger', () => { + it('censors logs of lat and lng info for mds-logger.info', done => { + const toCensor = { + device_id: 'ec551174-f324-4251-bfed-28d9f3f473fc', + gps: { + lat: 1231.21, + lng: 1231.21, + speed: 0, + hdop: 1, + heading: 180 + }, + charge: 0.5, + timestamp: 1555384091559, + recorded: 1555384091836 + } + const [result] = logger.info(toCensor).map(arg => JSON.parse(arg)) + test.string(result.gps.lat).is('[REDACTED]') + test.string(result.gps.lng).is('[REDACTED]') + done() + }) + + it('censors logs of lat and lng info for mds-logger.warn', done => { + const toCensor = { + device_id: 'ec551174-f324-4251-bfed-28d9f3f473fc', + gps: { + lat: 1231.21, + lng: 1231.21, + speed: 0, + hdop: 1, + heading: 180 + }, + charge: 0.5, + timestamp: 1555384091559, + recorded: 1555384091836 + } + const [result] = logger.warn(toCensor).map(arg => JSON.parse(arg)) + test.string(result.gps.lat).is('[REDACTED]') + test.string(result.gps.lng).is('[REDACTED]') + done() + }) + + it('censors logs of lat and lng info for mds-logger.error', done => { + const toCensor = [ + { + device_id: 'ec551174-f324-4251-bfed-28d9f3f473fc', + gps: { + lat: 1231.21, + lng: 1231.21, + speed: 0, + hdop: 1, + heading: 180 + }, + charge: 0.5, + timestamp: 1555384091559, + recorded: 1555384091836 + }, + { + device_id: 'ec551174-f324-4251-bfed-28d9f3f473fc', + gps: { + lat: 34.21, + lng: 341231.21, + speed: 100, + hdop: 10, + heading: 20 + }, + charge: 0.75, + timestamp: 1555384090000, + recorded: 1555384090000 + } + ] + const [[result1, result2]] = logger.error(toCensor).map(arg => JSON.parse(arg)) + test.string(result1.gps.lat).is('[REDACTED]') + test.string(result1.gps.lng).is('[REDACTED]') + test.string(result2.gps.lat).is('[REDACTED]') + test.string(result2.gps.lng).is('[REDACTED]') + done() + }) + + it('verifies conversion of [object Object] to stringified version', done => { + const [result] = logger.info({ key1: 'key1', key2: 'key2' }).map(arg => JSON.parse(arg)) + test.string(result.key1).contains('key1') + done() + }) + + it('verifies conversion of an error', done => { + const err = new Error('puzzling evidence') + const [ohai2, result] = logger.info('ohai2', err).map(arg => JSON.parse(arg)) + test.string(ohai2).is('ohai2') + test.string(result).contains('evidence') + done() + }) + + it('verifies parameterized log INFO works', done => { + const results = logger.log('info', { key1: 'key1', key2: 'key2' }).map(arg => JSON.parse(arg)) + test.object(results).isArray() + test.array(results).hasLength(1) + done() + }) + + it('verifies parameterized log WARN works', done => { + const results = logger.log('warn', { key1: 'key1', key2: 'key2' }).map(arg => JSON.parse(arg)) + test.object(results).isArray() + test.array(results).hasLength(1) + done() + }) + + it('verifies parameterized log ERROR works', done => { + const results = logger.log('error', { key1: 'key1', key2: 'key2' }).map(arg => JSON.parse(arg)) + test.object(results).isArray() + test.array(results).hasLength(1) + done() + }) + + it('verifies parameterized log ERROR with multiple parameters works', done => { + const results = logger.log('error', { key1: 'key1', key2: 'key2' }, { b: 2 }).map(arg => JSON.parse(arg)) + test.object(results).isArray() + test.array(results).hasLength(2) + done() + }) + + it('verifies QUIET mode', () => { + process.env.QUIET = 'false' + const result1 = logger.log('error', { key1: 'key1', key2: 'key2' }, { b: 2 }) + test.value(result1.length).is(2) + process.env.QUIET = 'true' + const result2 = logger.log('error', { key1: 'key1', key2: 'key2' }, { b: 2 }) + test.value(result2.length).is(0) + }) +}) diff --git a/packages/mds-logger/tests/test.ts b/packages/mds-logger/tests/test.ts deleted file mode 100644 index bfc077ee7..000000000 --- a/packages/mds-logger/tests/test.ts +++ /dev/null @@ -1,114 +0,0 @@ -/* eslint-disable promise/prefer-await-to-callbacks */ -/* eslint-disable promise/prefer-await-to-then */ -/* eslint-disable promise/always-return */ -/* eslint-disable promise/no-callback-in-promise */ -/* eslint-disable @typescript-eslint/no-explicit-any */ -import * as test from 'unit.js' -import logger from '../index' - -describe('MDS Logger', () => { - it('censors logs of lat and lng info for mds-logger.info', done => { - const toCensor = { - device_id: 'ec551174-f324-4251-bfed-28d9f3f473fc', - gps: { - lat: 1231.21, - lng: 1231.21, - speed: 0, - hdop: 1, - heading: 180 - }, - charge: 0.5, - timestamp: 1555384091559, - recorded: 1555384091836 - } - const [result] = logger.info(toCensor) - const res = JSON.parse(result) - test.string(res.gps.lat).contains('CENSORED') - test.string(res.gps.lng).contains('CENSORED') - done() - }) - - it('censors logs of lat and lng info for mds-logger.warn', done => { - const toCensor = { - device_id: 'ec551174-f324-4251-bfed-28d9f3f473fc', - gps: { - lat: 1231.21, - lng: 1231.21, - speed: 0, - hdop: 1, - heading: 180 - }, - charge: 0.5, - timestamp: 1555384091559, - recorded: 1555384091836 - } - logger - .warn(toCensor) - .then((val: any[]) => { - const [result] = val - const res = JSON.parse(result) - test.string(res.gps.lat).contains('CENSORED') - test.string(res.gps.lng).contains('CENSORED') - done() - }) - .catch((err: Error) => { - done(err) - }) - }) - - it('censors logs of lat and lng info for mds-logger.error', done => { - const toCensor = [ - { - device_id: 'ec551174-f324-4251-bfed-28d9f3f473fc', - gps: { - lat: 1231.21, - lng: 1231.21, - speed: 0, - hdop: 1, - heading: 180 - }, - charge: 0.5, - timestamp: 1555384091559, - recorded: 1555384091836 - }, - { - device_id: 'ec551174-f324-4251-bfed-28d9f3f473fc', - gps: { - lat: 34.21, - lng: 341231.21, - speed: 100, - hdop: 10, - heading: 20 - }, - charge: 0.75, - timestamp: 1555384090000, - recorded: 1555384090000 - } - ] - logger - .error(toCensor) - .then((vals: any[]) => { - const [[result1, result2]] = vals - test.string(result1.gps.lat).contains('CENSORED') - test.string(result1.gps.lng).contains('CENSORED') - test.string(result2.gps.lat).contains('CENSORED') - test.string(result2.gps.lng).contains('CENSORED') - done() - }) - .catch(done) - }) - - it('verifies conversion of [object Object] to stringified version', done => { - const [result] = logger.info({ key1: 'key1', key2: 'key2' }) - const res = JSON.parse(result) - test.string(res.key1).contains('key1') - done() - }) - - it('verifies conversion of an error', done => { - const err = new Error('puzzling evidence') - const [, result] = logger.info('ohai2', err) - test.string(result).contains('evidence') - done() - }) -}) diff --git a/packages/mds-logger/tsconfig.eslint.json b/packages/mds-logger/tsconfig.eslint.json new file mode 100644 index 000000000..2a400942a --- /dev/null +++ b/packages/mds-logger/tsconfig.eslint.json @@ -0,0 +1 @@ +{ "extends": "../../tsconfig.eslint.json" } diff --git a/packages/mds-metrics-sheet/index.ts b/packages/mds-metrics-sheet/index.ts index 3750f58c9..f0bf22aaa 100644 --- a/packages/mds-metrics-sheet/index.ts +++ b/packages/mds-metrics-sheet/index.ts @@ -1,4 +1,3 @@ import { MetricsLogHandler } from './metrics-log' -import { VehicleCountsHandler } from './vehicle-counts' -export { MetricsLogHandler, VehicleCountsHandler } +export { MetricsLogHandler } diff --git a/packages/mds-metrics-sheet/metrics-log.ts b/packages/mds-metrics-sheet/metrics-log.ts index c6e578ffa..ad279357a 100644 --- a/packages/mds-metrics-sheet/metrics-log.ts +++ b/packages/mds-metrics-sheet/metrics-log.ts @@ -16,7 +16,7 @@ import GoogleSpreadsheet from 'google-spreadsheet' import { promisify } from 'util' -import log from '@mds-core/mds-logger' +import logger from '@mds-core/mds-logger' import { JUMP_PROVIDER_ID, LIME_PROVIDER_ID, @@ -28,7 +28,7 @@ import { BOLT_PROVIDER_ID } from '@mds-core/mds-providers' import { VEHICLE_EVENT, EVENT_STATUS_MAP, VEHICLE_STATUS } from '@mds-core/mds-types' -import { requestPromiseExceptionHelper } from './utils' +import { requestPromiseExceptionHelper, MAX_TIMEOUT_MS } from './utils' import { VehicleCountResponse, LastDayStatsResponse, MetricsSheetRow, VehicleCountRow } from './types' // The list of providers ids on which to report @@ -144,15 +144,15 @@ async function appendSheet(sheetName: string, rows: MetricsSheetRow[]) { const doc = new GoogleSpreadsheet(process.env.SPREADSHEET_ID) await promisify(doc.useServiceAccountAuth)(creds) const info = await promisify(doc.getInfo)() - log.info(`Loaded doc: ${info.title} by ${info.author.email}`) + logger.info(`Loaded doc: ${info.title} by ${info.author.email}`) const sheet = info.worksheets.filter((s: { title: string; rowCount: number } & unknown) => s.title === sheetName)[0] - log.info(`${sheetName} sheet: ${sheet.title} ${sheet.rowCount}x${sheet.colCount}`) + logger.info(`${sheetName} sheet: ${sheet.title} ${sheet.rowCount}x${sheet.colCount}`) if (sheet.title === sheetName) { const inserted = rows.map(insert_row => promisify(sheet.addRow)(insert_row)) - log.info(`Wrote ${inserted.length} rows.`) + logger.info(`Wrote ${inserted.length} rows.`) return Promise.all(inserted) } - log.info('Wrong sheet!') + logger.info('Wrong sheet!') } export async function getProviderMetrics(iter: number): Promise { @@ -170,19 +170,22 @@ export async function getProviderMetrics(iter: number): Promise mapProviderToPayload(provider, last)) return rows } catch (err) { - await log.error(`getProviderMetrics() API call error on ${err.url}`, err) + logger.error(`getProviderMetrics() API call error on ${err.url}`, err) return getProviderMetrics(iter + 1) } } @@ -203,6 +206,6 @@ export const MetricsLogHandler = async () => { const rows = await getProviderMetrics(0) await appendSheet('Metrics Log', rows) } catch (err) { - await log.error('MetricsLogHandler', err) + logger.error('MetricsLogHandler', err) } } diff --git a/packages/mds-metrics-sheet/package.json b/packages/mds-metrics-sheet/package.json index b3a763642..c9db22d86 100644 --- a/packages/mds-metrics-sheet/package.json +++ b/packages/mds-metrics-sheet/package.json @@ -1,6 +1,6 @@ { "name": "@mds-core/mds-metrics-sheet", - "version": "0.0.19", + "version": "0.0.27", "description": "Automate reporting api insert into LADOT compliance spreadsheet", "main": "dist/index.js", "types": "dist/index.d.ts", @@ -10,17 +10,19 @@ "scripts": { "build": "tsc --build tsconfig.build.json", "test": "yarn test:eslint && yarn test:unit", - "test:eslint": "eslint --fix --ignore-path ../../.gitignore '**/*.ts'", - "test:unit": "DOTENV_CONFIG_PATH=../../.env nyc --check-coverage --exclude tests --extension .ts --lines 50 --reporter=text --reporter=html ts-mocha --project ../../tsconfig.json --require tsconfig-paths/register --require source-map-support/register --require dotenv/config --recursive --timeout 5000 tests/**/*.ts" + "test:eslint": "eslint --ignore-path ../../.gitignore '**/*.ts'", + "test:unit": "DOTENV_CONFIG_PATH=../../.env nyc --lines 60 ts-mocha --project ../../tsconfig.json" }, "author": "City of Los Angeles", "license": "Apache-2.0", "dependencies": { - "@mds-core/mds-logger": "0.1.16", - "@mds-core/mds-providers": "0.1.18", - "@mds-core/mds-types": "0.1.15", - "google-spreadsheet": "2.0.8", - "request": "2.88.0", - "request-promise": "4.2.4" + "@mds-core/mds-logger": "0.1.24", + "@mds-core/mds-providers": "0.1.26", + "@mds-core/mds-types": "0.1.23", + "@mds-core/mds-utils": "0.1.26", + "@types/request-promise": "4.1.46", + "google-spreadsheet": "3.0.11", + "request": "2.88.2", + "request-promise": "4.2.5" } } diff --git a/packages/mds-metrics-sheet/tests/mds-metrics-sheet.test.ts b/packages/mds-metrics-sheet/tests/mds-metrics-sheet.spec.ts similarity index 78% rename from packages/mds-metrics-sheet/tests/mds-metrics-sheet.test.ts rename to packages/mds-metrics-sheet/tests/mds-metrics-sheet.spec.ts index 25b39204f..c6a80daf4 100644 --- a/packages/mds-metrics-sheet/tests/mds-metrics-sheet.test.ts +++ b/packages/mds-metrics-sheet/tests/mds-metrics-sheet.spec.ts @@ -1,9 +1,8 @@ import assert from 'assert' -import uuid from 'uuid' +import { uuid } from '@mds-core/mds-utils' import Sinon from 'sinon' import { mapProviderToPayload, eventCountsToStatusCounts, sum, percent, getProviderMetrics } from '../metrics-log' import { VehicleCountRow, LastDayStatsResponse } from '../types' -import { mapRow, sumColumns } from '../vehicle-counts' import * as utils from '../utils' const getStatus = (): VehicleCountRow['status'] => { @@ -91,19 +90,21 @@ const getProvider = (): VehicleCountRow => { count: 42, status: getStatus(), event_type: getEvent(), - areas: {}, - areas_48h: {} + areas: {} } } // https://stackoverflow.com/a/46957474 // TODO holdover from old node, when we upgrade replace with assert.rejects() async function assertThrowsAsync(fn: Function, regExp: RegExp) { - let f = () => {} + let f = () => { + // Function that does not throw + } try { await fn() } catch (e) { f = () => { + // Function that throws throw e } } finally { @@ -286,58 +287,6 @@ describe('MDS Metrics Sheet', () => { }) }) - it('Maps empty row correctly', () => { - const areas_48h = {} - const row = { areas_48h, provider: 'fake-provider' } as VehicleCountRow - const actual = mapRow(row) - const expected = { date: actual.date, name: 'fake-provider', 'Venice Area': 0 } - assert.deepStrictEqual(actual, expected) - }) - - it('Maps filled in row correctly', () => { - const areas_48h: VehicleCountRow['areas_48h'] = {} - const veniceAreaKeys = ['Venice', 'Venice Beach', 'Venice Canals', 'Venice Beach Special Operations Zone'] - for (const veniceAreaKey of veniceAreaKeys) { - areas_48h[veniceAreaKey] = 5 - } - const row = { areas_48h, provider: 'fake-provider' } as VehicleCountRow - const actual = mapRow(row) - const expected = { - date: actual.date, - name: 'fake-provider', - Venice: 5, - 'Venice Area': 20, - 'Venice Beach': 5, - 'Venice Beach Special Operations Zone': 5, - 'Venice Canals': 5 - } - assert.deepStrictEqual(actual, expected) - }) - - it('Summarizes over Venice correctly', () => { - const areas_48h: VehicleCountRow['areas_48h'] = {} - const veniceAreaKeys = ['Venice', 'Venice Beach', 'Venice Canals', 'Venice Beach Special Operations Zone'] - for (const veniceAreaKey of veniceAreaKeys) { - areas_48h[veniceAreaKey] = 5 - } - const row = { areas_48h, provider: 'fake-provider' } as VehicleCountRow - const actual = sumColumns(veniceAreaKeys, row) - assert.strictEqual(actual, 20) - }) - - it('Summarizes over Venice correctly with undefined column entries', () => { - const areas_48h: VehicleCountRow['areas_48h'] = {} - const veniceAreaKeys = ['Venice', 'Venice Beach', 'Venice Canals', 'Venice Beach Special Operations Zone'] - for (const veniceAreaKey of veniceAreaKeys) { - if (veniceAreaKey !== 'Venice') { - areas_48h[veniceAreaKey] = 5 - } - } - const row = { areas_48h, provider: 'fake-provider' } as VehicleCountRow - const actual = sumColumns(veniceAreaKeys, row) - assert.strictEqual(actual, 15) - }) - describe('getProviderMetrics()', () => { it('Retries 10 times', async () => { const fakeRejects = Sinon.fake.rejects('it-broke') diff --git a/packages/mds-metrics-sheet/tsconfig.build.json b/packages/mds-metrics-sheet/tsconfig.build.json index 69c8efc92..85898afcf 100644 --- a/packages/mds-metrics-sheet/tsconfig.build.json +++ b/packages/mds-metrics-sheet/tsconfig.build.json @@ -6,6 +6,7 @@ "references": [ { "path": "../../packages/mds-logger/tsconfig.build.json" }, { "path": "../../packages/mds-providers/tsconfig.build.json" }, - { "path": "../../packages/mds-types/tsconfig.build.json" } + { "path": "../../packages/mds-types/tsconfig.build.json" }, + { "path": "../../packages/mds-utils/tsconfig.build.json" } ] } diff --git a/packages/mds-metrics-sheet/tsconfig.eslint.json b/packages/mds-metrics-sheet/tsconfig.eslint.json new file mode 100644 index 000000000..2a400942a --- /dev/null +++ b/packages/mds-metrics-sheet/tsconfig.eslint.json @@ -0,0 +1 @@ +{ "extends": "../../tsconfig.eslint.json" } diff --git a/packages/mds-metrics-sheet/types.ts b/packages/mds-metrics-sheet/types.ts index fc113d51b..d95402fae 100644 --- a/packages/mds-metrics-sheet/types.ts +++ b/packages/mds-metrics-sheet/types.ts @@ -7,7 +7,6 @@ export interface VehicleCountRow { status: { [s in VEHICLE_STATUS]: number } event_type: { [s in VEHICLE_EVENT]: number } areas: { [s: string]: number } - areas_48h: { [s: string]: number } } export type VehicleCountResponse = VehicleCountRow[] diff --git a/packages/mds-metrics-sheet/utils.ts b/packages/mds-metrics-sheet/utils.ts index 55b275497..a54c3c372 100644 --- a/packages/mds-metrics-sheet/utils.ts +++ b/packages/mds-metrics-sheet/utils.ts @@ -1,6 +1,9 @@ import { UrlOptions } from 'request' import requestPromise, { RequestPromiseOptions } from 'request-promise' +// 120 seconds in ms +export const MAX_TIMEOUT_MS = Number(process.env.MAX_TIMEOUT_MS ?? 120000) + // Utility to add additional fields to error object export const requestPromiseExceptionHelper = async (payload: UrlOptions & RequestPromiseOptions) => { try { diff --git a/packages/mds-metrics-sheet/vehicle-counts.ts b/packages/mds-metrics-sheet/vehicle-counts.ts deleted file mode 100644 index e68bd3d6c..000000000 --- a/packages/mds-metrics-sheet/vehicle-counts.ts +++ /dev/null @@ -1,135 +0,0 @@ -/* - Copyright 2019 City of Los Angeles. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - */ - -import GoogleSpreadsheet from 'google-spreadsheet' - -import { promisify } from 'util' - -import requestPromise from 'request-promise' - -import log from '@mds-core/mds-logger' -import { - JUMP_PROVIDER_ID, - LIME_PROVIDER_ID, - BIRD_PROVIDER_ID, - LYFT_PROVIDER_ID, - WHEELS_PROVIDER_ID, - SPIN_PROVIDER_ID, - SHERPA_LA_PROVIDER_ID, - BOLT_PROVIDER_ID -} from '@mds-core/mds-providers' - -import { VehicleCountResponse, VehicleCountRow } from './types' - -// The list of providers ids on which to report -const reportProviders = [ - JUMP_PROVIDER_ID, - LIME_PROVIDER_ID, - BIRD_PROVIDER_ID, - LYFT_PROVIDER_ID, - WHEELS_PROVIDER_ID, - SPIN_PROVIDER_ID, - SHERPA_LA_PROVIDER_ID, - BOLT_PROVIDER_ID -] - -const creds = { - client_email: process.env.GOOGLE_CLIENT_EMAIL, - private_key: process.env.GOOGLE_PRIVATE_KEY ? process.env.GOOGLE_PRIVATE_KEY.split('\\n').join('\n') : null -} - -async function appendSheet(sheetName: string, rows: ({ date: string; name: string } & unknown)[]) { - const doc = new GoogleSpreadsheet(process.env.SPREADSHEET_ID) - await promisify(doc.useServiceAccountAuth)(creds) - const info = await promisify(doc.getInfo)() - log.info(`Loaded doc: ${info.title} by ${info.author.email}`) - const sheet = info.worksheets.filter((s: { title: string; rowCount: number } & unknown) => s.title === sheetName)[0] - log.info(`${sheetName} sheet: ${sheet.title} ${sheet.rowCount}x${sheet.colCount}`) - if (sheet.title === sheetName) { - const inserted = rows.map(insert_row => promisify(sheet.addRow)(insert_row)) - log.info(`Wrote ${inserted.length} rows.`) - return Promise.all(inserted) - } - log.info('Wrong sheet!') -} - -export function sumColumns(keysToSummarize: string[], row: VehicleCountRow) { - return keysToSummarize.reduce((acc, veniceAreaKey) => acc + row.areas_48h[veniceAreaKey] || 0, 0) -} - -export function mapRow(row: VehicleCountRow) { - const dateOptions = { timeZone: 'America/Los_Angeles', day: '2-digit', month: '2-digit', year: 'numeric' } - const timeOptions = { timeZone: 'America/Los_Angeles', hour12: false, hour: '2-digit', minute: '2-digit' } - const d = new Date() - const veniceAreaKeys = ['Venice', 'Venice Beach', 'Venice Canals', 'Venice Beach Special Operations Zone'] - const veniceAreaSum = sumColumns(veniceAreaKeys, row) - const augmentedRow = { - 'Venice Area': veniceAreaSum, - ...row.areas_48h - } - return { - date: `${d.toLocaleDateString('en-US', dateOptions)} ${d.toLocaleTimeString('en-US', timeOptions)}`, - name: row.provider, - ...augmentedRow - } -} - -async function getProviderMetrics(iter: number): Promise<({ date: string; name: string } & unknown)[]> { - /* after 10 failed iterations, give up */ - if (iter >= 10) { - throw new Error(`Failed to write to sheet after 10 tries!`) - } - - const token_options = { - method: 'POST', - url: `${process.env.AUTH0_DOMAIN}/oauth/token`, - headers: { 'content-type': 'application/json' }, - body: { - grant_type: 'client_credentials', - client_id: process.env.CLIENT_ID, - client_secret: process.env.CLIENT_SECRET, - audience: process.env.AUDIENCE - }, - json: true - } - - try { - const token = await requestPromise(token_options) - const counts_options = { - uri: 'https://api.ladot.io/daily/admin/vehicle_counts', - headers: { authorization: `Bearer ${token.access_token}` }, - json: true - } - - const counts: VehicleCountResponse = await requestPromise(counts_options) - const rows: ({ date: string; name: string } & unknown)[] = counts - .filter(p => reportProviders.includes(p.provider_id)) - .map(mapRow) - return rows - } catch (err) { - await log.error('getProviderMetrics', err) - return getProviderMetrics(iter + 1) - } -} - -export const VehicleCountsHandler = async () => { - try { - const rows = await getProviderMetrics(0) - await appendSheet('Vehicle Counts', rows) - } catch (err) { - await log.error('VehicleCountsHandler', err) - } -} diff --git a/packages/mds-native/api.ts b/packages/mds-native/api.ts deleted file mode 100644 index 9d2726bf7..000000000 --- a/packages/mds-native/api.ts +++ /dev/null @@ -1,170 +0,0 @@ -/* - Copyright 2019 City of Los Angeles. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - */ - -import express from 'express' -import { - pathsFor, - ValidationError, - ServerError, - AuthorizationError, - isValidDeviceId, - NotFoundError, - isValidProviderId, - isValidTimestamp, - isValidNumber -} from '@mds-core/mds-utils' -import logger from '@mds-core/mds-logger' -import db from '@mds-core/mds-db' -import { UUID, Timestamp } from '@mds-core/mds-types' -import { providers } from '@mds-core/mds-providers' -import { ApiResponse, ApiRequest, checkAccess } from '@mds-core/mds-api-server' - -import { - NativeApiGetEventsRequest, - NativeApiGetEventsReponse, - NativeApiGetVehiclesRequest, - NativeApiGetVehiclesResponse, - NativeApiGetProvidersRequest, - NativeApiGetProvidersResponse, - NativeApiCurrentVersion -} from './types' - -/* istanbul ignore next */ -const InternalServerError = async (req: ApiRequest, res: ApiResponse, err?: string | Error) => { - // 500 Internal Server Error - await logger.error(req.method, req.originalUrl, err) - return res.status(500).send({ error: new ServerError(err) }) -} - -function api(app: express.Express): express.Express { - // ///////////////////// begin middleware /////////////////////// - app.use(async (req: ApiRequest, res: ApiResponse, next: express.NextFunction) => { - if (!(req.path.includes('/health') || req.path === '/')) { - if (!res.locals.claims) { - return res.status(401).send({ error: new AuthorizationError('missing_claims') }) - } - } - return next() - }) - // ///////////////////// begin middleware /////////////////////// - - type NativeApiGetEventsCursor = Partial<{ - provider_id: UUID - device_id: UUID - start_time: Timestamp - end_time: Timestamp - last_id: number - }> - - const numericQueryStringParam = (param: string | undefined): number | undefined => (param ? Number(param) : undefined) - - const getRequestParameters = ( - req: NativeApiGetEventsRequest - ): { cursor: NativeApiGetEventsCursor; limit: number } => { - const { - params: { cursor }, - query: { limit: query_limit, ...filters } - } = req - const limit = numericQueryStringParam(query_limit) || 1000 - isValidNumber(limit, { required: false, min: 1, max: 1000, property: 'limit' }) - if (cursor) { - if (Object.keys(filters).length > 0) { - throw new ValidationError('unexpected_filters', { cursor, filters }) - } - try { - return { cursor: JSON.parse(Buffer.from(cursor, 'base64').toString('ascii')), limit } - } catch (err) { - throw new ValidationError('invalid_cursor', { cursor }) - } - } else { - const { provider_id, device_id, start_time: query_start_time, end_time: query_end_time } = filters - const start_time = numericQueryStringParam(query_start_time) - const end_time = numericQueryStringParam(query_end_time) - isValidProviderId(provider_id, { required: false }) - isValidDeviceId(device_id, { required: false }) - isValidTimestamp(start_time, { required: false }) - isValidTimestamp(end_time, { required: false }) - return { cursor: { provider_id, device_id, start_time, end_time }, limit } - } - } - - app.get( - pathsFor('/events/:cursor?'), - checkAccess(scopes => scopes.includes('events:read')), // TODO: events:read:provider with filtering - async (req: NativeApiGetEventsRequest, res: NativeApiGetEventsReponse) => { - try { - const { cursor, limit } = getRequestParameters(req) - const events = await db.readEventsWithTelemetry({ ...cursor, limit }) - return res.status(200).send({ - version: NativeApiCurrentVersion, - cursor: Buffer.from( - JSON.stringify({ - ...cursor, - last_id: events.length === 0 ? cursor.last_id : events[events.length - 1].id - }) - ).toString('base64'), - events: events.map(({ id, service_area_id, ...event }) => event) - }) - } catch (err) { - if (err instanceof ValidationError) { - await logger.warn(req.method, req.originalUrl, err) - return res.status(400).send({ error: err }) - } - /* istanbul ignore next */ - return InternalServerError(req, res, err) - } - } - ) - - app.get( - pathsFor('/vehicles/:device_id'), - checkAccess(scopes => scopes.includes('vehicles:read')), // TODO: vehicles:read:provider with filtering - async (req: NativeApiGetVehiclesRequest, res: NativeApiGetVehiclesResponse) => { - const { device_id } = req.params - try { - if (isValidDeviceId(device_id)) { - const { id, ...vehicle } = await db.readDevice(device_id) - return res.status(200).send({ version: NativeApiCurrentVersion, vehicle }) - } - } catch (err) { - if (err instanceof ValidationError) { - // 400 Bad Request - return res.status(400).send({ error: err }) - } - if (err instanceof Error && err.message.includes('not found')) { - // 404 Not Found - return res.status(404).send({ error: new NotFoundError('device_id_not_found', { device_id }) }) - } - /* istanbul ignore next */ - return InternalServerError(req, res, err) - } - } - ) - - app.get( - pathsFor('/providers'), - checkAccess(scopes => scopes.includes('providers:read')), - async (req: NativeApiGetProvidersRequest, res: NativeApiGetProvidersResponse) => - res.status(200).send({ - version: NativeApiCurrentVersion, - providers: Object.values(providers) - }) - ) - - return app -} - -export { api } diff --git a/packages/mds-native/package.json b/packages/mds-native/package.json deleted file mode 100644 index 2cd6c8d10..000000000 --- a/packages/mds-native/package.json +++ /dev/null @@ -1,34 +0,0 @@ -{ - "name": "@mds-core/mds-native", - "description": "MDS Native Data Feed", - "version": "0.0.16", - "author": "City of Los Angeles", - "license": "Apache-2.0", - "main": "dist/index.js", - "types": "dist/index.d.ts", - "files": [ - "dist/" - ], - "scripts": { - "build": "tsc --build tsconfig.build.json", - "start": "PATH_PREFIX=/native yarn watch server", - "test": "yarn test:eslint && yarn test:unit", - "test:eslint": "eslint --fix --ignore-path ../../.gitignore '**/*.ts'", - "test:unit": "PATH_PREFIX=/native DOTENV_CONFIG_PATH=../../.env nyc --check-coverage --exclude tests --extension .ts --lines 85 --reporter=text --reporter=html ts-mocha --project ../../tsconfig.json --require tsconfig-paths/register --require source-map-support/register --require dotenv/config --recursive --timeout 5000 tests/**/*.ts", - "watch": "nodemon --watch '../../packages' --ext 'ts' --ignore '*.d.ts' --exec yarn watch:exec --", - "watch:exec": "yarn build && DOTENV_CONFIG_PATH=../../.env ts-node -r dotenv/config" - }, - "dependencies": { - "@mds-core/mds-api-helpers": "0.1.18", - "@mds-core/mds-api-server": "0.1.18", - "@mds-core/mds-db": "0.1.18", - "@mds-core/mds-logger": "0.1.16", - "@mds-core/mds-providers": "0.1.18", - "@mds-core/mds-types": "0.1.15", - "@mds-core/mds-utils": "0.1.18", - "express": "4.17.1" - }, - "devDependencies": { - "@mds-core/mds-test-data": "0.1.18" - } -} diff --git a/packages/mds-native/tests/api.spec.ts b/packages/mds-native/tests/api.spec.ts deleted file mode 100644 index 0e49253da..000000000 --- a/packages/mds-native/tests/api.spec.ts +++ /dev/null @@ -1,242 +0,0 @@ -/* eslint-disable promise/no-callback-in-promise */ -/* eslint-disable promise/prefer-await-to-then */ -/* eslint-disable promise/no-nesting */ -/* eslint-disable promise/always-return */ -/* eslint-disable promise/catch-or-return */ -/* eslint-disable promise/prefer-await-to-callbacks */ -/* eslint-disable @typescript-eslint/no-floating-promises */ -import supertest from 'supertest' -import test from 'unit.js' -import db from '@mds-core/mds-db' -import { ApiServer } from '@mds-core/mds-api-server' -import { SCOPED_AUTH } from '@mds-core/mds-test-data' -import { providers, MOCHA_PROVIDER_ID } from '@mds-core/mds-providers' -import uuid from 'uuid' -import { PROPULSION_TYPES, VEHICLE_TYPES } from '@mds-core/mds-types' -import { api } from '../api' - -const APP_JSON = 'application/json; charset=utf-8' - -const provider_id = MOCHA_PROVIDER_ID -const device_id = uuid() - -const request = supertest(ApiServer(api)) -const EMPTY_SCOPE = SCOPED_AUTH([], '') -const EVENTS_READ_SCOPE = SCOPED_AUTH(['events:read'], MOCHA_PROVIDER_ID) -const VEHICLES_READ_SCOPE = SCOPED_AUTH(['vehicles:read'], MOCHA_PROVIDER_ID) -const PROVIDERS_READ_SCOPE = SCOPED_AUTH(['providers:read'], MOCHA_PROVIDER_ID) - -before('Initializing Database', async () => { - await db.initialize() -}) - -describe('Verify API', () => { - before(done => { - const timestamp = Date.now() - db.writeDevice({ - device_id, - provider_id, - vehicle_id: 'test-vehicle', - propulsion: [PROPULSION_TYPES.electric], - type: VEHICLE_TYPES.scooter, - recorded: timestamp - }).then(() => { - db.writeEvent({ - provider_id, - device_id, - event_type: 'trip_start', - telemetry: { - provider_id, - device_id, - timestamp, - gps: { lat: 37.4230723, lng: -122.13742939999999 } - }, - telemetry_timestamp: timestamp, - trip_id: uuid(), - timestamp, - recorded: timestamp - }).then(() => done()) - }) - }) - - it('Get Events (no authorization)', done => { - request - .get('/native/events') - .expect(401) - .end((err, result) => { - test.value(result).hasHeader('content-type', APP_JSON) - done(err) - }) - }) - - it('Get Events (no scope)', done => { - request - .get('/native/events') - .set('Authorization', EMPTY_SCOPE) - .expect(403) - .end((err, result) => { - test.value(result).hasHeader('content-type', APP_JSON) - done(err) - }) - }) - - it('Get Events', done => { - request - .get('/native/events') - .set('Authorization', EVENTS_READ_SCOPE) - .expect(200) - .end((err1, result1) => { - test.value(result1).hasHeader('content-type', APP_JSON) - test.object(result1.body).hasProperty('version') - test.object(result1.body).hasProperty('events') - test.value(result1.body.events.length).is(1) - test.object(result1.body.events[0]).hasProperty('device_id', device_id) - test.object(result1.body).hasProperty('cursor') - if (err1) { - done(err1) - } else { - request - .get(`/native/events/${result1.body.cursor}`) - .set('Authorization', EVENTS_READ_SCOPE) - .expect(200) - .end((err2, result2) => { - test.value(result2).hasHeader('content-type', APP_JSON) - test.object(result2.body).hasProperty('version') - test.object(result2.body).hasProperty('events') - test.value(result2.body.events.length).is(0) - test.object(result2.body).hasProperty('cursor', result1.body.cursor) - if (err2) { - done(err2) - } else { - request - .get(`/native/events/${result1.body.cursor}?provider_id=invalid-filter-with-cursor`) - .set('Authorization', EVENTS_READ_SCOPE) - .expect(400) - .end(err3 => { - test.value(result2).hasHeader('content-type', APP_JSON) - done(err3) - }) - } - }) - } - }) - }) - - it('Get Events (Bad Request)', done => { - request - .get('/native/events?provider_id=invalid-provider-id') - .set('Authorization', EVENTS_READ_SCOPE) - .expect(400) - .end((err, result) => { - test.value(result).hasHeader('content-type', APP_JSON) - done(err) - }) - }) - - it('Get Events (Bad Cursor)', done => { - request - .get('/native/events/invalid-cursor') - .set('Authorization', EVENTS_READ_SCOPE) - .expect(400) - .end((err, result) => { - test.value(result).hasHeader('content-type', APP_JSON) - done(err) - }) - }) - - it('Get Vehicle (no authorization)', done => { - request - .get(`/native/vehicles/${device_id}`) - .expect(401) - .end((err, result) => { - test.value(result).hasHeader('content-type', APP_JSON) - done(err) - }) - }) - - it('Get Vehicle (no scope)', done => { - request - .get(`/native/vehicles/${device_id}`) - .set('Authorization', EMPTY_SCOPE) - .expect(403) - .end((err, result) => { - test.value(result).hasHeader('content-type', APP_JSON) - done(err) - }) - }) - - it('Get Vehicle', done => { - request - .get(`/native/vehicles/${device_id}`) - .set('Authorization', VEHICLES_READ_SCOPE) - .expect(200) - .end((err, result) => { - test.value(result).hasHeader('content-type', APP_JSON) - test.object(result.body).hasProperty('version') - test.object(result.body).hasProperty('vehicle') - test.object(result.body.vehicle).hasProperty('device_id', device_id) - done(err) - }) - }) - - it('Get Vehicle (not found)', done => { - request - .get(`/native/vehicles/${uuid()}`) - .set('Authorization', VEHICLES_READ_SCOPE) - .expect(404) - .end((err, result) => { - test.value(result).hasHeader('content-type', APP_JSON) - done(err) - }) - }) - - it('Get Vehicle (bad request)', done => { - request - .get(`/native/vehicles/invalid-device-id`) - .set('Authorization', VEHICLES_READ_SCOPE) - .expect(400) - .end((err, result) => { - test.value(result).hasHeader('content-type', APP_JSON) - done(err) - }) - }) - - it('Get Providers (no authorization)', done => { - request - .get(`/native/providers`) - .expect(401) - .end((err, result) => { - test.value(result).hasHeader('content-type', APP_JSON) - done(err) - }) - }) - - it('Get Providers (no scope)', done => { - request - .get(`/native/providers`) - .set('Authorization', EMPTY_SCOPE) - .expect(403) - .end((err, result) => { - test.value(result).hasHeader('content-type', APP_JSON) - done(err) - }) - }) - - it('Get Providers', done => { - request - .get(`/native/providers`) - .set('Authorization', PROVIDERS_READ_SCOPE) - .expect(200) - .end((err, result) => { - test.value(result).hasHeader('content-type', APP_JSON) - test.object(result.body).hasProperty('version') - test.object(result.body).hasProperty('providers') - test.value(result.body.providers.length).is(Object.keys(providers).length) - done(err) - }) - }) -}) - -after('Shutting down Database', async () => { - await db.shutdown() -}) diff --git a/packages/mds-native/types.ts b/packages/mds-native/types.ts deleted file mode 100644 index 734d8b4c0..000000000 --- a/packages/mds-native/types.ts +++ /dev/null @@ -1,74 +0,0 @@ -/* - Copyright 2019 City of Los Angeles. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - */ - -import { ApiRequest, ApiResponse, ApiResponseLocals } from '@mds-core/mds-api-server' -import { UUID, VehicleEvent, Recorded, Device, Provider } from '@mds-core/mds-types' - -// Place newer versions at the beginning of the list -const NATIVE_API_VERSIONS = ['0.1.0'] as const -type NATIVE_API_VERSION = typeof NATIVE_API_VERSIONS[number] -export const [NativeApiCurrentVersion] = NATIVE_API_VERSIONS - -// Allow adding type definitions for Express Request objects -export type NativeApiRequest = ApiRequest - -// Allow adding type definitions for Express Response objects -export interface NativeApiResponse extends ApiResponse { - locals: ApiResponseLocals & { - provider_id: UUID - } -} - -export interface NativeApiGetEventsRequest extends NativeApiRequest { - params: { - cursor: string - } - // Query string parameters always come in as strings - query: Partial< - { - [P in 'limit' | 'device_id' | 'provider_id' | 'start_time' | 'end_time']: string - } - > -} - -interface NativeApiResponseBody { - version: NATIVE_API_VERSION -} - -interface NativeApiGetEventsReponseBody extends NativeApiResponseBody { - events: Omit, 'id' | 'service_area_id'>[] - cursor: string -} - -export type NativeApiGetEventsReponse = NativeApiResponse - -export interface NativeApiGetVehiclesRequest extends NativeApiRequest { - params: { device_id: UUID } -} - -interface NativeApiGetVehiclesResponseBody extends NativeApiResponseBody { - vehicle: Omit, 'id'> -} - -export type NativeApiGetVehiclesResponse = NativeApiResponse - -export type NativeApiGetProvidersRequest = NativeApiRequest - -interface NativeApiGetProvidersResponseBody extends NativeApiResponseBody { - providers: Provider[] -} - -export type NativeApiGetProvidersResponse = NativeApiResponse diff --git a/packages/mds-policy-author/api.ts b/packages/mds-policy-author/api.ts index 0035bda26..5a2f342ac 100644 --- a/packages/mds-policy-author/api.ts +++ b/packages/mds-policy-author/api.ts @@ -15,474 +15,236 @@ */ import express from 'express' -import Joi from '@hapi/joi' -import uuid from 'uuid' -import { VEHICLE_TYPES, DAYS_OF_WEEK } from '@mds-core/mds-types' -import db from '@mds-core/mds-db' import { + uuid, pathsFor, - ServerError, - UUID_REGEX, - NotFoundError, + AlreadyPublishedError, BadParamsError, - AlreadyPublishedError + NotFoundError, + ValidationError, + ServerError, + isUUID, + DependencyMissingError, + ConflictError } from '@mds-core/mds-utils' -import log from '@mds-core/mds-logger' - -import { checkAccess } from '@mds-core/mds-api-server' - -const ruleSchema = Joi.object().keys({ - name: Joi.string().required(), - rule_id: Joi.string() - .guid() - .required(), - rule_type: Joi.string() - .valid(['count', 'time', 'speed', 'user']) - .required(), - rule_units: Joi.string().valid(['seconds', 'minutes', 'hours', 'mph', 'kph']), - geographies: Joi.array().items(Joi.string().guid()), - statuses: Joi.object().keys({ - available: Joi.array(), - reserved: Joi.array(), - unavailable: Joi.array(), - removed: Joi.array(), - inactive: Joi.array(), - trip: Joi.array(), - elsewhere: Joi.array() - }), - vehicle_types: Joi.array().items(Joi.string().valid(Object.values(VEHICLE_TYPES))), - maximum: Joi.number(), - minimum: Joi.number(), - start_time: Joi.string(), - end_time: Joi.string(), - days: Joi.array().items(Joi.string().valid(Object.values(DAYS_OF_WEEK))), - messages: Joi.object(), - value_url: Joi.string().uri() -}) - -const policySchema = Joi.object().keys({ - name: Joi.string().required(), - description: Joi.string().required(), - policy_id: Joi.string() - .guid() - .required(), - start_date: Joi.date() - .timestamp('javascript') - .required(), - end_date: Joi.date() - .timestamp('javascript') - .allow(null), - prev_policies: Joi.array() - .items(Joi.string().guid()) - .allow(null), - provider_ids: Joi.array() - .items(Joi.string().guid()) - .allow(null), - rules: Joi.array() - .min(1) - .items(ruleSchema) - .required() -}) - -const featureSchema = Joi.object() - .keys({ - type: Joi.string() - .valid(['Feature']) - .required(), - properties: Joi.object().required(), - geometry: Joi.object().required() - }) - .unknown(true) // TODO +import db from '@mds-core/mds-db' -const featureCollectionSchema = Joi.object() - .keys({ - type: Joi.string() - .valid(['FeatureCollection']) - .required(), - features: Joi.array() - .min(1) - .items(featureSchema) - .required() - }) - .unknown(true) // TODO +import { policyValidationDetails } from '@mds-core/mds-schema-validators' +import logger from '@mds-core/mds-logger' -const geographySchema = Joi.object() - .keys({ - geography_id: Joi.string() - .guid() - .required(), - geography_json: featureCollectionSchema, - read_only: Joi.boolean().allow(null), - previous_geography_ids: Joi.array() - .items(Joi.string().guid()) - .allow(null), - name: Joi.string().required() - }) - .unknown(true) +import { checkAccess, AccessTokenScopeValidator, ApiRequest, ApiResponse } from '@mds-core/mds-api-server' +import { PolicyAuthorApiVersionMiddleware } from './middleware/policy-author-api-version' +import { + PolicyAuthorApiRequest, + PostPolicyResponse, + EditPolicyResponse, + DeletePolicyResponse, + PolicyAuthorApiAccessTokenScopes, + PublishPolicyResponse, + GetPolicyMetadataResponse, + GetPolicyMetadatumResponse, + EditPolicyMetadataResponse +} from './types' + +const checkPolicyAuthorApiAccess = (validator: AccessTokenScopeValidator) => + checkAccess(validator) function api(app: express.Express): express.Express { - app.get(pathsFor('/policies'), checkAccess(scopes => scopes.includes('policies:read')), async (req, res) => { - const { get_published = null, get_unpublished = null } = req.query - log.info('read /policies', req.query) + app.use(PolicyAuthorApiVersionMiddleware) - try { - const policies = await db.readPolicies({ get_published, get_unpublished }) + app.post( + pathsFor('/policies'), + checkPolicyAuthorApiAccess(scopes => scopes.includes('policies:write')), + async (req: PolicyAuthorApiRequest, res: PostPolicyResponse, next: express.NextFunction) => { + const policy = { policy_id: uuid(), ...req.body } - // Let's not worry about filtering for just active policies at the moment. + const details = policyValidationDetails(policy) - /* - const prev_policies: UUID[] = policies.reduce((prev_policies_acc: UUID[], policy: Policy) => { - if (policy.prev_policies) { - prev_policies_acc.push(...policy.prev_policies) - } - return prev_policies_acc - }, []) - const active = policies.filter(p => { - // overlapping segment logic - const p_start_date = p.start_date - const p_end_date = p.end_date || Number.MAX_SAFE_INTEGER - return end_date >= p_start_date && p_end_date >= start_date && !prev_policies.includes(p.policy_id) - }) - */ - res.status(200).send(policies) - } catch (err) { - await log.error('failed to read policies', err) - if (err instanceof BadParamsError) { - res.status(400).send({ - result: - 'Cannot set both get_unpublished and get_published to be true. If you want all policies, set both params to false or do not send them.' - }) + if (details != null) { + return res.status(400).send({ error: new ValidationError(JSON.stringify(details)) }) } - res.status(404).send({ - result: 'not found' - }) - } - }) - - app.post(pathsFor('/policies'), checkAccess(scopes => scopes.includes('policies:write')), async (req, res) => { - const policy = { policy_id: uuid(), ...req.body } - const validation = Joi.validate(policy, policySchema) - const details = validation.error ? validation.error.details : null - if (details) { - await log.error('invalid policy json', details) - return res.status(400).send(details) - } - - try { - await db.writePolicy(policy) - return res.status(201).send(policy) - } catch (err) { - if (err.code === '23505') { - return res.status(409).send({ result: `policy ${policy.policy_id} already exists! Did you mean to PUT?` }) + try { + await db.writePolicy(policy) + return res.status(201).send({ version: res.locals.version, data: { policy } }) + } catch (error) { + if (error.code === '23505') { + return res + .status(409) + .send({ error: new ConflictError(`policy ${policy.policy_id} already exists! Did you mean to PUT?`) }) + } + /* istanbul ignore next */ + return next(new ServerError(error)) } - /* istanbul ignore next */ - await log.error('failed to write policy', err) - /* istanbul ignore next */ - return res.status(500).send({ error: new ServerError(err) }) } - }) + ) app.post( pathsFor('/policies/:policy_id/publish'), - checkAccess(scopes => scopes.includes('policies:publish')), - async (req, res) => { + checkPolicyAuthorApiAccess(scopes => scopes.includes('policies:publish')), + async (req: PolicyAuthorApiRequest, res: PublishPolicyResponse, next: express.NextFunction) => { const { policy_id } = req.params try { - await db.publishPolicy(policy_id) - return res.status(200).send({ result: `successfully published policy of id ${policy_id}` }) - } catch (err) { - if (err instanceof NotFoundError) { - if (err.message.includes('geography')) { - const geography_id = err.message.match(UUID_REGEX) - return res.status(404).send({ error: `geography_id ${geography_id} not_found` }) - } - if (err.message.includes('policy')) { - return res.status(404).send({ error: `policy_id ${policy_id} not_found` }) - } + const policy = await db.publishPolicy(policy_id) + return res.status(200).send({ version: res.locals.version, data: { policy } }) + } catch (error) { + logger.error('failed to publish policy', error.stack) + if (error instanceof AlreadyPublishedError) { + return res.status(409).send({ error }) } - if (err instanceof AlreadyPublishedError) { - return res.status(409).send({ error: `policy_id ${policy_id} has already been published` }) + if (error instanceof NotFoundError) { + return res.status(404).send({ error }) + } + if (error instanceof DependencyMissingError) { + return res.status(424).send({ error }) } /* istanbul ignore next */ - await log.error('failed to publish policy', err.stack) - /* istanbul ignore next */ - return res.status(404).send({ result: 'not found' }) + return next(new ServerError(error)) } } ) app.put( pathsFor('/policies/:policy_id'), - checkAccess(scopes => scopes.includes('policies:write')), - async (req, res) => { + checkPolicyAuthorApiAccess(scopes => scopes.includes('policies:write')), + async (req: PolicyAuthorApiRequest, res: EditPolicyResponse, next: express.NextFunction) => { const policy = req.body - const validation = Joi.validate(policy, policySchema) - const details = validation.error ? validation.error.details : null - if (details) { - return res.status(400).send(details) + const details = policyValidationDetails(policy) + + if (details !== null) { + return res.status(400).send({ error: new ValidationError(JSON.stringify(details)) }) } try { await db.editPolicy(policy) - return res.status(200).send({ result: `successfully edited policy ${policy}` }) - } catch (err) { - if (err instanceof NotFoundError) { - return res.status(404).send({ error: 'not found' }) + const result = await db.readPolicy(policy.policy_id) + return res.status(200).send({ version: res.locals.version, data: { policy: result } }) + } catch (error) { + if (error instanceof NotFoundError) { + return res.status(404).send({ error }) } - if (err instanceof AlreadyPublishedError) { - return res.status(409).send({ error: `policy ${policy.policy_id} has already been published!` }) + if (error instanceof AlreadyPublishedError) { + return res.status(409).send({ error }) } /* istanbul ignore next */ - await log.error('failed to edit policy', err.stack) - /* istanbul ignore next */ - if (err instanceof NotFoundError) { - res.status(404).send({ result: 'not found' }) - } else { - res.status(500).send(new ServerError(err)) - } + return next(new ServerError(error)) } } ) app.delete( pathsFor('/policies/:policy_id'), - checkAccess(scopes => scopes.includes('policies:delete')), - async (req, res) => { + checkPolicyAuthorApiAccess(scopes => scopes.includes('policies:delete')), + async (req: PolicyAuthorApiRequest, res: DeletePolicyResponse, next: express.NextFunction) => { const { policy_id } = req.params try { await db.deletePolicy(policy_id) - return res.status(200).send({ result: `successfully deleted policy of id ${policy_id}` }) - } catch (err) { + return res.status(200).send({ version: res.locals.version, data: { policy_id } }) + } catch (error) { /* istanbul ignore next */ - await log.error('failed to delete policy', err.stack) + logger.error('failed to delete policy', error.stack) /* istanbul ignore next */ - return res.status(404).send({ result: 'policy either not found, or has already been published' }) + return res.status(404).send({ error }) + /* FIXME: Potential server-errors are caught and handled as a 404, need to add explicit error handling */ } } ) - app.get(pathsFor('/policies/meta/'), checkAccess(scopes => scopes.includes('policies:read')), async (req, res) => { - const { get_published = null, get_unpublished = null } = req.query - log.info('read /policies/meta', req.query) - try { - const metadata = await db.readBulkPolicyMetadata({ get_published, get_unpublished }) - - res.status(200).send(metadata) - } catch (err) { - await log.error('failed to read policies', err) - if (err instanceof BadParamsError) { - res.status(400).send({ - result: - 'Cannot set both get_unpublished and get_published to be true. If you want all policy metadata, set both params to false or do not send them.' - }) + app.get( + pathsFor('/policies/meta/'), + checkPolicyAuthorApiAccess(scopes => scopes.includes('policies:read')), + async (req: PolicyAuthorApiRequest, res: GetPolicyMetadataResponse, next: express.NextFunction) => { + const { get_published, get_unpublished } = req.query + const params = { + get_published: get_published ? get_published === 'true' : null, + get_unpublished: get_unpublished ? get_unpublished === 'true' : null } - res.status(404).send({ - result: 'not found' - }) - } - }) - app.get( - pathsFor('/policies/:policy_id'), - checkAccess(scopes => scopes.includes('policies:read')), - async (req, res) => { - const { policy_id } = req.params try { - const policies = await db.readPolicies({ policy_id }) - if (policies.length > 0) { - res.status(200).send(policies[0]) - } else { - res.status(404).send({ result: 'not found' }) + const policy_metadata = await db.readBulkPolicyMetadata(params) + + if (policy_metadata.length === 0) { + throw new NotFoundError('No metadata found') + } + + res.status(200).send({ version: res.locals.version, data: { policy_metadata } }) + } catch (error) { + if (error instanceof BadParamsError) { + res.status(400).send({ + error: + 'Cannot set both get_unpublished and get_published to be true. If you want all policy metadata, set both params to false or do not send them.' + }) + } + if (error instanceof NotFoundError) { + res.status(404).send({ + error + }) } - } catch (err) { - await log.error('failed to read one policy', err) - res.status(404).send({ result: 'not found' }) + /* istanbul ignore next */ + return next(new ServerError(error)) } } ) app.get( pathsFor('/policies/:policy_id/meta'), - checkAccess(scopes => scopes.includes('policies:read')), - async (req, res) => { + checkPolicyAuthorApiAccess(scopes => scopes.includes('policies:read')), + async (req: PolicyAuthorApiRequest, res: GetPolicyMetadatumResponse, next: express.NextFunction) => { const { policy_id } = req.params + try { + if (!isUUID(policy_id)) { + throw new BadParamsError(`${policy_id} is not a valid UUID`) + } + const result = await db.readSinglePolicyMetadata(policy_id) - return res.status(200).send(result) - } catch (err) { - await log.error('failed to read policy metadata', err.stack) - return res.status(404).send({ result: 'not found' }) + return res.status(200).send({ version: res.locals.version, data: { policy_metadata: result } }) + } catch (error) { + if (error instanceof NotFoundError) { + return res.status(404).send({ error }) + } + if (error instanceof BadParamsError) { + return res.status(400).send({ error }) + } + /* istanbul ignore next */ + return next(new ServerError(error)) } } ) app.put( pathsFor('/policies/:policy_id/meta'), - checkAccess(scopes => scopes.includes('policies:write')), - async (req, res) => { + checkPolicyAuthorApiAccess(scopes => scopes.includes('policies:write')), + async (req: PolicyAuthorApiRequest, res: EditPolicyMetadataResponse, next: express.NextFunction) => { const policy_metadata = req.body try { await db.updatePolicyMetadata(policy_metadata) - return res.status(200).send(policy_metadata) + return res.status(200).send({ version: res.locals.version, data: { policy_metadata } }) } catch (updateErr) { if (updateErr instanceof NotFoundError) { try { await db.writePolicyMetadata(policy_metadata) - return res.status(201).send(policy_metadata) + return res.status(201).send({ version: res.locals.version, data: { policy_metadata } }) } catch (writeErr) { - await log.error('failed to write policy metadata', writeErr.stack) - return res.status(500).send(new ServerError()) + /* istanbul ignore next */ + return next(new ServerError(writeErr)) } - } else { - return res.status(500).send(new ServerError()) } + /* istanbul ignore next */ + return next(new ServerError(updateErr)) } } ) - app.get(pathsFor('/geographies/meta/'), checkAccess(scopes => scopes.includes('policies:read')), async (req, res) => { - const get_read_only = req.query === 'true' - - log.info('read /geographies/meta', req.query) - try { - const metadata = await db.readBulkGeographyMetadata({ get_read_only }) - res.status(200).send(metadata) - } catch (err) { - await log.error('failed to read geography metadata', err) - res.status(404).send({ - result: 'not found' - }) - } - }) - - app.get( - pathsFor('/geographies/:geography_id'), - checkAccess(scopes => scopes.includes('policies:read')), - async (req, res) => { - log.info('read geo', JSON.stringify(req.params)) - const { geography_id } = req.params - log.info('read geo', geography_id) - try { - const geography = await db.readSingleGeography(geography_id) - res.status(200).send(geography) - } catch (err) { - await log.error('failed to read geography', err.stack) - res.status(404).send({ result: 'not found' }) - } - } - ) - - app.post(pathsFor('/geographies/'), checkAccess(scopes => scopes.includes('policies:write')), async (req, res) => { - const geography = req.body - const validation = Joi.validate(geography, geographySchema) - const details = validation.error ? validation.error.details : null - if (details) { - return res.status(400).send(details) - } - - try { - await db.writeGeography(geography) - return res.status(201).send(geography) - } catch (err) { - if (err.code === '23505') { - return res - .status(409) - .send({ result: `geography ${geography.geography_id} already exists! Did you mean to PUT?` }) - } - /* istanbul ignore next */ - await log.error('failed to write geography', err.stack) - /* istanbul ignore next */ - return res.status(500).send(new ServerError(err)) - } - }) - - app.put( - pathsFor('/geographies/:geography_id'), - checkAccess(scopes => scopes.includes('policies:write')), - async (req, res) => { - const geography = req.body - const validation = Joi.validate(geography, geographySchema) - const details = validation.error ? validation.error.details : null - if (details) { - return res.status(400).send(details) - } - - try { - await db.editGeography(geography) - return res.status(201).send(geography) - } catch (err) { - await log.error('failed to write geography', err.stack) - return res.status(404).send({ result: 'not found' }) - } - } - ) - - app.delete( - pathsFor('/geographies/:geography_id'), - checkAccess(scopes => scopes.includes('policies:delete')), - async (req, res) => { - const { geography_id } = req.params - try { - await db.deleteGeography(geography_id) - return res.status(200).send({ result: `Successfully deleted geography of id ${geography_id}` }) - } catch (err) { - await log.error('failed to delete geography', err.stack) - return res.status(404).send({ result: 'geography either not found or already published' }) - } - } - ) - - app.get( - pathsFor('/geographies/:geography_id/meta'), - checkAccess(scopes => scopes.includes('policies:read')), - async (req, res) => { - const { geography_id } = req.params - try { - const geography_metadata = await db.readSingleGeographyMetadata(geography_id) - return res.status(200).send(geography_metadata) - } catch (err) { - await log.error('failed to read geography metadata', err.stack) - return res.status(404).send({ result: 'not found' }) - } - } - ) - - app.get(pathsFor('/geographies'), checkAccess(scopes => scopes.includes('policies:read')), async (req, res) => { - const summary = req.query.summary === 'true' - try { - const geographies = summary ? await db.readGeographySummaries() : await db.readGeographies() - return res.status(200).send(geographies) - } catch (err) { - await log.error('failed to read geographies', err.stack) - return res.status(404).send({ result: 'not found' }) - } + /* eslint-reason global error handling middleware */ + /* istanbul ignore next */ + /* eslint-disable-next-line @typescript-eslint/no-unused-vars */ + app.use(async (error: Error, req: ApiRequest, res: ApiResponse) => { + await logger.error(req.method, req.originalUrl, error) + return res.status(500).send({ error }) }) - app.put( - pathsFor('/geographies/:geography_id/meta'), - checkAccess(scopes => scopes.includes('policies:write')), - async (req, res) => { - const geography_metadata = req.body - try { - await db.updateGeographyMetadata(geography_metadata) - return res.status(200).send(geography_metadata) - } catch (updateErr) { - if (updateErr instanceof NotFoundError) { - try { - await db.writeGeographyMetadata(geography_metadata) - return res.status(201).send(geography_metadata) - } catch (writeErr) { - await log.error('failed to write geography metadata', writeErr.stack) - return res.status(500).send(new ServerError()) - } - } else { - return res.status(500).send(new ServerError()) - } - } - } - ) return app } diff --git a/packages/mds-policy-author/middleware/index.ts b/packages/mds-policy-author/middleware/index.ts new file mode 100644 index 000000000..3cb404609 --- /dev/null +++ b/packages/mds-policy-author/middleware/index.ts @@ -0,0 +1 @@ +export * from './policy-author-api-version' diff --git a/packages/mds-policy-author/middleware/policy-author-api-version.ts b/packages/mds-policy-author/middleware/policy-author-api-version.ts new file mode 100644 index 000000000..f914a3c9b --- /dev/null +++ b/packages/mds-policy-author/middleware/policy-author-api-version.ts @@ -0,0 +1,7 @@ +import { ApiVersionMiddleware } from '@mds-core/mds-api-server' +import { POLICY_AUTHOR_API_SUPPORTED_VERSIONS, POLICY_AUTHOR_API_DEFAULT_VERSION } from '../types' + +export const PolicyAuthorApiVersionMiddleware = ApiVersionMiddleware( + 'application/vnd.mds.policy-author+json', + POLICY_AUTHOR_API_SUPPORTED_VERSIONS +).withDefaultVersion(POLICY_AUTHOR_API_DEFAULT_VERSION) diff --git a/packages/mds-policy-author/package.json b/packages/mds-policy-author/package.json index 44c9a6d17..ade91527f 100644 --- a/packages/mds-policy-author/package.json +++ b/packages/mds-policy-author/package.json @@ -1,36 +1,31 @@ { "name": "@mds-core/mds-policy-author", "description": "MDS Policy Author API", - "version": "0.0.15", + "version": "0.0.23", "private": true, + "author": "City of Los Angeles", "licence": "Apache-2.0", - "engineStrict": true, - "engines": { - "node": ">= 8.0.0", - "npm": ">= 6.8.0" - }, "dependencies": { - "@hapi/joi": "15.1.1", - "@mds-core/mds-api-helpers": "0.1.18", - "@mds-core/mds-api-server": "0.1.18", - "@mds-core/mds-db": "0.1.18", - "@mds-core/mds-logger": "0.1.16", - "@mds-core/mds-providers": "0.1.18", - "@mds-core/mds-utils": "0.1.18", - "express": "4.17.1", - "joi-to-json-schema": "5.1.0", - "uuid": "3.3.3" + "@mds-core/mds-api-helpers": "0.1.26", + "@mds-core/mds-api-server": "0.1.26", + "@mds-core/mds-db": "0.1.26", + "@mds-core/mds-logger": "0.1.24", + "@mds-core/mds-providers": "0.1.26", + "@mds-core/mds-schema-validators": "0.1.2", + "@mds-core/mds-types": "0.1.23", + "@mds-core/mds-utils": "0.1.26", + "@types/express": "4.17.6", + "express": "4.17.1" }, "main": "dist/index.js", "types": "dist/index.d.ts", "scripts": { "build": "tsc --build tsconfig.build.json", "start": "PATH_PREFIX=/policy-author yarn watch server", - "test": "yarn test:prettier && yarn test:eslint && yarn test:unit", + "test": "yarn test:eslint && yarn test:unit", "test:eslint": "eslint --ignore-path ../../.gitignore '**/*.ts'", - "test:prettier": "prettier --write --check --ignore-path ../../.gitignore '**/*.ts'", - "test:unit": "PATH_PREFIX=/policy-author DOTENV_CONFIG_PATH=../../.env nyc --check-coverage --exclude tests --extension .ts --lines 65 --reporter=text --reporter=html ts-mocha --timeout 4000 --project ../../tsconfig.json --require source-map-support/register --require dotenv/config --recursive --timeout 6000 tests/**/*.ts", - "watch": "nodemon --watch '../../packages' --ext 'ts' --ignore '*.d.ts' --exec yarn watch:exec --", - "watch:exec": "yarn build && ts-node" + "test:unit": "PATH_PREFIX=/policy-author DOTENV_CONFIG_PATH=../../.env nyc ts-mocha --project ../../tsconfig.json", + "ts-node": "yarn build && DOTENV_CONFIG_PATH=../../.env ts-node -r dotenv/config", + "watch": "nodemon --watch '../../packages' --ext 'ts' --ignore '*.d.ts' --exec yarn ts-node --" } } diff --git a/packages/mds-policy-author/server.ts b/packages/mds-policy-author/server.ts index db3ba6862..63bbbc36b 100644 --- a/packages/mds-policy-author/server.ts +++ b/packages/mds-policy-author/server.ts @@ -11,14 +11,7 @@ limitations under the License. */ -// Express local -import { ApiServer } from '@mds-core/mds-api-server' +import { ApiServer, HttpServer } from '@mds-core/mds-api-server' import { api } from './api' -const { - env: { npm_package_name, PORT = 4007 } -} = process - -/* eslint-reason avoids import of logger */ -/* eslint-disable-next-line no-console */ -ApiServer(api, { handleCors: true }).listen(PORT, () => console.log(`${npm_package_name} running on port ${PORT}`)) +HttpServer(ApiServer(api), { port: process.env.POLICY_AUTHOR_API_PORT }) diff --git a/packages/mds-policy-author/tests/api.spec.ts b/packages/mds-policy-author/tests/api.spec.ts index cd8ac22b2..f7e6373fb 100644 --- a/packages/mds-policy-author/tests/api.spec.ts +++ b/packages/mds-policy-author/tests/api.spec.ts @@ -27,32 +27,30 @@ import should from 'should' import supertest from 'supertest' import test from 'unit.js' import db from '@mds-core/mds-db' -import { clone, isUUID } from '@mds-core/mds-utils' -import { Policy, Geography } from '@mds-core/mds-types' +import { clone, isUUID, uuid } from '@mds-core/mds-utils' +import { Policy } from '@mds-core/mds-types' import { ApiServer } from '@mds-core/mds-api-server' import { POLICY_JSON, POLICY2_JSON, POLICY3_JSON, - POLICY4_JSON, POLICY_JSON_MISSING_POLICY_ID, - SUPERSEDING_POLICY_JSON, POLICY_UUID, POLICY2_UUID, GEOGRAPHY_UUID, - GEOGRAPHY2_UUID, LA_CITY_BOUNDARY, - DISTRICT_SEVEN, - SCOPED_AUTH + SCOPED_AUTH, + PUBLISHED_POLICY } from '@mds-core/mds-test-data' import { api } from '../api' +import { POLICY_AUTHOR_API_DEFAULT_VERSION } from '../types' /* eslint-disable-next-line no-console */ const log = console.log.bind(console) const request = supertest(ApiServer(api)) -const APP_JSON = 'application/json; charset=utf-8' +const APP_JSON = 'application/vnd.mds.policy-author+json; charset=utf-8; version=0.4' const EMPTY_SCOPE = SCOPED_AUTH([], '') const EVENTS_READ_SCOPE = SCOPED_AUTH(['events:read']) const POLICIES_WRITE_SCOPE = SCOPED_AUTH(['policies:write']) @@ -105,7 +103,7 @@ describe('Tests app', () => { .expect(400) .end((err, result) => { const body = result.body - test.value(body[0].message).contains('rule_type') + test.value(body.error.reason).contains('rule_type') test.value(result).hasHeader('content-type', APP_JSON) done(err) }) @@ -159,6 +157,7 @@ describe('Tests app', () => { .expect(201) .end((err, result) => { test.value(result).hasHeader('content-type', APP_JSON) + test.value(result.body.version).is(POLICY_AUTHOR_API_DEFAULT_VERSION) done(err) }) }) @@ -190,13 +189,19 @@ describe('Tests app', () => { it('edits one current policy', async () => { const policy = clone(POLICY_JSON) policy.name = 'a shiny new name' - await request + const apiResult = await request .put(`/policies/${POLICY_UUID}`) .set('Authorization', POLICIES_WRITE_SCOPE) .send(policy) .expect(200) - const [result] = await db.readPolicies({ policy_id: policy.policy_id, get_unpublished: true }) + test.value(apiResult.body.version).is(POLICY_AUTHOR_API_DEFAULT_VERSION) + + const [result] = await db.readPolicies({ + policy_id: policy.policy_id, + get_unpublished: true, + get_published: null + }) test.value(result.name).is('a shiny new name') }) @@ -209,6 +214,7 @@ describe('Tests app', () => { .expect(201) .end((err, result) => { test.value(result).hasHeader('content-type', APP_JSON) + test.value(result.body.version).is(POLICY_AUTHOR_API_DEFAULT_VERSION) done(err) }) }) @@ -223,6 +229,7 @@ describe('Tests app', () => { .expect(201) .end((err, result) => { test.value(result).hasHeader('content-type', APP_JSON) + test.value(result.body.version).is(POLICY_AUTHOR_API_DEFAULT_VERSION) done(err) }) }) @@ -269,8 +276,17 @@ describe('Tests app', () => { }) }) - it('can publish a policy', async () => { + it('cannot publish a policy if the geo is not published', async () => { await db.writeGeography({ name: 'LA', geography_id: GEOGRAPHY_UUID, geography_json: LA_CITY_BOUNDARY }) + const result = await request + .post(`/policies/${POLICY_JSON.policy_id}/publish`) + .set('Authorization', POLICIES_PUBLISH_SCOPE) + .expect(424) + test.value(result).hasHeader('content-type', APP_JSON) + }) + + it('can publish a policy if the geo is published', async () => { + await db.publishGeography({ geography_id: GEOGRAPHY_UUID }) const result = await request .post(`/policies/${POLICY_JSON.policy_id}/publish`) .set('Authorization', POLICIES_PUBLISH_SCOPE) @@ -358,7 +374,10 @@ describe('Tests app', () => { const body = result.body log('read back nonexistent policy response:', body) test.value(result).hasHeader('content-type', APP_JSON) - await db.readPolicies({ policy_id: POLICY2_UUID }).should.be.fulfilledWith([]) + test.value(result.body.version).is(POLICY_AUTHOR_API_DEFAULT_VERSION) + await db + .readPolicies({ policy_id: POLICY2_UUID, get_published: null, get_unpublished: null }) + .should.be.fulfilledWith([]) done(err) }) }) @@ -373,6 +392,16 @@ describe('Tests app', () => { }) }) + it('cannot GET policy metadata (no entries exist)', done => { + request + .get(`/policies/meta`) + .set('Authorization', POLICIES_READ_SCOPE) + .expect(404) + .end(err => { + done(err) + }) + }) + it('cannot PUTing policy metadata to create (no auth)', async () => { const metadata = { some_arbitrary_thing: 'boop' } await request @@ -393,22 +422,24 @@ describe('Tests app', () => { it('verifies PUTing policy metadata to create', async () => { const metadata = { some_arbitrary_thing: 'boop' } - await request + const apiResult = await request .put(`/policies/${POLICY_UUID}/meta`) .set('Authorization', POLICIES_WRITE_SCOPE) .send({ policy_id: POLICY_UUID, policy_metadata: metadata }) .expect(201) + test.value(apiResult.body.version).is(POLICY_AUTHOR_API_DEFAULT_VERSION) const result = await db.readSinglePolicyMetadata(POLICY_UUID) test.assert(result.policy_metadata.some_arbitrary_thing === 'boop') }) it('verifies PUTing policy metadata to edit', async () => { const metadata = { some_arbitrary_thing: 'beep' } - await request + const apiResult = await request .put(`/policies/${POLICY_UUID}/meta`) .set('Authorization', POLICIES_WRITE_SCOPE) .send({ policy_id: POLICY_UUID, policy_metadata: metadata }) .expect(200) + test.value(apiResult.body.version).is(POLICY_AUTHOR_API_DEFAULT_VERSION) const result = await db.readSinglePolicyMetadata(POLICY_UUID) test.assert(result.policy_metadata.some_arbitrary_thing === 'beep') }) @@ -439,164 +470,55 @@ describe('Tests app', () => { .set('Authorization', POLICIES_READ_SCOPE) .expect(200) .end((err, result) => { - test.assert(result.body.policy_metadata.some_arbitrary_thing === 'beep') + test.value(result.body.data.policy_metadata.some_arbitrary_thing, 'beep') + test.value(result.body.version).is(POLICY_AUTHOR_API_DEFAULT_VERSION) test.value(result).hasHeader('content-type', APP_JSON) done(err) }) }) - it('verifies cannot GET non-existent policy metadata', done => { + it('verifies cannot GET non-uuid policy_id metadata', done => { request .get(`/policies/beepbapboop/meta`) .set('Authorization', POLICIES_READ_SCOPE) - .expect(404) + .expect(400) .end((err, result) => { - test.assert(result.body.result === 'not found') test.value(result).hasHeader('content-type', APP_JSON) done(err) }) }) - it('cannot GET policy metadata (no auth)', async () => { - await request - .get(`/policies/meta`) - .set('Authorization', EMPTY_SCOPE) - .expect(403) - }) - - it('cannot GET policy metadata (wrong auth)', async () => { - await request - .get(`/policies/meta`) - .set('Authorization', EVENTS_READ_SCOPE) - .expect(403) - }) - - it('verifies GETting policy metadata with the same params as for bulk policy reads', async () => { - const result = await request - .get(`/policies/meta`) - .set('Authorization', POLICIES_READ_SCOPE) - .expect(200) - test.assert(result.body.length === 1) - test.value(result).hasHeader('content-type', APP_JSON) - }) - - it('cannot GET a single policy (no auth)', done => { - request - .get(`/policies/${POLICY_UUID}`) - .set('Authorization', EMPTY_SCOPE) - .expect(403) - .end(async err => { - done(err) - }) - }) - - it('cannot GET a single policy (wrong auth)', done => { - request - .get(`/policies/${POLICY_UUID}`) - .set('Authorization', EVENTS_READ_SCOPE) - .expect(403) - .end(async err => { - done(err) - }) - }) - - it('can GET a single policy', done => { - request - .get(`/policies/${POLICY_UUID}`) - .set('Authorization', POLICIES_READ_SCOPE) - .expect(200) - .end(async (err, result) => { - test.assert(result.body.policy_id === POLICY_UUID) - test.assert(result.body.description === POLICY_JSON.description) - done(err) - }) - }) - - it('cannot GET a single nonexistent policy', done => { + it('verifies cannot GET non-existent policy metadata', done => { request - .get(`/policies/544d36c4-29f5-4088-a52f-7c9a64d5874c`) + .get(`/policies/${uuid()}/meta`) .set('Authorization', POLICIES_READ_SCOPE) .expect(404) - .end(async err => { + .end((err, result) => { + test.value(result).hasHeader('content-type', APP_JSON) done(err) }) }) - /* - it('read back one geography', async () => { - await db.writeGeography({ geography_id: GEOGRAPHY_UUID, geography_json: la_city_boundary }) - request - .get(`/geographies/${GEOGRAPHY_UUID}`) - .set('Authorization', AUTH) - .expect(200) - .end((err, result) => { - const body = result.body - log('read back one geo response:', body) - test.value(result).hasHeader('content-type', APP_JSON) - // TODO verify contents - return err - }) - }) - */ - it('cannot GET all active policies (no auth)', async () => { - await request - .get(`/policies`) - .set('Authorization', EMPTY_SCOPE) - .expect(403) - }) - - it('cannot GET all active policies (wrong auth)', async () => { - await request - .get(`/policies`) - .set('Authorization', EVENTS_READ_SCOPE) - .expect(403) - }) - - it('can GET all active policies', async () => { - await db.writeGeography({ name: 'Geography 2', geography_id: GEOGRAPHY2_UUID, geography_json: DISTRICT_SEVEN }) - await db.writePolicy(POLICY4_JSON) - await db.writePolicy(SUPERSEDING_POLICY_JSON) - await db.publishPolicy(SUPERSEDING_POLICY_JSON.policy_id) - request - .get(`/policies`) - .set('Authorization', POLICIES_READ_SCOPE) - .expect(200) - .end(async (policies_err, policies_result) => { - test.assert(policies_result.body.length === 4) - return policies_err - }) + it('cannot GET policy metadata (no auth)', async () => { + await request.get(`/policies/meta`).set('Authorization', EMPTY_SCOPE).expect(403) }) - it('can GET all published policies', done => { - request - .get(`/policies?get_published=true`) - .set('Authorization', POLICIES_READ_SCOPE) - .expect(200) - .end(async (policies_err, policies_result) => { - test.assert(policies_result.body.length === 2) - done(policies_err) - }) + it('cannot GET policy metadata (wrong auth)', async () => { + await request.get(`/policies/meta`).set('Authorization', EVENTS_READ_SCOPE).expect(403) }) - it('can GET all unpublished policies', done => { - request - .get(`/policies?get_unpublished=true`) + it('cannot GET policy metadata with both get_published and get_unpublished set to true', async () => { + await request + .get(`/policies/meta?get_published=true&get_unpublished=true`) .set('Authorization', POLICIES_READ_SCOPE) - .expect(200) - .end(async (policies_err, policies_result) => { - test.assert(policies_result.body.length === 2) - done(policies_err) - }) + .expect(400) }) - it('throws an exception if both get_unpublished and get_published are submitted', done => { - request - .get(`/policies?get_unpublished=true&get_published=true`) - .set('Authorization', POLICIES_READ_SCOPE) - .expect(400) - .end(async policies_err => { - done(policies_err) - }) + it('verifies GETting policy metadata with the same params as for bulk policy reads', async () => { + const result = await request.get(`/policies/meta`).set('Authorization', POLICIES_READ_SCOPE).expect(200) + test.assert(result.body.data.policy_metadata.length === 1) + test.value(result.body.version).is(POLICY_AUTHOR_API_DEFAULT_VERSION) + test.value(result).hasHeader('content-type', APP_JSON) }) it('generates a UUID for a policy that has no UUID', done => { @@ -607,369 +529,38 @@ describe('Tests app', () => { .expect(201) .end((err, result) => { test.value(result).hasHeader('content-type', APP_JSON) - test.assert(isUUID(result.body.policy_id)) + test.value(result.body.version).is(POLICY_AUTHOR_API_DEFAULT_VERSION) + test.assert(isUUID(result.body.data.policy.policy_id)) done(err) }) }) - }) - describe('Geography endpoint tests', () => { - before(async () => { - await db.initialize() - }) - - after(async () => { - await db.shutdown() - }) - - it('cannot POST one current geography (no auth)', done => { - const geography = { geography_id: GEOGRAPHY_UUID, geography_json: LA_CITY_BOUNDARY } + it('Cannot PUT a policy with publish_date set', done => { request - .post(`/geographies`) - .set('Authorization', EMPTY_SCOPE) - .send(geography) - .expect(403) - .end(err => { - done(err) - }) - }) - - it('cannot POST one current geography (wrong auth)', done => { - const geography = { geography_id: GEOGRAPHY_UUID, geography_json: LA_CITY_BOUNDARY } - request - .post(`/geographies`) - .set('Authorization', EVENTS_READ_SCOPE) - .send(geography) - .expect(403) - .end(err => { - done(err) - }) - }) - - it('creates one current geography', done => { - const geography = { name: 'LA', geography_id: GEOGRAPHY_UUID, geography_json: LA_CITY_BOUNDARY } - request - .post(`/geographies`) + .put(`/policies/${PUBLISHED_POLICY.policy_id}`) .set('Authorization', POLICIES_WRITE_SCOPE) - .send(geography) - .expect(201) - .end((err, result) => { - const body = result.body - log('create one geo response:', body) - test.value(result).hasHeader('content-type', APP_JSON) - done(err) - }) - }) - - it('cannot GETs one current geography (no auth)', done => { - request - .get(`/geographies/${GEOGRAPHY_UUID}`) - .set('Authorization', EMPTY_SCOPE) - .expect(403) - .end(err => { - done(err) - }) - }) - - it('cannot GETs one current geography (wrong auth)', done => { - request - .get(`/geographies/${GEOGRAPHY_UUID}`) - .set('Authorization', EVENTS_READ_SCOPE) - .expect(403) - .end(err => { - done(err) - }) - }) - - it('GETs one current geography', done => { - request - .get(`/geographies/${GEOGRAPHY_UUID}`) - .set('Authorization', POLICIES_READ_SCOPE) - .expect(200) - .end((err, result) => { - test.assert(result.body.geography_id === GEOGRAPHY_UUID) - test.value(result).hasHeader('content-type', APP_JSON) - done(err) - }) - }) - - it('cannot GET a nonexistent geography', done => { - request - .get(`/geographies/${POLICY_UUID}`) - .set('Authorization', POLICIES_READ_SCOPE) - .expect(404) - .end(err => { - done(err) - }) - }) - - it('cannot update one geography (no auth)', done => { - const geography = { geography_id: GEOGRAPHY_UUID, geography_json: DISTRICT_SEVEN } - request - .put(`/geographies/${GEOGRAPHY_UUID}`) - .set('Authorization', EMPTY_SCOPE) - .send(geography) - .expect(403) - .end(err => { - done(err) - }) - }) - - it('cannot update one geography (wrong auth)', done => { - const geography = { geography_id: GEOGRAPHY_UUID, geography_json: DISTRICT_SEVEN } - request - .put(`/geographies/${GEOGRAPHY_UUID}`) - .set('Authorization', EVENTS_READ_SCOPE) - .send(geography) - .expect(403) - .end(err => { - done(err) - }) - }) - - it('verifies updating one geography', done => { - const geography = { name: 'LA', geography_id: GEOGRAPHY_UUID, geography_json: DISTRICT_SEVEN } - request - .put(`/geographies/${GEOGRAPHY_UUID}`) - .set('Authorization', POLICIES_WRITE_SCOPE) - .send(geography) - .expect(201) - .end((err, result) => { - test.value(result).hasHeader('content-type', APP_JSON) - done(err) - }) - }) - - it('cannot PUT geography metadata to create (no auth)', async () => { - const metadata = { some_arbitrary_thing: 'boop' } - await request - .put(`/geographies/${GEOGRAPHY_UUID}/meta`) - .set('Authorization', EMPTY_SCOPE) - .send({ geography_id: GEOGRAPHY_UUID, geography_metadata: metadata }) - .expect(403) - }) - - it('cannot PUT geography metadata to create (wrong auth)', async () => { - const metadata = { some_arbitrary_thing: 'boop' } - await request - .put(`/geographies/${GEOGRAPHY_UUID}/meta`) - .set('Authorization', EVENTS_READ_SCOPE) - .send({ geography_id: GEOGRAPHY_UUID, geography_metadata: metadata }) - .expect(403) - }) - - it('verifies PUTing geography metadata to create', async () => { - const metadata = { some_arbitrary_thing: 'boop' } - await request - .put(`/geographies/${GEOGRAPHY_UUID}/meta`) - .set('Authorization', POLICIES_WRITE_SCOPE) - .send({ geography_id: GEOGRAPHY_UUID, geography_metadata: metadata }) - .expect(201) - const result = await db.readSingleGeographyMetadata(GEOGRAPHY_UUID) - test.assert(result.geography_metadata.some_arbitrary_thing === 'boop') - }) - - it('verifies PUTing geography metadata to edit', async () => { - const metadata = { some_arbitrary_thing: 'beep' } - await request - .put(`/geographies/${GEOGRAPHY_UUID}/meta`) - .set('Authorization', POLICIES_WRITE_SCOPE) - .send({ geography_id: GEOGRAPHY_UUID, geography_metadata: metadata }) - .expect(200) - const result = await db.readSingleGeographyMetadata(GEOGRAPHY_UUID) - test.assert(result.geography_metadata.some_arbitrary_thing === 'beep') - }) - - it('cannot GET geographies (no auth)', done => { - request - .get(`/geographies/`) - .set('Authorization', EMPTY_SCOPE) - .expect(403) - .end(err => { - done(err) - }) - }) - - it('cannot GET geographies (wrong auth)', done => { - request - .get(`/geographies/`) - .set('Authorization', EVENTS_READ_SCOPE) - .expect(403) - .end(err => { - done(err) - }) - }) - - it('can GET geographies, full version', done => { - request - .get(`/geographies/`) - .set('Authorization', POLICIES_READ_SCOPE) - .expect(200) - .end((err, result) => { - result.body.forEach((item: Geography) => { - test.assert(item.geography_json) - }) - done(err) - }) - }) - - it('can GET geographies, summarized version', done => { - request - .get(`/geographies?summary=true`) - .set('Authorization', POLICIES_READ_SCOPE) - .expect(200) - .end((err, result) => { - result.body.forEach((item: Geography) => { - test.assert(!item.geography_json) - }) - done(err) - }) - }) - - it('cannot GET geography metadata (no auth)', done => { - request - .get(`/geographies/${GEOGRAPHY_UUID}/meta`) - .set('Authorization', EMPTY_SCOPE) - .expect(403) - .end(err => { - done(err) - }) - }) - - it('cannot GET geography metadata (wrong auth)', done => { - request - .get(`/geographies/${GEOGRAPHY_UUID}/meta`) - .set('Authorization', EVENTS_READ_SCOPE) - .expect(403) - .end(err => { - done(err) - }) - }) - - it('verifies GETing geography metadata', done => { - request - .get(`/geographies/${GEOGRAPHY_UUID}/meta`) - .set('Authorization', POLICIES_READ_SCOPE) - .expect(200) - .end((err, result) => { - test.assert(result.body.geography_metadata.some_arbitrary_thing === 'beep') - test.value(result).hasHeader('content-type', APP_JSON) - done(err) - }) - }) - - it('verifies cannot GET non-existent geography metadata', done => { - request - .get(`/geographies/beepbapboop/meta`) - .set('Authorization', POLICIES_READ_SCOPE) - .expect(404) - .end((err, result) => { - test.assert(result.body.result === 'not found') - test.value(result).hasHeader('content-type', APP_JSON) - done(err) - }) - }) - - it('cannot PUT geography (no auth)', done => { - const geography = { geography_id: GEOGRAPHY_UUID, geography_json: 'garbage_json' } - request - .put(`/geographies/${GEOGRAPHY_UUID}`) - .set('Authorization', EMPTY_SCOPE) - .send(geography) - .expect(403) - .end(err => { - done(err) - }) - }) - - it('cannot PUT geography (wrong auth)', done => { - const geography = { geography_id: GEOGRAPHY_UUID, geography_json: 'garbage_json' } - request - .put(`/geographies/${GEOGRAPHY_UUID}`) - .set('Authorization', EVENTS_READ_SCOPE) - .send(geography) - .expect(403) - .end(err => { - done(err) - }) - }) - - it('verifies cannot PUT bad geography', done => { - const geography = { name: 'LA', geography_id: GEOGRAPHY_UUID, geography_json: 'garbage_json' } - request - .put(`/geographies/${GEOGRAPHY_UUID}`) - .set('Authorization', POLICIES_WRITE_SCOPE) - .send(geography) + .send(PUBLISHED_POLICY) .expect(400) .end((err, result) => { + test.assert(result.body.error.name === `ValidationError`) + test.assert(result.body.error.reason.includes('publish_date')) test.value(result).hasHeader('content-type', APP_JSON) done(err) }) }) - it('verifies cannot PUT non-existent geography', done => { - const geography = { name: 'LA', geography_id: POLICY_UUID, geography_json: DISTRICT_SEVEN } + it('Cannot POST a policy with publish_date set', done => { request - .put(`/geographies/${POLICY_UUID}`) - .set('Authorization', POLICIES_WRITE_SCOPE) - .send(geography) - .expect(404) - .end((err, result) => { - test.value(result).hasHeader('content-type', APP_JSON) - done(err) - }) - }) - - it('verifies cannot POST invalid geography', done => { - const geography = { name: 'LA', geography_id: GEOGRAPHY_UUID, geography_json: 'garbage_json' } - request - .post(`/geographies`) + .post(`/policies`) .set('Authorization', POLICIES_WRITE_SCOPE) - .send(geography) + .send(PUBLISHED_POLICY) .expect(400) .end((err, result) => { + test.assert(result.body.error.name === `ValidationError`) + test.assert(result.body.error.reason.includes('publish_date')) test.value(result).hasHeader('content-type', APP_JSON) done(err) }) }) - - it('cannot POST duplicate geography', done => { - const geography = { name: 'LA', geography_id: GEOGRAPHY_UUID, geography_json: LA_CITY_BOUNDARY } - request - .post(`/geographies`) - .set('Authorization', POLICIES_WRITE_SCOPE) - .send(geography) - .expect(409) - .end((err, result) => { - test.value(result).hasHeader('content-type', APP_JSON) - done(err) - }) - }) - - it('cannot do bulk geography metadata reads (no auth)', async () => { - await request - .get(`/geographies/meta?get_read_only=false`) - .set('Authorization', EMPTY_SCOPE) - .expect(403) - }) - - it('cannot do bulk geography metadata reads (wrong auth)', async () => { - await request - .get(`/geographies/meta?get_read_only=false`) - .set('Authorization', EVENTS_READ_SCOPE) - .expect(403) - }) - - it('can do bulk geography metadata reads', async () => { - await db.writeGeography({ name: 'Geography 2', geography_id: GEOGRAPHY2_UUID, geography_json: DISTRICT_SEVEN }) - await db.writeGeographyMetadata({ geography_id: GEOGRAPHY2_UUID, geography_metadata: { earth: 'isround' } }) - - const result = await request - .get(`/geographies/meta?get_read_only=false`) - .set('Authorization', POLICIES_READ_SCOPE) - .expect(200) - test.assert(result.body.length === 2) - test.value(result).hasHeader('content-type', APP_JSON) - }) }) }) diff --git a/packages/mds-policy-author/tsconfig.build.json b/packages/mds-policy-author/tsconfig.build.json index e70f30f8b..7a9871e24 100644 --- a/packages/mds-policy-author/tsconfig.build.json +++ b/packages/mds-policy-author/tsconfig.build.json @@ -4,11 +4,13 @@ "outDir": "dist" }, "references": [ - { "path": "../../packages/mds-api-server/tsconfig.build.json" }, { "path": "../../packages/mds-api-helpers/tsconfig.build.json" }, + { "path": "../../packages/mds-api-server/tsconfig.build.json" }, { "path": "../../packages/mds-db/tsconfig.build.json" }, { "path": "../../packages/mds-logger/tsconfig.build.json" }, { "path": "../../packages/mds-providers/tsconfig.build.json" }, + { "path": "../../packages/mds-schema-validators/tsconfig.build.json" }, + { "path": "../../packages/mds-types/tsconfig.build.json" }, { "path": "../../packages/mds-utils/tsconfig.build.json" } ] } diff --git a/packages/mds-policy-author/tsconfig.eslint.json b/packages/mds-policy-author/tsconfig.eslint.json new file mode 100644 index 000000000..2a400942a --- /dev/null +++ b/packages/mds-policy-author/tsconfig.eslint.json @@ -0,0 +1 @@ +{ "extends": "../../tsconfig.eslint.json" } diff --git a/packages/mds-policy-author/types.ts b/packages/mds-policy-author/types.ts index 8a279344f..4e242e59e 100644 --- a/packages/mds-policy-author/types.ts +++ b/packages/mds-policy-author/types.ts @@ -1,4 +1,31 @@ -import { ApiRequest, ApiResponse } from '@mds-core/mds-api-server' +import { Policy, UUID, PolicyMetadata } from '@mds-core/mds-types' +import { ApiRequest, ApiVersionedResponse, ApiClaims } from '@mds-core/mds-api-server' -export type PolicyApiRequest = ApiRequest -export type PolicyApiResponse = ApiResponse +export const POLICY_AUTHOR_API_SUPPORTED_VERSIONS = ['0.4.1'] as const +export type POLICY_AUTHOR_API_SUPPORTED_VERSION = typeof POLICY_AUTHOR_API_SUPPORTED_VERSIONS[number] +export const [POLICY_AUTHOR_API_DEFAULT_VERSION] = POLICY_AUTHOR_API_SUPPORTED_VERSIONS + +export type PolicyAuthorApiRequest = ApiRequest + +export type PolicyAuthorApiAccessTokenScopes = + | 'policies:read' + | 'policies:write' + | 'policies:publish' + | 'policies:delete' + +type PolicyAuthorApiResponse = ApiVersionedResponse< + POLICY_AUTHOR_API_SUPPORTED_VERSION, + ApiClaims, + TBody +> + +export type GetPoliciesResponse = PolicyAuthorApiResponse<{ data: { policies: Policy[] } }> +export type GetPolicyResponse = PolicyAuthorApiResponse<{ data: { policy: Policy } }> +export type PostPolicyResponse = PolicyAuthorApiResponse<{ data: { policy: Policy } }> +export type PublishPolicyResponse = PolicyAuthorApiResponse<{ data: { policy: Policy } }> +export type EditPolicyResponse = PolicyAuthorApiResponse<{ data: { policy: Policy } }> +export type DeletePolicyResponse = PolicyAuthorApiResponse<{ data: { policy_id: UUID } }> + +export type GetPolicyMetadatumResponse = PolicyAuthorApiResponse<{ data: { policy_metadata: PolicyMetadata } }> +export type GetPolicyMetadataResponse = PolicyAuthorApiResponse<{ data: { policy_metadata: PolicyMetadata[] } }> +export type EditPolicyMetadataResponse = PolicyAuthorApiResponse<{ data: { policy_metadata: PolicyMetadata } }> diff --git a/packages/mds-policy/README.md b/packages/mds-policy/README.md index 105b2188e..0b0c0296f 100644 --- a/packages/mds-policy/README.md +++ b/packages/mds-policy/README.md @@ -1,2 +1,14 @@ -# mds-policy -Delivery mechanism for MDS Policy documents +# MDS Policy API +Delivery mechanism for [MDS-Policy](https://github.com/openmobilityfoundation/mobility-data-specification/blob/dev/policy/README.md) documents. + +## Package Content +### Stable Content +This package contains an MDS-Polcy 0.4.0 implementation +### Experimental Content +N/A + +## Quick Start and Tests +See [Quick Start](../../README.md#Installation) + +## Build Deployment Package +See [Build](../../README.md#Build) diff --git a/packages/mds-policy/api.ts b/packages/mds-policy/api.ts index d742837e3..1a2d15de0 100644 --- a/packages/mds-policy/api.ts +++ b/packages/mds-policy/api.ts @@ -16,15 +16,17 @@ import express from 'express' // import { isProviderId, providerName } from '@mds-core/mds-providers' -import Joi from '@hapi/joi' -import joiToJsonSchema from 'joi-to-json-schema' -import { Policy, UUID, VEHICLE_TYPES, DAYS_OF_WEEK } from '@mds-core/mds-types' +import { Policy, UUID } from '@mds-core/mds-types' import db from '@mds-core/mds-db' -import { now, pathsFor, NotFoundError, isUUID } from '@mds-core/mds-utils' -import log from '@mds-core/mds-logger' -import { PolicyApiRequest, PolicyApiResponse } from './types' +import { now, pathsFor, NotFoundError, isUUID, BadParamsError, ServerError } from '@mds-core/mds-utils' +import logger from '@mds-core/mds-logger' +import { parseRequest } from '@mds-core/mds-api-helpers' +import { ApiRequest, ApiResponse } from '@mds-core/mds-api-server' +import { PolicyApiRequest, PolicyApiResponse, GetPoliciesResponse, GetPolicyResponse } from './types' +import { PolicyApiVersionMiddleware } from './middleware' function api(app: express.Express): express.Express { + app.use(PolicyApiVersionMiddleware) /** * Policy-specific middleware to extract provider_id into locals, do some logging, etc. */ @@ -38,12 +40,12 @@ function api(app: express.Express): express.Express { // const { provider_id } = res.locals.claims // /* istanbul ignore next */ // if (!provider_id) { - // await log.warn('Missing provider_id in', req.originalUrl) + // logger.warn('Missing provider_id in', req.originalUrl) // return res.status(400).send({ result: 'missing provider_id' }) // } // /* istanbul ignore next */ // if (!isUUID(provider_id)) { - // await log.warn(req.originalUrl, 'bogus provider_id', provider_id) + // logger.warn(req.originalUrl, 'bogus provider_id', provider_id) // return res.status(400).send({ result: `invalid provider_id ${provider_id} is not a UUID` }) // } // if (!isProviderId(provider_id)) { @@ -51,139 +53,115 @@ function api(app: express.Express): express.Express { // result: `invalid provider_id ${provider_id} is not a known provider` // }) // } - // log.info(providerName(provider_id), req.method, req.originalUrl) + // logger.info(providerName(provider_id), req.method, req.originalUrl) } else { - return res.status(401).send('Unauthorized') + return res.status(401).send({ error: 'Unauthorized' }) } } } catch (err) { /* istanbul ignore next */ - await log.error(req.originalUrl, 'request validation fail:', err.stack) + logger.error(req.originalUrl, 'request validation fail:', err.stack) } next() }) - app.get(pathsFor('/policies'), async (req, res) => { - // TODO extract start/end applicability - // TODO filter by start/end applicability - const { start_date = now(), end_date = now() } = req.query - log.info('read /policies', req.query, start_date, end_date) - if (start_date > end_date) { - res.status(400).send({ result: 'start_date after end_date' }) - return - } - try { - const policies = await db.readPolicies({ get_published: true }) - const prev_policies: UUID[] = policies.reduce((prev_policies_acc: UUID[], policy: Policy) => { - if (policy.prev_policies) { - prev_policies_acc.push(...policy.prev_policies) + app.get( + pathsFor('/policies'), + async (req: PolicyApiRequest, res: GetPoliciesResponse, next: express.NextFunction) => { + const { start_date = now(), end_date = now() } = req.query + const { scopes } = res.locals + + try { + /* + If the client is scoped to read unpublished policies, + they are permitted to query for both published and unpublished policies. + Otherwise, they can only read published. + */ + const { get_published = null, get_unpublished = null } = scopes.includes('policies:read') + ? parseRequest(req, { parser: x => (x ? JSON.parse(x) : null) }).query('get_published', 'get_unpublished') + : { get_published: true } + + if (start_date > end_date) { + throw new BadParamsError(`start_date ${start_date} > end_date ${end_date}`) } - return prev_policies_acc - }, []) - const active = policies.filter(p => { - // overlapping segment logic - const p_start_date = p.start_date - const p_end_date = p.end_date || Number.MAX_SAFE_INTEGER - return end_date >= p_start_date && p_end_date >= start_date && !prev_policies.includes(p.policy_id) - }) - res.status(200).send({ policies: active }) - } catch (err) { - res.status(404).send({ - result: 'not found' - }) - } - }) + const policies = await db.readPolicies({ get_published, get_unpublished }) + const prev_policies: UUID[] = policies.reduce((prev_policies_acc: UUID[], policy: Policy) => { + if (policy.prev_policies) { + prev_policies_acc.push(...policy.prev_policies) + } + return prev_policies_acc + }, []) + const active = policies.filter(p => { + // overlapping segment logic + const p_start_date = p.start_date + const p_end_date = p.end_date || Number.MAX_SAFE_INTEGER + return end_date >= p_start_date && p_end_date >= start_date && !prev_policies.includes(p.policy_id) + }) - app.get(pathsFor('/policies/:policy_id'), async (req, res) => { - const { policy_id } = req.params - if (!isUUID(policy_id)) { - res.status(400).send({ error: 'bad_param' }) - } - try { - const policy = await db.readPolicy(policy_id) - res.status(200).send(policy) - } catch (err) { - await log.error('failed to read one policy', err) - if (err instanceof NotFoundError) { - res.status(404).send({ result: 'not found' }) - } else { - res.status(500).send({ result: 'something else went wrong' }) + if (active.length === 0) { + throw new NotFoundError('No policies found!') + } + + res.status(200).send({ version: res.locals.version, data: { policies: active } }) + } catch (error) { + if (error instanceof NotFoundError) { + return res.status(404).send({ error }) + } + if (error instanceof BadParamsError) { + return res.status(400).send({ error }) + } + next(new ServerError(error)) } } - }) + ) - app.get(pathsFor('/geographies/:geography_id'), async (req, res) => { - log.info('read geo', JSON.stringify(req.params)) - const { geography_id } = req.params - if (!isUUID(geography_id)) { - res.status(400).send({ error: 'bad_param' }) - } - log.info('read geo', geography_id) - try { - const geography = await db.readSingleGeography(geography_id) - res.status(200).send({ geography }) - } catch (err) { - res.status(404).send({ result: 'not found' }) - } - }) + app.get( + pathsFor('/policies/:policy_id'), + async (req: PolicyApiRequest, res: GetPolicyResponse, next: express.NextFunction) => { + const { policy_id } = req.params + const { scopes } = res.locals - const ruleSchema = Joi.object().keys({ - name: Joi.string().required(), - rule_id: Joi.string() - .guid() - .required(), - rule_type: Joi.string() - .valid(['count', 'time', 'speed', 'user']) - .required(), - rule_units: Joi.string().valid(['seconds', 'minutes', 'hours', 'mph', 'kph']), - geographies: Joi.array().items(Joi.string().guid()), - statuses: Joi.object() - .keys({ - available: Joi.array(), - reserved: Joi.array(), - unavailable: Joi.array(), - removed: Joi.array(), - inactive: Joi.array(), - trip: Joi.array(), - elsewhere: Joi.array() - }) - .allow(null), - vehicle_types: Joi.array().items(Joi.string().valid(Object.values(VEHICLE_TYPES))), - maximum: Joi.number(), - minimum: Joi.number(), - start_time: Joi.string(), - end_time: Joi.string(), - days: Joi.array().items(Joi.string().valid(Object.values(DAYS_OF_WEEK))), - messages: Joi.object(), - value_url: Joi.string().uri() - }) + try { + if (!isUUID(policy_id)) { + throw new BadParamsError(`policy_id ${policy_id} is not a valid UUID`) + } - const policySchema = Joi.object().keys({ - name: Joi.string().required(), - description: Joi.string().required(), - policy_id: Joi.string() - .guid() - .required(), - start_date: Joi.date() - .timestamp('javascript') - .required(), - end_date: Joi.date() - .timestamp('javascript') - .allow(null), - prev_policies: Joi.array() - .items(Joi.string().guid()) - .allow(null), - provider_ids: Joi.array() - .items(Joi.string().guid()) - .allow(null), - rules: Joi.array() - .min(1) - .items(ruleSchema) - .required() - }) + /* + If the client is scoped to read unpublished policies, + they are permitted to query for both published and unpublished policies. + Otherwise, they can only read published. + */ + const { get_published = null, get_unpublished = null } = scopes.includes('policies:read') + ? parseRequest(req, { parser: x => (x ? JSON.parse(x) : null) }).query('get_published', 'get_unpublished') + : { get_published: true } + + const policies = await db.readPolicies({ policy_id, get_published, get_unpublished }) + + if (policies.length === 0) { + throw new NotFoundError(`policy_id ${policy_id} not found`) + } + + const [policy] = policies + + res.status(200).send({ version: res.locals.version, data: { policy } }) + } catch (error) { + if (error instanceof BadParamsError) { + return res.status(400).send({ error }) + } + if (error instanceof NotFoundError) { + return res.status(404).send({ error }) + } + return next(new ServerError(error)) + } + } + ) - app.get(pathsFor('/schema/policy'), (req, res) => { - res.status(200).send(joiToJsonSchema(policySchema)) + /* eslint-reason global error handling middleware */ + /* istanbul ignore next */ + /* eslint-disable-next-line @typescript-eslint/no-unused-vars */ + app.use(async (error: Error, req: ApiRequest, res: ApiResponse) => { + await logger.error(req.method, req.originalUrl, error) + return res.status(500).send({ error }) }) return app diff --git a/packages/mds-policy/middleware/index.ts b/packages/mds-policy/middleware/index.ts new file mode 100644 index 000000000..4cf756d9f --- /dev/null +++ b/packages/mds-policy/middleware/index.ts @@ -0,0 +1 @@ +export * from './policy-api-version' diff --git a/packages/mds-policy/middleware/policy-api-version.ts b/packages/mds-policy/middleware/policy-api-version.ts new file mode 100644 index 000000000..77adf5e6d --- /dev/null +++ b/packages/mds-policy/middleware/policy-api-version.ts @@ -0,0 +1,7 @@ +import { ApiVersionMiddleware } from '@mds-core/mds-api-server' +import { POLICY_API_SUPPORTED_VERSIONS, POLICY_API_DEFAULT_VERSION } from '../types' + +export const PolicyApiVersionMiddleware = ApiVersionMiddleware( + 'application/vnd.mds.policy+json', + POLICY_API_SUPPORTED_VERSIONS +).withDefaultVersion(POLICY_API_DEFAULT_VERSION) diff --git a/packages/mds-policy/package.json b/packages/mds-policy/package.json index b1f1fcba4..04de274fa 100644 --- a/packages/mds-policy/package.json +++ b/packages/mds-policy/package.json @@ -1,24 +1,20 @@ { "name": "@mds-core/mds-policy", "description": "MDS Policy API", - "version": "0.0.19", + "version": "0.0.27", + "author": "City of Los Angeles", "license": "Apache-2.0", - "engineStrict": true, - "engines": { - "node": ">= 8.0.0", - "npm": ">= 6.8.0" - }, "dependencies": { - "@hapi/joi": "15.1.1", - "@mds-core/mds-api-server": "0.1.18", - "@mds-core/mds-db": "0.1.18", - "@mds-core/mds-logger": "0.1.16", - "@mds-core/mds-providers": "0.1.18", - "@mds-core/mds-types": "0.1.15", - "@mds-core/mds-utils": "0.1.18", - "express": "4.17.1", - "joi-to-json-schema": "5.1.0", - "uuid": "3.3.3" + "@mds-core/mds-api-helpers": "0.1.26", + "@mds-core/mds-api-server": "0.1.26", + "@mds-core/mds-db": "0.1.26", + "@mds-core/mds-logger": "0.1.24", + "@mds-core/mds-providers": "0.1.26", + "@mds-core/mds-schema-validators": "0.1.2", + "@mds-core/mds-types": "0.1.23", + "@mds-core/mds-utils": "0.1.26", + "@types/express": "4.17.6", + "express": "4.17.1" }, "main": "dist/index.js", "types": "dist/index.d.ts", @@ -29,9 +25,9 @@ "build": "tsc --build tsconfig.build.json", "start": "PATH_PREFIX=/policy yarn watch server", "test": "yarn test:eslint && yarn test:unit", - "test:eslint": "eslint --fix --ignore-path ../../.gitignore '**/*.ts'", - "test:unit": "PATH_PREFIX=/policy DOTENV_CONFIG_PATH=../../.env nyc --check-coverage --exclude tests --extension .ts --lines 80 --reporter=text --reporter=html ts-mocha --project ../../tsconfig.json --require tsconfig-paths/register --require source-map-support/register --require dotenv/config --recursive --timeout 5000 tests/**/*.ts", - "watch": "nodemon --watch '../../packages' --ext 'ts' --ignore '*.d.ts' --exec yarn watch:exec --", - "watch:exec": "yarn build && DOTENV_CONFIG_PATH=../../.env ts-node -r dotenv/config" + "test:eslint": "eslint --ignore-path ../../.gitignore '**/*.ts'", + "test:unit": "PATH_PREFIX=/policy DOTENV_CONFIG_PATH=../../.env nyc ts-mocha --project ../../tsconfig.json", + "ts-node": "yarn build && DOTENV_CONFIG_PATH=../../.env ts-node -r dotenv/config", + "watch": "nodemon --watch '../../packages' --ext 'ts' --ignore '*.d.ts' --exec yarn ts-node --" } } diff --git a/packages/mds-policy/server.ts b/packages/mds-policy/server.ts index 3863b10ed..1ea33b032 100644 --- a/packages/mds-policy/server.ts +++ b/packages/mds-policy/server.ts @@ -11,14 +11,7 @@ limitations under the License. */ -// Express local -import { ApiServer } from '@mds-core/mds-api-server' +import { ApiServer, HttpServer } from '@mds-core/mds-api-server' import { api } from './api' -const { - env: { npm_package_name, PORT = 4003 } -} = process - -/* eslint-reason avoids import of logger */ -/* eslint-disable-next-line no-console */ -ApiServer(api).listen(PORT, () => console.log(`${npm_package_name} running on port ${PORT}`)) +HttpServer(ApiServer(api), { port: process.env.POLICY_API_PORT }) diff --git a/packages/mds-policy/tests/api.spec.ts b/packages/mds-policy/tests/api.spec.ts index 3ef2d0b9c..5a303b23a 100644 --- a/packages/mds-policy/tests/api.spec.ts +++ b/packages/mds-policy/tests/api.spec.ts @@ -40,212 +40,152 @@ import { START_ONE_MONTH_AGO, START_ONE_WEEK_AGO, PROVIDER_SCOPES, - GEOGRAPHY2_UUID + GEOGRAPHY2_UUID, + veniceSpecOps, + SCOPED_AUTH } from '@mds-core/mds-test-data' + import { la_city_boundary } from './la-city-boundary' import { api } from '../api' +import { POLICY_API_DEFAULT_VERSION } from '../types' /* eslint-disable-next-line @typescript-eslint/no-var-requires */ -const veniceSpecialOpsZone = require('../../ladot-service-areas/venice-special-ops-zone') /* eslint-disable-next-line no-console */ const log = console.log.bind(console) const request = supertest(ApiServer(api)) -const APP_JSON = 'application/json; charset=utf-8' +const APP_JSON = 'application/vnd.mds.policy+json; charset=utf-8; version=0.4' const AUTH = `basic ${Buffer.from(`${TEST1_PROVIDER_ID}|${PROVIDER_SCOPES}`).toString('base64')}` +const POLICIES_READ_SCOPE = SCOPED_AUTH(['policies:read']) describe('Tests app', () => { before('Initialize the DB', async () => { await db.initialize() + await db.writeGeography({ name: 'Los Angeles', geography_id: GEOGRAPHY_UUID, geography_json: la_city_boundary }) }) after('Shutdown the DB', async () => { await db.shutdown() }) - it('reads the Policy schema', done => { - request - .get('/schema/policy') - .expect(200) - .end((err, result) => { - const body = result.body - log('schema', JSON.stringify(body)) - test.value(result).hasHeader('content-type', APP_JSON) - done(err) - }) - }) - - // MAIN TESTS HERE - - it('read back one geography', async () => { - await db.writeGeography({ name: 'Los Angeles', geography_id: GEOGRAPHY_UUID, geography_json: la_city_boundary }) - request - .get(`/geographies/${GEOGRAPHY_UUID}`) - .set('Authorization', AUTH) - .expect(200) - .end((err, result) => { - const body = result.body - log('read back one geo response:', body) - test.value(result).hasHeader('content-type', APP_JSON) - // TODO verify contents - return err - }) - }) - - it('tries to get policy for invalid dates', done => { - request - .get('/policies?start_date=100000&end_date=100') - .set('Authorization', AUTH) - .expect(400) - .end((err, result) => { - test.value(result.body.result === 'start_date after end_date') - done(err) - }) - }) - - it('tries to read non-existant policy', done => { - request - .get('/policies/notarealgeography') - .set('Authorization', AUTH) - .expect(400) - .end((err, result) => { - test.value(result.body.result === 'not found') - done(err) - }) - }) - - it('tries to read non-existant geography', done => { - request - .get('/geographies/notarealgeography') - .set('Authorization', AUTH) - .expect(400) - .end((err, result) => { - test.value(result.body.result === 'not found') - done(err) - }) + it('tries to get policy for invalid dates', async () => { + const result = await request.get('/policies?start_date=100000&end_date=100').set('Authorization', AUTH).expect(400) + test.value(result.body.result === 'start_date after end_date') }) it('read back one policy', async () => { await db.writePolicy(POLICY_JSON) + await db.publishGeography({ geography_id: GEOGRAPHY_UUID }) await db.publishPolicy(POLICY_UUID) - request - .get(`/policies/${POLICY_UUID}`) - .set('Authorization', AUTH) - .expect(200) - .end((err, result) => { - const body = result.body - log('read back one policy response:', body) - test.value(result).hasHeader('content-type', APP_JSON) - // TODO verify contents - return err - }) + const result = await request.get(`/policies/${POLICY_UUID}`).set('Authorization', AUTH).expect(200) + const body = result.body + log('read back one policy response:', body) + test.value(body.version).is(POLICY_API_DEFAULT_VERSION) + test.value(result).hasHeader('content-type', APP_JSON) + test.value(result.body.data.policy.policies, POLICY_UUID) }) - it('reads back all active policies', done => { - request - .get(`/policies`) - .set('Authorization', AUTH) - .expect(200) - .end((err, result) => { - const body = result.body - log('read back all policies response:', body) - test.value(body.policies.length).is(1) // only one should be currently valid - test.value(body.policies[0].policy_id).is(POLICY_UUID) - test.value(result).hasHeader('content-type', APP_JSON) - // TODO verify contents - done(err) - }) + it('reads back all active policies', async () => { + const result = await request.get(`/policies`).set('Authorization', AUTH).expect(200) + const body = result.body + log('read back all policies response:', body) + test.value(body.data.policies.length).is(1) // only one should be currently valid + test.value(body.data.policies[0].policy_id).is(POLICY_UUID) + test.value(body.version).is(POLICY_API_DEFAULT_VERSION) + test.value(result).hasHeader('content-type', APP_JSON) + test.value(result.body.data.policies, [POLICY_JSON]) }) it('read back all published policies and no superseded ones', async () => { await db.writeGeography({ name: 'Los Angeles', geography_id: GEOGRAPHY2_UUID, - geography_json: veniceSpecialOpsZone + geography_json: veniceSpecOps }) await db.writePolicy(POLICY2_JSON) + await db.publishGeography({ geography_id: GEOGRAPHY_UUID }) + await db.publishGeography({ geography_id: GEOGRAPHY2_UUID }) await db.publishPolicy(POLICY2_JSON.policy_id) await db.writePolicy(POLICY3_JSON) await db.publishPolicy(POLICY3_JSON.policy_id) - await db.writePolicy(POLICY4_JSON) await db.writePolicy(SUPERSEDING_POLICY_JSON) await db.publishPolicy(SUPERSEDING_POLICY_JSON.policy_id) - request + const result = await request .get(`/policies?start_date=${now() - days(365)}&end_date=${now() + days(365)}`) .set('Authorization', AUTH) .expect(200) - .end((err, result) => { - const body = result.body - log('read back all published policies response:', body) - test.value(body.policies.length).is(3) - test.value(result).hasHeader('content-type', APP_JSON) - const isSupersededPolicyPresent = body.policies.some((policy: Policy) => { - return policy.policy_id === POLICY_JSON.policy_id - }) - const isSupersedingPolicyPresent = body.policies.some((policy: Policy) => { - return policy.policy_id === SUPERSEDING_POLICY_JSON.policy_id - }) - test.value(isSupersededPolicyPresent).is(false) - test.value(isSupersedingPolicyPresent).is(true) - return err - }) + const body = result.body + const policies = result.body.data.policies + log('read back all published policies response:', body) + test.value(policies.length).is(3) + test.value(body.version).is(POLICY_API_DEFAULT_VERSION) + test.value(result).hasHeader('content-type', APP_JSON) + const isSupersededPolicyPresent = policies.some((policy: Policy) => { + return policy.policy_id === POLICY_JSON.policy_id + }) + const isSupersedingPolicyPresent = policies.some((policy: Policy) => { + return policy.policy_id === SUPERSEDING_POLICY_JSON.policy_id + }) + test.value(isSupersededPolicyPresent).is(false) + test.value(isSupersedingPolicyPresent).is(true) }) - it('read back an old policy', done => { - request + it('read back an old policy', async () => { + const result = await request .get(`/policies?start_date=${START_ONE_MONTH_AGO}&end_date=${START_ONE_WEEK_AGO}`) .set('Authorization', AUTH) .expect(200) - .end((err, result) => { - const body = result.body - log('read back all policies response:', body) - test.value(body.policies.length).is(1) // only one - test.value(body.policies[0].policy_id).is(POLICY2_UUID) - test.value(result).hasHeader('content-type', APP_JSON) - // TODO verify contents - done(err) - }) + const body = result.body + const policies = body.data.policies + log('read back all policies response:', body) + test.value(policies.length).is(1) // only one + test.value(policies[0].policy_id).is(POLICY2_UUID) + test.value(body.version).is(POLICY_API_DEFAULT_VERSION) + test.value(result).hasHeader('content-type', APP_JSON) }) - it('read back current and future policies', done => { - request + it('read back current and future policies', async () => { + const result = await request .get(`/policies?end_date=${now() + days(365)}`) .set('Authorization', AUTH) .expect(200) - .end((err, result) => { - const body = result.body - log('read back all policies response:', body) - test.value(body.policies.length).is(2) // current and future - test.value(result).hasHeader('content-type', APP_JSON) - done(err) - }) + const body = result.body + log('read back all policies response:', body) + test.value(body.data.policies.length).is(2) // current and future + test.value(body.version).is(POLICY_API_DEFAULT_VERSION) + test.value(result).hasHeader('content-type', APP_JSON) }) - it('cannot GET a nonexistant policy', done => { - request + it('cannot GET a nonexistant policy', async () => { + const result = await request .get(`/policies/${GEOGRAPHY_UUID}`) // obvs not a policy .set('Authorization', AUTH) .expect(404) - .end((err, result) => { - const body = result.body - log('read back nonexistant policy response:', body) - test.value(result).hasHeader('content-type', APP_JSON) - done(err) - }) + const body = result.body + log('read back nonexistant policy response:', body) + test.value(result).hasHeader('content-type', APP_JSON) }) - it('read back a nonexistant geography', done => { - request - .get(`/geographies/${POLICY_UUID}`) // obvs not a geography - .set('Authorization', AUTH) - .expect(404) - .end((err, result) => { - const body = result.body - log('read back nonexistant geography response:', body) - test.value(result).hasHeader('content-type', APP_JSON) - done(err) - }) + it('tries to read non-UUID policy', async () => { + const result = await request.get('/policies/notarealpolicy').set('Authorization', AUTH).expect(400) + test.value(result.body.result === 'not found') + }) + + it('can GET all unpublished policies', async () => { + await db.writePolicy(POLICY4_JSON) + const result = await request + .get(`/policies?get_unpublished=true`) + .set('Authorization', POLICIES_READ_SCOPE) + .expect(200) + test.assert(result.body.data.policies.length === 1) + }) + + it('can GET one unpublished policy', async () => { + await request + .get(`/policies/${POLICY4_JSON.policy_id}?get_unpublished=true`) + .set('Authorization', POLICIES_READ_SCOPE) + .expect(200) }) }) diff --git a/packages/mds-policy/tsconfig.build.json b/packages/mds-policy/tsconfig.build.json index 756de142f..842aa7987 100644 --- a/packages/mds-policy/tsconfig.build.json +++ b/packages/mds-policy/tsconfig.build.json @@ -8,6 +8,7 @@ { "path": "../../packages/mds-db/tsconfig.build.json" }, { "path": "../../packages/mds-logger/tsconfig.build.json" }, { "path": "../../packages/mds-providers/tsconfig.build.json" }, + { "path": "../../packages/mds-schema-validators/tsconfig.build.json" }, { "path": "../../packages/mds-types/tsconfig.build.json" }, { "path": "../../packages/mds-utils/tsconfig.build.json" } ] diff --git a/packages/mds-policy/tsconfig.eslint.json b/packages/mds-policy/tsconfig.eslint.json new file mode 100644 index 000000000..2a400942a --- /dev/null +++ b/packages/mds-policy/tsconfig.eslint.json @@ -0,0 +1 @@ +{ "extends": "../../tsconfig.eslint.json" } diff --git a/packages/mds-policy/types.ts b/packages/mds-policy/types.ts index 8a279344f..668d21dcf 100644 --- a/packages/mds-policy/types.ts +++ b/packages/mds-policy/types.ts @@ -1,4 +1,35 @@ -import { ApiRequest, ApiResponse } from '@mds-core/mds-api-server' +/* + Copyright 2019 City of Los Angeles. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +import { ApiRequest, ApiVersionedResponse, ApiClaims } from '@mds-core/mds-api-server' +import { Policy } from '@mds-core/mds-types' + +export const POLICY_API_SUPPORTED_VERSIONS = ['0.4.1'] as const +export type POLICY_API_SUPPORTED_VERSION = typeof POLICY_API_SUPPORTED_VERSIONS[number] +export const [POLICY_API_DEFAULT_VERSION] = POLICY_API_SUPPORTED_VERSIONS export type PolicyApiRequest = ApiRequest -export type PolicyApiResponse = ApiResponse + +export type PolicyApiAccessTokenScopes = 'policies:read' + +export type PolicyApiResponse = ApiVersionedResponse< + POLICY_API_SUPPORTED_VERSION, + ApiClaims, + TBody +> + +export type GetPolicyResponse = PolicyApiResponse<{ data: { policy: Policy } }> +export type GetPoliciesResponse = PolicyApiResponse<{ data: { policies: Policy[] } }> diff --git a/packages/mds-providers/package.json b/packages/mds-providers/package.json index 47c5f99ca..665141b28 100644 --- a/packages/mds-providers/package.json +++ b/packages/mds-providers/package.json @@ -1,6 +1,6 @@ { "name": "@mds-core/mds-providers", - "version": "0.1.18", + "version": "0.1.26", "description": "Mobility Data Specification provider details", "main": "dist/index.js", "types": "dist/index.d.ts", @@ -16,10 +16,10 @@ "scripts": { "build": "tsc --build tsconfig.build.json", "test": "yarn test:eslint && yarn test:unit", - "test:eslint": "eslint --fix --ignore-path ../../.gitignore '**/*.ts'", + "test:eslint": "eslint --ignore-path ../../.gitignore '**/*.ts'", "test:unit": "exit 0" }, "dependencies": { - "@mds-core/mds-types": "0.1.15" + "@mds-core/mds-types": "0.1.23" } } diff --git a/packages/mds-providers/tsconfig.eslint.json b/packages/mds-providers/tsconfig.eslint.json new file mode 100644 index 000000000..2a400942a --- /dev/null +++ b/packages/mds-providers/tsconfig.eslint.json @@ -0,0 +1 @@ +{ "extends": "../../tsconfig.eslint.json" } diff --git a/packages/mds-repository/@types/index.ts b/packages/mds-repository/@types/index.ts new file mode 100644 index 000000000..abb1ccd30 --- /dev/null +++ b/packages/mds-repository/@types/index.ts @@ -0,0 +1,28 @@ +/* + Copyright 2019-2020 City of Los Angeles. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +import { InsertResult, UpdateResult } from 'typeorm' +import { AnyFunction } from '@mds-core/mds-types' + +export interface InsertReturning extends InsertResult { + raw: T[] +} + +export interface UpdateReturning extends UpdateResult { + raw: T[] +} + +export type Mixin = InstanceType> diff --git a/packages/mds-repository/connection.ts b/packages/mds-repository/connection.ts new file mode 100644 index 000000000..f09f2d946 --- /dev/null +++ b/packages/mds-repository/connection.ts @@ -0,0 +1,129 @@ +/* + Copyright 2019-2020 City of Los Angeles. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +import { Connection, ConnectionOptions, createConnection } from 'typeorm' +import { types as PostgresTypes } from 'pg' +import { LoggerOptions } from 'typeorm/logger/LoggerOptions' +import { PostgresConnectionOptions } from 'typeorm/driver/postgres/PostgresConnectionOptions' +import { Nullable, UUID } from '@mds-core/mds-types' +import { uuid } from '@mds-core/mds-utils' +import logger from '@mds-core/mds-logger' +import AwaitLock from 'await-lock' +import { MdsNamingStrategy } from './naming-strategies' +import { RepositoryError } from './exceptions' + +const loggingOption = (options: string): LoggerOptions => { + return ['false', 'true', 'all'].includes(options) ? options !== 'false' : (options.split(' ') as LoggerOptions) +} + +const ConnectionModes = ['ro', 'rw'] as const +export type ConnectionMode = typeof ConnectionModes[number] + +// Use parseInt for bigint columns so the values get returned as numbers instead of strings +PostgresTypes.setTypeParser(20, Number) + +export type ConnectionManagerOptions = Partial> +export type ConnectionManagerCliOptions = Partial + +export class ConnectionManager { + private readonly connections: Map> = new Map() + + private readonly lock = new AwaitLock() + + private readonly instance: UUID = uuid() + + private connectionName = (mode: TConnectionMode): string => { + const { prefix, instance } = this + return `${prefix}-${mode}-${instance}` + } + + private connectionMode = (mode: TConnectionMode): string => (mode === 'ro' ? 'R/O' : 'R/W') + + private connectionOptions = (mode: TConnectionMode): ConnectionOptions => { + const { connectionName, options } = this + const { PG_HOST, PG_HOST_READER, PG_PORT, PG_USER, PG_PASS, PG_NAME, PG_DEBUG = 'false' } = process.env + + return { + name: connectionName(mode), + type: 'postgres', + host: (mode === 'rw' ? PG_HOST : PG_HOST_READER) || PG_HOST || 'localhost', + port: Number(PG_PORT) || 5432, + username: PG_USER, + password: PG_PASS, + database: PG_NAME, + logging: loggingOption(PG_DEBUG.toLowerCase()), + maxQueryExecutionTime: 3000, + logger: 'simple-console', + synchronize: false, + migrationsRun: false, + namingStrategy: new MdsNamingStrategy(), + ...options + } + } + + public connect = async (mode: TConnectionMode): Promise => { + const { lock, connections, connectionOptions, connectionMode, connectionName } = this + await lock.acquireAsync() + try { + if (!connections.has(mode)) { + const options = connectionOptions(mode) + logger.info(`Initializing ${connectionMode(mode)} connection: ${options.name}`) + connections.set(mode, await createConnection(options)) + } + } finally { + lock.release() + } + const connection = connections.get(mode) + if (!connection) { + /* istanbul ignore next */ + throw RepositoryError(Error(`Connection ${connectionName(mode)} not found`)) + } + if (!connection.isConnected) { + try { + await connection.connect() + } catch (error) /* istanbul ignore next */ { + throw RepositoryError(error) + } + } + return connection + } + + public disconnect = async (mode: TConnectionMode) => { + const { lock, connections, connectionMode } = this + try { + await lock.acquireAsync() + const connection = connections.get(mode) + if (connection) { + if (connection.isConnected) { + logger.info(`Terminating ${connectionMode(mode)} connection: ${connection.options.name}`) + await connection.close() + } + connections.delete(mode) + } + } finally { + lock.release() + } + } + + public cli = (mode: TConnectionMode, options: ConnectionManagerCliOptions = {}) => { + const { connectionOptions } = this + // Make the "rw" connection the default for the TypeORM CLI by removing the connection name + const { name, ...ormconfig } = connectionOptions(mode) + return { ...ormconfig, cli: options } + } + + constructor(private prefix: string, private options: ConnectionManagerOptions = {}) {} +} diff --git a/packages/mds-repository/exceptions.ts b/packages/mds-repository/exceptions.ts new file mode 100644 index 000000000..6d8c9df28 --- /dev/null +++ b/packages/mds-repository/exceptions.ts @@ -0,0 +1,36 @@ +/* + Copyright 2019-2020 City of Los Angeles. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +import { ConflictError, ServerError } from '@mds-core/mds-utils' + +const isError = (error: unknown): error is Error => error instanceof Error + +const hasProperty = (property: T, error: unknown): error is { [P in T]: string } => + isError(error) && property in error + +const getProperty = (property: T, error: unknown): string | undefined => + hasProperty(property, error) ? error[property] : undefined + +export const RepositoryError = (error: unknown) => { + if (isError(error)) { + const code = getProperty('code', error) + if (code === '23505') { + return new ConflictError(getProperty('detail', error), error) + } + return error + } + return new ServerError('Unexpected Server Error', error) +} diff --git a/packages/mds-repository/filters.ts b/packages/mds-repository/filters.ts new file mode 100644 index 000000000..442878360 --- /dev/null +++ b/packages/mds-repository/filters.ts @@ -0,0 +1,25 @@ +import { FindOperator, In, Equal, IsNull } from 'typeorm' + +export const PropertyValue = ( + property: TProperty, + value: unknown +): Partial<{ [P in TProperty]: FindOperator }> => { + return { [property]: Array.isArray(value) ? In(value) : Equal(value) } as Partial< + { [P in TProperty]: FindOperator } + > +} + +export const NullablePropertyValue = ( + property: TProperty, + value: unknown +): Partial<{ [P in TProperty]: FindOperator }> => { + return value === null + ? ({ [property]: IsNull() } as Partial<{ [P in TProperty]: FindOperator }>) + : PropertyValue(property, value) +} + +export const OptionalPropertyValue = ( + filter: (property: TProperty, value: unknown) => Partial<{ [P in TProperty]: FindOperator }>, + property: TProperty, + value: unknown +): Partial<{ [P in TProperty]: FindOperator }> => (value === undefined ? {} : filter(property, value)) diff --git a/packages/mds-repository/index.ts b/packages/mds-repository/index.ts new file mode 100644 index 000000000..7800c4ba9 --- /dev/null +++ b/packages/mds-repository/index.ts @@ -0,0 +1,23 @@ +/* + Copyright 2019-2020 City of Los Angeles. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +export * from './exceptions' +export * from './filters' +export * from './mapper' +export * from './mixins' +export * from './repository' +export * from './transformers' +export * from './@types' diff --git a/packages/mds-repository/mapper.ts b/packages/mds-repository/mapper.ts new file mode 100644 index 000000000..d337ee429 --- /dev/null +++ b/packages/mds-repository/mapper.ts @@ -0,0 +1,25 @@ +/* + Copyright 2019-2020 City of Los Angeles. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +export const ModelMapper = >( + map: (model: From, options?: Options) => To +): { + map: (model: From, options?: Options) => To + mapper: (options?: Options) => (model: From) => To +} => ({ + map, + mapper: options => model => map(model, options) +}) diff --git a/packages/mds-repository/migration.ts b/packages/mds-repository/migration.ts new file mode 100644 index 000000000..79774bebd --- /dev/null +++ b/packages/mds-repository/migration.ts @@ -0,0 +1,39 @@ +/* + Copyright 2019-2020 City of Los Angeles. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +import { MigrationInterface, QueryRunner } from 'typeorm' +import { MdsNamingStrategy } from './naming-strategies' + +const strategy = new MdsNamingStrategy() + +export const CreateRepositoryMigration = (migrationTableName: string) => { + const migrationIndexName = strategy.indexName(migrationTableName, ['name']) + + return class CreateRepository0000000000001 implements MigrationInterface { + name = 'CreateRepository0000000000001' + + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query( + `CREATE UNIQUE INDEX "${migrationIndexName}" ON "${migrationTableName}" ("name") `, + undefined + ) + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query(`DROP INDEX "${migrationIndexName}"`, undefined) + } + } +} diff --git a/packages/mds-repository/mixins/identity-column.ts b/packages/mds-repository/mixins/identity-column.ts new file mode 100644 index 000000000..4c5014aa6 --- /dev/null +++ b/packages/mds-repository/mixins/identity-column.ts @@ -0,0 +1,36 @@ +/* + Copyright 2019-2020 City of Los Angeles. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +import { Column, Index } from 'typeorm' +import { ColumnCommonOptions } from 'typeorm/decorator/options/ColumnCommonOptions' +import { ColumnWithWidthOptions } from 'typeorm/decorator/options/ColumnWithWidthOptions' +import { AnyConstructor } from '@mds-core/mds-types' +import { BigintTransformer } from '../transformers' +import { Mixin } from '../@types' + +export const IdentityColumn = ( + EntityClass: T, + options: ColumnWithWidthOptions & ColumnCommonOptions = {} +) => { + abstract class IdentityColumnMixin extends EntityClass { + @Column('bigint', { generated: 'increment', transformer: BigintTransformer, ...options }) + @Index({ unique: true }) + id: number + } + return IdentityColumnMixin +} + +export type IdentityColumn = Mixin diff --git a/packages/mds-repository/mixins/index.ts b/packages/mds-repository/mixins/index.ts new file mode 100644 index 000000000..2d42ca6f4 --- /dev/null +++ b/packages/mds-repository/mixins/index.ts @@ -0,0 +1,18 @@ +/* + Copyright 2019-2020 City of Los Angeles. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +export * from './identity-column' +export * from './recorded-column' diff --git a/packages/mds-repository/mixins/recorded-column.ts b/packages/mds-repository/mixins/recorded-column.ts new file mode 100644 index 000000000..d4c8dabb3 --- /dev/null +++ b/packages/mds-repository/mixins/recorded-column.ts @@ -0,0 +1,40 @@ +/* + Copyright 2019-2020 City of Los Angeles. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +import { Column, Index } from 'typeorm' +import { ColumnCommonOptions } from 'typeorm/decorator/options/ColumnCommonOptions' +import { ColumnWithWidthOptions } from 'typeorm/decorator/options/ColumnWithWidthOptions' +import { Timestamp, AnyConstructor } from '@mds-core/mds-types' +import { BigintTransformer } from '../transformers' +import { Mixin } from '../@types' + +export const RecordedColumn = ( + EntityClass: T, + options: ColumnWithWidthOptions & ColumnCommonOptions = {} +) => { + abstract class RecordedColumnMixin extends EntityClass { + @Column('bigint', { + transformer: BigintTransformer, + default: () => '(extract(epoch from now()) * 1000)::bigint', + ...options + }) + @Index() + recorded: Timestamp + } + return RecordedColumnMixin +} + +export type RecordedColumn = Mixin diff --git a/packages/mds-repository/naming-strategies/index.ts b/packages/mds-repository/naming-strategies/index.ts new file mode 100644 index 000000000..e101ca94b --- /dev/null +++ b/packages/mds-repository/naming-strategies/index.ts @@ -0,0 +1,17 @@ +/* + Copyright 2019-2020 City of Los Angeles. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +export { MdsNamingStrategy } from './mds-naming-strategy' diff --git a/packages/mds-repository/naming-strategies/mds-naming-strategy.ts b/packages/mds-repository/naming-strategies/mds-naming-strategy.ts new file mode 100644 index 000000000..6cab246de --- /dev/null +++ b/packages/mds-repository/naming-strategies/mds-naming-strategy.ts @@ -0,0 +1,34 @@ +/* + Copyright 2019-2020 City of Los Angeles. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +import { NamingStrategyInterface, DefaultNamingStrategy, Table } from 'typeorm' + +const tableName = (tableOrName: Table | string) => + (typeof tableOrName === 'string' ? tableOrName : tableOrName.name).replace(/-/g, '_').replace(/ /g, '') + +export class MdsNamingStrategy extends DefaultNamingStrategy implements NamingStrategyInterface { + primaryKeyName(tableOrName: Table | string, columnNames: string[]): string { + return [tableName(tableOrName), 'pkey'].join('_') + } + + indexName(tableOrName: Table | string, columnNames: string[], where?: string): string { + return ['idx', ...columnNames, tableName(tableOrName)].join('_') + } + + uniqueConstraintName(tableOrName: Table | string, columnNames: string[]): string { + return ['uc', ...columnNames, tableName(tableOrName)].join('_') + } +} diff --git a/packages/mds-repository/package.json b/packages/mds-repository/package.json new file mode 100644 index 000000000..dcc69f120 --- /dev/null +++ b/packages/mds-repository/package.json @@ -0,0 +1,32 @@ +{ + "name": "@mds-core/mds-repository", + "version": "0.1.0", + "description": "Mobility Data Specification ORM", + "main": "dist/index.js", + "types": "dist/index.d.ts", + "files": [ + "dist/" + ], + "keywords": [ + "mds", + "database" + ], + "scripts": { + "build": "tsc --build tsconfig.build.json", + "test": "yarn test:eslint && yarn test:unit", + "test:eslint": "eslint --ignore-path ../../.gitignore '**/*.ts'", + "test:unit": "DOTENV_CONFIG_PATH=../../.env nyc ts-mocha --project ../../tsconfig.json" + }, + "author": "City of Los Angeles", + "license": "Apache-2.0", + "dependencies": { + "@mds-core/mds-logger": "0.1.24", + "@mds-core/mds-providers": "0.1.26", + "@mds-core/mds-types": "0.1.23", + "@mds-core/mds-utils": "0.1.26", + "@types/pg": "7.14.3", + "await-lock": "2.0.1", + "pg": "8.2.0", + "typeorm": "0.2.24" + } +} diff --git a/packages/mds-repository/repository.ts b/packages/mds-repository/repository.ts new file mode 100644 index 000000000..7b7b21fac --- /dev/null +++ b/packages/mds-repository/repository.ts @@ -0,0 +1,126 @@ +/* + Copyright 2019-2020 City of Los Angeles. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +import { Connection } from 'typeorm' +import logger from '@mds-core/mds-logger' +import { pluralize } from '@mds-core/mds-utils' +import { ConnectionManager, ConnectionManagerOptions, ConnectionMode, ConnectionManagerCliOptions } from './connection' +import { CreateRepositoryMigration } from './migration' + +export type RepositoryOptions = Pick + +abstract class BaseRepository { + protected readonly manager: ConnectionManager + + protected abstract initialize(): Promise + + protected connect = async (mode: TConnectionMode): Promise> => { + const { connect } = this.manager + const connection = await connect(mode) + return connection + } + + protected abstract shutdown(): Promise + + constructor(public readonly name: string, { entities, migrations }: Required) { + const migrationsTableName = `${name}-migrations` + this.manager = new ConnectionManager(name, { + migrationsTableName, + entities, + migrations: migrations.length === 0 ? [] : [CreateRepositoryMigration(migrationsTableName), ...migrations] + }) + } +} + +export abstract class ReadOnlyRepository extends BaseRepository<'ro'> { + public initialize = async (): Promise => { + const { name } = this + logger.info(`Initializing R/O repository: ${name}`) + } + + public shutdown = async (): Promise => { + const { + name, + manager: { disconnect } + } = this + logger.info(`Terminating R/O repository: ${name}`) + await disconnect('ro') + } + + public cli = () => { + const { cli } = this.manager + return cli('ro') + } + + constructor(name: string, { entities = [] }: Omit = {}) { + super(name, { entities, migrations: [] }) + } +} + +export abstract class ReadWriteRepository extends BaseRepository<'ro' | 'rw'> { + public initialize = async (): Promise => { + const { + name, + manager: { connect } + } = this + logger.info(`Initializing R/W repository: ${name}`) + + const { + PG_MIGRATIONS = 'true' // Enable migrations by default + } = process.env + + /* istanbul ignore if */ + if (PG_MIGRATIONS === 'true') { + const connection = await connect('rw') + const { + options: { migrationsTableName } + } = connection + if (migrationsTableName) { + const migrations = await connection.runMigrations({ transaction: 'all' }) + logger.info( + `Ran ${migrations.length || 'no'} ${pluralize( + migrations.length, + 'migration', + 'migrations' + )} (${migrationsTableName})${ + migrations.length ? `: ${migrations.map(migration => migration.name).join(', ')}` : '' + }` + ) + } + } + } + + public shutdown = async (): Promise => { + const { + name, + manager: { disconnect } + } = this + logger.info(`Terminating R/W repository: ${name}`) + await Promise.all([disconnect('rw'), disconnect('ro')]) + } + + public cli = (options?: ConnectionManagerCliOptions) => { + const { cli } = this.manager + return cli('rw', options) + } + + constructor(name: string, { entities = [], migrations = [] }: RepositoryOptions = {}) { + super(name, { + entities, + migrations + }) + } +} diff --git a/packages/mds-repository/tests/connection.spec.ts b/packages/mds-repository/tests/connection.spec.ts new file mode 100644 index 000000000..b7b2e6aca --- /dev/null +++ b/packages/mds-repository/tests/connection.spec.ts @@ -0,0 +1,40 @@ +/* + Copyright 2019-2020 City of Los Angeles. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +import test from 'unit.js' +import { ConnectionManager } from '../connection' + +const TEST_REPOSITORY_NAME = 'test-repository' + +const manager = new ConnectionManager(TEST_REPOSITORY_NAME) + +describe('Test Connections', () => { + it('Create R/W Connection', async () => { + const rw = await manager.connect('rw') + test.value(rw.name).startsWith(`${TEST_REPOSITORY_NAME}-rw`) + test.value(rw.isConnected).is(true) + await manager.disconnect('rw') + test.value(rw.isConnected).is(false) + }) + + it('Create R/O Connection', async () => { + const ro = await manager.connect('ro') + test.value(ro.name).startsWith(`${TEST_REPOSITORY_NAME}-ro`) + test.value(ro.isConnected).is(true) + await manager.disconnect('ro') + test.value(ro.isConnected).is(false) + }) +}) diff --git a/packages/mds-repository/tests/exceptions.spec.ts b/packages/mds-repository/tests/exceptions.spec.ts new file mode 100644 index 000000000..f205362fa --- /dev/null +++ b/packages/mds-repository/tests/exceptions.spec.ts @@ -0,0 +1,26 @@ +/* + Copyright 2019-2020 City of Los Angeles. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +import test from 'unit.js' +import { RepositoryError } from '../exceptions' + +describe('Test Exceptions', () => { + it('RepositoryError', done => { + const error = RepositoryError(Error('Some Caught Error')) + test.value(error instanceof Error).is(true) + done() + }) +}) diff --git a/packages/mds-repository/tests/filters.spec.ts b/packages/mds-repository/tests/filters.spec.ts new file mode 100644 index 000000000..75e2aab94 --- /dev/null +++ b/packages/mds-repository/tests/filters.spec.ts @@ -0,0 +1,59 @@ +/* + Copyright 2019-2020 City of Los Angeles. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +import test from 'unit.js' +import { OptionalPropertyValue, NullablePropertyValue, PropertyValue } from '../filters' + +describe('Test Filters', () => { + it('Test Scalar PropertyValue', async () => { + const filter = PropertyValue('property', 'scalar') + test.object(filter).hasProperty('property') + test.object(filter.property).hasProperty('_type', 'equal') + test.object(filter.property).hasProperty('value', 'scalar') + }) + + it('Test Array PropertyValue', async () => { + const filter = PropertyValue('property', ['array']) + test.object(filter).hasProperty('property') + test.object(filter.property).hasProperty('_type', 'in') + test.object(filter.property).hasProperty('value') + test.value(filter.property?.value).is(['array']) + }) + + it('Test NullablePropertyValue (null)', async () => { + const filter = NullablePropertyValue('property', null) + test.object(filter).hasProperty('property') + test.object(filter.property).hasProperty('_type', 'isNull') + }) + + it('Test NullablePropertyValue (value)', async () => { + const filter = NullablePropertyValue('property', 'scalar') + test.object(filter).hasProperty('property') + test.object(filter.property).hasProperty('_type', 'equal') + test.object(filter.property).hasProperty('value', 'scalar') + }) + + it('Test OptionalPropertyValue (undefined)', async () => { + const filter = OptionalPropertyValue(NullablePropertyValue, 'property', undefined) + test.value(filter).is({}) + }) + + it('Test OptionalPropertyValue (null)', async () => { + const filter = OptionalPropertyValue(NullablePropertyValue, 'property', null) + test.object(filter).hasProperty('property') + test.object(filter.property).hasProperty('_type', 'isNull') + }) +}) diff --git a/packages/mds-repository/tests/mapper.spec.ts b/packages/mds-repository/tests/mapper.spec.ts new file mode 100644 index 000000000..ec2708ceb --- /dev/null +++ b/packages/mds-repository/tests/mapper.spec.ts @@ -0,0 +1,37 @@ +/* + Copyright 2019-2020 City of Los Angeles. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +import test from 'unit.js' +import { ModelMapper } from '../mapper' + +const stringify = ModelMapper>((from, options) => { + const { multiplier = 1 } = options ?? {} + return `${from * multiplier}` +}) + +describe('Test CreateModelMapper', () => { + it('Without Options', done => { + test.value(stringify.map(1)).is('1') + test.value([1, 2].map(stringify.mapper())).is(['1', '2']) + done() + }) + + it('With Options', done => { + test.value(stringify.map(1, { multiplier: 2 })).is('2') + test.value([1, 2].map(stringify.mapper({ multiplier: 2 }))).is(['2', '4']) + done() + }) +}) diff --git a/packages/mds-repository/tests/naming-strategies.spec.ts b/packages/mds-repository/tests/naming-strategies.spec.ts new file mode 100644 index 000000000..15434eb36 --- /dev/null +++ b/packages/mds-repository/tests/naming-strategies.spec.ts @@ -0,0 +1,37 @@ +/* + Copyright 2019-2020 City of Los Angeles. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +import test from 'unit.js' +import { MdsNamingStrategy } from '../naming-strategies' + +const strategy = new MdsNamingStrategy() + +describe('Test Naming Strategy', () => { + it('Primary Key Naming Strategy', done => { + test.value(strategy.primaryKeyName('table', ['column'])).is('table_pkey') + done() + }) + + it('Index Naming Strategy', done => { + test.value(strategy.indexName('table', ['column'])).is('idx_column_table') + done() + }) + + it('Unique Constraint Naming Strategy', done => { + test.value(strategy.uniqueConstraintName('table', ['column'])).is('uc_column_table') + done() + }) +}) diff --git a/container-images/mds-agency/index.ts b/packages/mds-repository/tests/transformers.spec.ts similarity index 51% rename from container-images/mds-agency/index.ts rename to packages/mds-repository/tests/transformers.spec.ts index 2536f9cbf..7b23c0617 100644 --- a/container-images/mds-agency/index.ts +++ b/packages/mds-repository/tests/transformers.spec.ts @@ -1,5 +1,5 @@ /* - Copyright 2019 City of Los Angeles. + Copyright 2019-2020 City of Los Angeles. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -14,15 +14,17 @@ limitations under the License. */ -// Express local -import { ApiServer } from '@mds-core/mds-api-server' -import { api } from '@mds-core/mds-agency' -import { env } from '@container-images/env-inject' +import test from 'unit.js' +import { BigintTransformer } from '../transformers' -const { npm_package_name, npm_package_version, npm_package_git_commit, PORT = 4001 } = env() - -ApiServer(api).listen(PORT, () => - /* eslint-reason avoids import of logger */ - /* eslint-disable-next-line no-console */ - console.log(`${npm_package_name} v${npm_package_version} (${npm_package_git_commit}) running on port ${PORT}`) -) +describe('Test Transformers', () => { + it('BigIntTransformer', done => { + test.value(BigintTransformer.to(1)).is(1) + test.value(BigintTransformer.to(null)).is(null) + test.value(BigintTransformer.to([1, null])).is([1, null]) + test.value(BigintTransformer.from('1')).is(1) + test.value(BigintTransformer.from(null)).is(null) + test.value(BigintTransformer.from(['1', null])).is([1, null]) + done() + }) +}) diff --git a/packages/mds-repository/transformers/bigint-transformer.ts b/packages/mds-repository/transformers/bigint-transformer.ts new file mode 100644 index 000000000..1ca5a3835 --- /dev/null +++ b/packages/mds-repository/transformers/bigint-transformer.ts @@ -0,0 +1,23 @@ +/* + Copyright 2019-2020 City of Los Angeles. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +const Bigint = (value: string | null): number | null => (value === null ? value : Number(value)) + +// Use Number for bigint columns so the values get returned as numbers instead of strings +export const BigintTransformer = { + to: (to: number | null | (number | null)[]) => to, + from: (from: string | null | (string | null)[]) => (Array.isArray(from) ? from.map(Bigint) : Bigint(from)) +} diff --git a/packages/mds-repository/transformers/index.ts b/packages/mds-repository/transformers/index.ts new file mode 100644 index 000000000..682e2eeda --- /dev/null +++ b/packages/mds-repository/transformers/index.ts @@ -0,0 +1,17 @@ +/* + Copyright 2019-2020 City of Los Angeles. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +export { BigintTransformer } from './bigint-transformer' diff --git a/packages/mds-repository/tsconfig.build.json b/packages/mds-repository/tsconfig.build.json new file mode 100644 index 000000000..24d28c8e7 --- /dev/null +++ b/packages/mds-repository/tsconfig.build.json @@ -0,0 +1,13 @@ +{ + "extends": "../../tsconfig.build.json", + "compilerOptions": { + "outDir": "dist" + }, + "references": [ + { "path": "../../packages/mds-logger/tsconfig.build.json" }, + { "path": "../../packages/mds-providers/tsconfig.build.json" }, + { "path": "../../packages/mds-test-data/tsconfig.build.json" }, + { "path": "../../packages/mds-types/tsconfig.build.json" }, + { "path": "../../packages/mds-utils/tsconfig.build.json" } + ] +} diff --git a/packages/mds-repository/tsconfig.eslint.json b/packages/mds-repository/tsconfig.eslint.json new file mode 100644 index 000000000..2a400942a --- /dev/null +++ b/packages/mds-repository/tsconfig.eslint.json @@ -0,0 +1 @@ +{ "extends": "../../tsconfig.eslint.json" } diff --git a/packages/mds-schema-validators/index.ts b/packages/mds-schema-validators/index.ts new file mode 100644 index 000000000..2ec2291fc --- /dev/null +++ b/packages/mds-schema-validators/index.ts @@ -0,0 +1 @@ +export * from './validators' diff --git a/packages/mds-schema-validators/package.json b/packages/mds-schema-validators/package.json new file mode 100644 index 000000000..adb7b1ec2 --- /dev/null +++ b/packages/mds-schema-validators/package.json @@ -0,0 +1,31 @@ +{ + "name": "@mds-core/mds-schema-validators", + "version": "0.1.2", + "description": "Mobility Data Specification JSON schema definition and validation", + "keywords": [ + "mds", + "json", + "schema" + ], + "author": "City of Los Angeles", + "license": "Apache-2.0", + "dependencies": { + "@hapi/joi": "15.1.1", + "@mds-core/mds-providers": "0.1.26", + "@mds-core/mds-types": "0.1.23", + "@mds-core/mds-utils": "0.1.26", + "@types/hapi__joi": "15.0.4", + "joi-to-json-schema": "5.1.0" + }, + "main": "dist/index.js", + "types": "dist/index.d.ts", + "files": [ + "dist/" + ], + "scripts": { + "build": "tsc --build tsconfig.build.json", + "test": "yarn test:eslint && yarn test:unit", + "test:eslint": "eslint --ignore-path ../../.gitignore '**/*.ts'", + "test:unit": "DOTENV_CONFIG_PATH=../../.env nyc --lines 75 ts-mocha --project ../../tsconfig.json" + } +} diff --git a/packages/mds-utils/tests/validators.spec.ts b/packages/mds-schema-validators/tests/validators.spec.ts similarity index 74% rename from packages/mds-utils/tests/validators.spec.ts rename to packages/mds-schema-validators/tests/validators.spec.ts index f71783b8f..f93d44ddc 100644 --- a/packages/mds-utils/tests/validators.spec.ts +++ b/packages/mds-schema-validators/tests/validators.spec.ts @@ -15,7 +15,7 @@ */ import test from 'unit.js' -import uuid from 'uuid' +import { uuid } from '@mds-core/mds-utils' import { AUDIT_EVENT_TYPES, VEHICLE_EVENTS } from '@mds-core/mds-types' import { providers } from '@mds-core/mds-providers' // map of uuids -> obj import { @@ -31,9 +31,10 @@ import { isValidAuditEventType, isValidAuditIssueCode, isValidAuditNote, - isValidNumber + isValidNumber, + ValidationError, + validateEvent } from '../validators' -import { ValidationError } from '../exceptions' describe('Tests validators', () => { it('verified Number validator', done => { @@ -175,6 +176,96 @@ describe('Tests validators', () => { done() }) + it('verifies Vehicle Event validator', done => { + const DEREGISTER_EVENT_TYPE_REASONS = ['missing', 'decomissioned'] + const PROVIDER_PICK_UP_EVENT_TYPE_REASONS = ['rebalance', 'maintenance', 'charge', 'compliance'] + const SERVICE_END_EVENT_TYPE_REASONS = ['low_battery', 'maintenance', 'compliance', 'off_hours'] + + test.assert.throws(() => validateEvent(undefined), ValidationError) + test.assert.throws(() => validateEvent(null), ValidationError) + test.assert.throws(() => validateEvent('invalid'), ValidationError) + test.assert.throws( + () => + validateEvent({ + device_id: '395144fb-ebef-4842-ba91-b5ba98d34945', + provider_id: 'b54c08c7-884a-4c5f-b9ed-2c7dc24638cb', + event_type: 'deregister', + telemetry: { timestamp: Date.now(), gps: { lat: 0, lng: 0 } }, + timestamp: Date.now() + }), + ValidationError + ) + test.assert.throws( + () => + validateEvent({ + device_id: '395144fb-ebef-4842-ba91-b5ba98d34945', + provider_id: 'b54c08c7-884a-4c5f-b9ed-2c7dc24638cb', + event_type: 'provider_pick_up', + telemetry: { timestamp: Date.now(), gps: { lat: 0, lng: 0 } }, + timestamp: Date.now() + }), + ValidationError + ) + test.assert.throws( + () => + validateEvent({ + device_id: '395144fb-ebef-4842-ba91-b5ba98d34945', + provider_id: 'b54c08c7-884a-4c5f-b9ed-2c7dc24638cb', + event_type: 'service_end', + telemetry: { timestamp: Date.now(), gps: { lat: 0, lng: 0 } }, + timestamp: Date.now() + }), + ValidationError + ) + + DEREGISTER_EVENT_TYPE_REASONS.forEach(event_type_reason => { + test + .value( + validateEvent({ + device_id: '395144fb-ebef-4842-ba91-b5ba98d34945', + provider_id: 'b54c08c7-884a-4c5f-b9ed-2c7dc24638cb', + event_type: 'deregister', + event_type_reason, + telemetry: { timestamp: Date.now(), gps: { lat: 0, lng: 0 } }, + timestamp: Date.now() + }) + ) + .is(true) + }) + + PROVIDER_PICK_UP_EVENT_TYPE_REASONS.forEach(event_type_reason => { + test + .value( + validateEvent({ + device_id: '395144fb-ebef-4842-ba91-b5ba98d34945', + provider_id: 'b54c08c7-884a-4c5f-b9ed-2c7dc24638cb', + event_type: 'provider_pick_up', + event_type_reason, + telemetry: { timestamp: Date.now(), gps: { lat: 0, lng: 0 } }, + timestamp: Date.now() + }) + ) + .is(true) + }) + + SERVICE_END_EVENT_TYPE_REASONS.forEach(event_type_reason => { + test + .value( + validateEvent({ + device_id: '395144fb-ebef-4842-ba91-b5ba98d34945', + provider_id: 'b54c08c7-884a-4c5f-b9ed-2c7dc24638cb', + event_type: 'service_end', + event_type_reason, + telemetry: { timestamp: Date.now(), gps: { lat: 0, lng: 0 } }, + timestamp: Date.now() + }) + ) + .is(true) + }) + + done() + }) + it('verifies Audit Issue Code validator', done => { test.assert.throws(() => isValidAuditIssueCode(undefined), ValidationError) test.assert.throws(() => isValidAuditIssueCode(''), ValidationError) diff --git a/packages/mds-schema-validators/tsconfig.build.json b/packages/mds-schema-validators/tsconfig.build.json new file mode 100644 index 000000000..e6b6f8c20 --- /dev/null +++ b/packages/mds-schema-validators/tsconfig.build.json @@ -0,0 +1,11 @@ +{ + "extends": "../../tsconfig.build.json", + "compilerOptions": { + "outDir": "dist" + }, + "references": [ + { "path": "../../packages/mds-providers/tsconfig.build.json" }, + { "path": "../../packages/mds-types/tsconfig.build.json" }, + { "path": "../../packages/mds-utils/tsconfig.build.json" } + ] +} diff --git a/packages/mds-schema-validators/tsconfig.eslint.json b/packages/mds-schema-validators/tsconfig.eslint.json new file mode 100644 index 000000000..2a400942a --- /dev/null +++ b/packages/mds-schema-validators/tsconfig.eslint.json @@ -0,0 +1 @@ +{ "extends": "../../tsconfig.eslint.json" } diff --git a/packages/mds-schema-validators/validators.ts b/packages/mds-schema-validators/validators.ts new file mode 100644 index 000000000..efe2f6805 --- /dev/null +++ b/packages/mds-schema-validators/validators.ts @@ -0,0 +1,432 @@ +/* + Copyright 2019 City of Los Angeles. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +import { providers } from '@mds-core/mds-providers' // map of uuids -> obb +import { + Geography, + Policy, + VehicleEvent, + VEHICLE_TYPES, + DAYS_OF_WEEK, + AUDIT_EVENT_TYPES, + VEHICLE_EVENTS, + AUDIT_EVENT_TYPE, + VEHICLE_EVENT, + RULE_TYPES, + UUID, + Timestamp, + Telemetry, + Stop, + PROPULSION_TYPES, + VEHICLE_STATUSES, + Device +} from '@mds-core/mds-types' +import * as Joi from '@hapi/joi' +import joiToJsonSchema from 'joi-to-json-schema' + +import { ValidationError } from '@mds-core/mds-utils' + +export { ValidationError } + +interface ValidatorOptions { + property: string + assert: boolean + required: boolean + allowUnknown: boolean +} + +// Convert empty string to undefined so required/optional works as expected +export const stringSchema = Joi.string().empty('') + +// Don't allow type conversion +export const numberSchema = Joi.number().options({ convert: false }) + +export const uuidSchema = stringSchema.guid() + +export const timestampSchema = numberSchema.min(1420099200000) + +export const providerIdSchema = uuidSchema.valid(Object.keys(providers)) + +const vehicleIdSchema = stringSchema.max(255) + +const telemetrySchema = Joi.object().keys({ + gps: Joi.object() + .keys({ + lat: numberSchema.min(-90).max(90).required(), + lng: numberSchema.min(-180).max(180).required(), + speed: numberSchema.optional(), + heading: numberSchema.optional(), + accuracy: numberSchema.optional(), + hdop: numberSchema.optional(), + altitude: numberSchema.optional(), + satellites: numberSchema.optional() + }) + .required(), + charge: numberSchema.optional(), + provider_id: providerIdSchema.optional(), + device_id: uuidSchema.optional(), + timestamp: timestampSchema.required(), + recorded: timestampSchema.optional() +}) + +const ruleSchema = Joi.object().keys({ + name: Joi.string().required(), + rule_id: Joi.string().guid().required(), + rule_type: Joi.string().valid(Object.values(RULE_TYPES)).required(), + rule_units: Joi.string().valid(['seconds', 'minutes', 'hours', 'mph', 'kph']), + geographies: Joi.array().items(Joi.string().guid()), + statuses: Joi.object() + .keys({ + available: Joi.array(), + reserved: Joi.array(), + unavailable: Joi.array(), + removed: Joi.array(), + inactive: Joi.array(), + trip: Joi.array(), + elsewhere: Joi.array() + }) + .allow(null), + vehicle_types: Joi.array().items(Joi.string().valid(Object.values(VEHICLE_TYPES))), + maximum: Joi.number(), + minimum: Joi.number(), + start_time: Joi.string(), + end_time: Joi.string(), + days: Joi.array().items(Joi.string().valid(Object.values(DAYS_OF_WEEK))), + messages: Joi.object(), + value_url: Joi.string().uri() +}) + +export const policySchema = Joi.object().keys({ + name: Joi.string().required(), + description: Joi.string().required(), + policy_id: Joi.string().guid().required(), + start_date: Joi.date().timestamp('javascript').required(), + end_date: Joi.date().timestamp('javascript').allow(null), + prev_policies: Joi.array().items(Joi.string().guid()).allow(null), + provider_ids: Joi.array().items(Joi.string().guid()).allow(null), + rules: Joi.array().min(1).items(ruleSchema).required() +}) + +const policiesSchema = Joi.array().items(policySchema) + +const featureSchema = Joi.object() + .keys({ + type: Joi.string().valid(['Feature']).required(), + properties: Joi.object().required(), + geometry: Joi.object().required() + }) + .unknown(true) // TODO + +const featureCollectionSchema = Joi.object() + .keys({ + type: Joi.string().valid(['FeatureCollection']).required(), + features: Joi.array().min(1).items(featureSchema).required() + }) + .unknown(true) // TODO + +export const geographySchema = Joi.object().keys({ + geography_id: Joi.string().guid().required(), + geography_json: featureCollectionSchema, + previous_geography_ids: Joi.array().items(Joi.string().guid()).allow(null), + name: Joi.string().required() +}) + +const geographiesSchema = Joi.array().items(geographySchema) + +const eventsSchema = Joi.array().items() + +const vehicleEventTypeSchema = stringSchema.valid(Object.keys(VEHICLE_EVENTS)) + +const vehicleTypeSchema = stringSchema.valid(Object.keys(VEHICLE_TYPES)) + +const propulsionTypeSchema = stringSchema.valid(Object.keys(PROPULSION_TYPES)) + +const vehicleStatusSchema = stringSchema.valid(Object.keys(VEHICLE_STATUSES)) + +const eventSchema = Joi.object().keys({ + device_id: uuidSchema.required(), + provider_id: uuidSchema.required(), + timestamp: timestampSchema.required(), + event_type: vehicleEventTypeSchema.required(), + telemetry_timestamp: timestampSchema.optional(), + telemetry: telemetrySchema.required(), + service_area_id: uuidSchema.allow(null).optional(), + recorded: timestampSchema.optional() +}) + +const tripEventSchema = eventSchema.keys({ + trip_id: uuidSchema.required() +}) + +const serviceEndEventSchema = eventSchema.keys({ + event_type_reason: stringSchema.valid(['low_battery', 'maintenance', 'compliance', 'off_hours']).required() +}) + +const providerPickUpEventSchema = eventSchema.keys({ + event_type_reason: stringSchema.valid(['rebalance', 'maintenance', 'charge', 'compliance']).required() +}) + +const deregisterEventSchema = eventSchema.keys({ + event_type_reason: stringSchema.valid(['missing', 'decomissioned']).required() +}) + +const auditEventTypeSchema = (accept?: AUDIT_EVENT_TYPE[]): Joi.StringSchema => + stringSchema.valid(accept || Object.keys(AUDIT_EVENT_TYPES)) + +const auditIssueCodeSchema = stringSchema.max(31) + +const auditNoteSchema = stringSchema.max(255) + +const vehicleTypesCountMapSchema = Joi.object().keys({ + scooter: Joi.number(), + bicycle: Joi.number(), + car: Joi.number(), + moped: Joi.number() +}) + +const stopSchema = Joi.object().keys({ + stop_id: uuidSchema.required(), + stop_name: stringSchema.required(), + short_name: stringSchema.optional(), + platform_code: stringSchema.optional(), + geography_id: uuidSchema.optional(), + lat: numberSchema.min(-90).max(90).required(), + lng: numberSchema.min(-180).max(180).required(), + zone_id: uuidSchema.optional(), + address: stringSchema.optional(), + post_code: stringSchema.optional(), + rental_methods: stringSchema.optional(), + capacity: vehicleTypesCountMapSchema.required(), + location_type: stringSchema.optional(), + timezone: stringSchema.optional(), + cross_street: stringSchema.optional(), + num_vehicles_available: vehicleTypesCountMapSchema.required(), + num_vehicles_disabled: vehicleTypesCountMapSchema.optional(), + num_spots_available: vehicleTypesCountMapSchema.required(), + num_spots_disabled: vehicleTypesCountMapSchema.optional(), + wheelchair_boarding: Joi.bool(), + reservation_cost: vehicleTypesCountMapSchema.optional() +}) + +const deviceSchema = Joi.object().keys({ + device_id: uuidSchema.required(), + provider_id: uuidSchema.required(), + vehicle_id: stringSchema.required(), + type: vehicleTypeSchema.required(), + propulsion: Joi.array().items(propulsionTypeSchema).required(), + year: numberSchema.optional(), + mfgr: stringSchema.optional(), + model: stringSchema.optional(), + recorded: timestampSchema.optional(), + status: vehicleStatusSchema +}) + +const Format = (property: string, error: Joi.ValidationError): string => { + const [{ message, path }] = error.details + const [, ...details] = message.split(' ') + return `${[property, ...path].join('.')} ${details.join(' ')}` +} + +export const ValidateSchema = ( + value: unknown, + schema: Joi.Schema, + options: Partial = {} +): value is T => { + const { assert = true, required = true, property = 'value', allowUnknown = false } = options + const { error } = Joi.validate(value, schema, { presence: required ? 'required' : 'optional', allowUnknown }) + if (error && assert) { + throw new ValidationError(`invalid_${property}`.toLowerCase(), { + [property]: value, + details: Format(property, error) + }) + } + return !error +} + +interface NumberValidatorOptions extends ValidatorOptions { + min: number + max: number +} + +export const isValidNumber = (value: unknown, options: Partial = {}): value is number => + ValidateSchema( + value, + numberSchema + .min(options.min === undefined ? Number.MIN_SAFE_INTEGER : options.min) + .max(options.max === undefined ? Number.MAX_SAFE_INTEGER : options.max), + options + ) + +export const isValidAuditTripId = ( + audit_trip_id: unknown, + options: Partial = {} +): audit_trip_id is UUID => ValidateSchema(audit_trip_id, uuidSchema, { property: 'audit_trip_id', ...options }) + +interface AuditEventValidatorOptions extends ValidatorOptions { + accept: AUDIT_EVENT_TYPE[] +} + +export const isValidStop = (value: unknown): value is Stop => { + const { error } = Joi.validate(value, stopSchema) + if (error) { + throw new ValidationError('invalid_stop', { + value, + details: Format('stop', error) + }) + } + return true +} + +export const isValidDeviceId = (value: unknown, options: Partial = {}): value is UUID => + ValidateSchema(value, uuidSchema, { property: 'device_id', ...options }) + +export const isValidAuditEventType = ( + value: unknown, + { accept, ...options }: Partial = {} +): value is AUDIT_EVENT_TYPE => + ValidateSchema(value, auditEventTypeSchema(accept), { property: 'audit_event_type', ...options }) + +export const isValidTimestamp = (value: unknown, options: Partial = {}): value is Timestamp => + ValidateSchema(value, timestampSchema, { property: 'timestamp', ...options }) + +export const isValidProviderId = (value: unknown, options: Partial = {}): value is UUID => + ValidateSchema(value, providerIdSchema, { property: 'provider_id', ...options }) + +export const isValidProviderVehicleId = (value: unknown, options: Partial = {}): value is string => + ValidateSchema(value, vehicleIdSchema, { property: 'provider_vehicle_id', ...options }) + +export const isValidAuditEventId = (value: unknown, options: Partial = {}): value is UUID => + ValidateSchema(value, uuidSchema, { property: 'audit_event_id', ...options }) + +export const isValidAuditDeviceId = (value: unknown, options: Partial = {}): value is UUID => + ValidateSchema(value, uuidSchema, { property: 'audit_device_id', ...options }) + +export const isValidTelemetry = (value: unknown, options: Partial = {}): value is Telemetry => + ValidateSchema(value, telemetrySchema, { property: 'telemetry', ...options }) + +export const isValidDevice = (value: unknown, options: Partial = {}): value is Device => + ValidateSchema(value, deviceSchema, options) + +export const isValidEvent = (value: unknown, options: Partial = {}): value is VehicleEvent => + ValidateSchema(value, eventSchema, options) + +export const isValidVehicleEventType = ( + value: unknown, + options: Partial = {} +): value is VEHICLE_EVENT => + ValidateSchema(value, vehicleEventTypeSchema, { property: 'vehicle_event_type', ...options }) + +export const isValidAuditIssueCode = (value: unknown, options: Partial = {}): value is string => + ValidateSchema(value, auditIssueCodeSchema, { property: 'audit_issue_code', ...options }) + +export const isValidAuditNote = (value: unknown, options: Partial = {}): value is string => + ValidateSchema(value, auditNoteSchema, { property: 'note', ...options }) + +export const HasPropertyAssertion = (obj: unknown, ...props: (keyof T)[]): obj is T => + typeof obj === 'object' && obj !== null && props.every(prop => prop in obj) + +export function validatePolicies(policies: unknown): policies is Policy[] { + const { error } = Joi.validate(policies, policiesSchema) + if (error) { + throw new ValidationError('invalid_policies', { + policies, + details: Format('policies', error) + }) + } + return true +} + +export function validateGeographies(geographies: unknown): geographies is Geography[] { + const { error } = Joi.validate(geographies, geographiesSchema) + if (error) { + throw new ValidationError('invalid_geographies', { + geographies, + details: Format('geographies', error) + }) + } + return true +} + +export function validateEvents(events: unknown): events is VehicleEvent[] { + const { error } = Joi.validate(events, eventsSchema) + if (error) { + throw new ValidationError('invalid events', { + events, + details: Format('events', error) + }) + } + return true +} + +export function policyValidationDetails(policy: Policy): Joi.ValidationErrorItem[] | null { + const { error } = Joi.validate(policy, policySchema, { allowUnknown: false }) + if (error) { + return error.details + } + return null +} + +export function geographyValidationDetails(geography: Geography): Joi.ValidationErrorItem[] | null { + const { error } = Joi.validate(geography, geographySchema, { allowUnknown: false }) + if (error) { + return error.details + } + return null +} + +export function rawValidatePolicy(policy: Policy): Joi.ValidationResult { + return Joi.validate(policy, policySchema) +} + +const validateTripEvent = (event: VehicleEvent) => ValidateSchema(event, tripEventSchema, {}) + +const validateProviderPickUpEvent = (event: VehicleEvent) => ValidateSchema(event, providerPickUpEventSchema, {}) + +const validateServiceEndEvent = (event: VehicleEvent) => ValidateSchema(event, serviceEndEventSchema, {}) + +const validateDeregisterEvent = (event: VehicleEvent) => ValidateSchema(event, deregisterEventSchema, {}) + +export const validateEvent = (event: unknown) => { + if (isValidEvent(event, { allowUnknown: true })) { + const { event_type } = event + + const TRIP_EVENTS: string[] = [ + VEHICLE_EVENTS.trip_start, + VEHICLE_EVENTS.trip_end, + VEHICLE_EVENTS.trip_enter, + VEHICLE_EVENTS.trip_leave + ] + + if (TRIP_EVENTS.includes(event_type)) { + return validateTripEvent(event) + } + if (event_type === VEHICLE_EVENTS.provider_pick_up) { + return validateProviderPickUpEvent(event) + } + if (event_type === VEHICLE_EVENTS.service_end) { + return validateServiceEndEvent(event) + } + if (event_type === VEHICLE_EVENTS.deregister) { + return validateDeregisterEvent(event) + } + + return ValidateSchema(event, eventSchema, {}) + } +} + +export const policySchemaJson = joiToJsonSchema(policySchema) + +export const SchemaBuilder = Joi diff --git a/packages/mds-service-helpers/@types/index.ts b/packages/mds-service-helpers/@types/index.ts new file mode 100644 index 000000000..321b0488a --- /dev/null +++ b/packages/mds-service-helpers/@types/index.ts @@ -0,0 +1,57 @@ +/* + Copyright 2019-2020 City of Los Angeles. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +import { AnyFunction } from '@mds-core/mds-types' + +export const ServiceErrorDescriptorTypes = [ + 'ServiceException', + 'NotFoundError', + 'ConflictError', + 'ValidationError' +] as const + +export type ServiceErrorDescriptorType = typeof ServiceErrorDescriptorTypes[number] + +export type ServiceErrorDescriptor = Readonly<{ + isServiceError: true + type: E + message: string + details?: string +}> + +export interface ServiceErrorType { + error: ServiceErrorDescriptor +} + +export interface ServiceResultType { + error: null + result: R +} + +export type ServiceResponse = ServiceErrorType | ServiceResultType + +export type ServiceClient = { + [P in keyof I]: I[P] extends AnyFunction ? (...args: Parameters) => Promise : never +} + +export type ServiceProvider = { + [P in keyof I]: I[P] extends AnyFunction ? (...args: Parameters) => Promise> : never +} + +export interface ProcessController { + start: () => Promise + stop: () => Promise +} diff --git a/packages/mds-service-helpers/client/index.ts b/packages/mds-service-helpers/client/index.ts new file mode 100644 index 000000000..803b39f41 --- /dev/null +++ b/packages/mds-service-helpers/client/index.ts @@ -0,0 +1,41 @@ +/* + Copyright 2019-2020 City of Los Angeles. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +import { AnyFunction } from '@mds-core/mds-types' +import { ServiceResponse, ServiceErrorDescriptor, ServiceErrorDescriptorType } from '../@types' + +export const isServiceError = ( + error: unknown, + ...types: E[] +): error is ServiceErrorDescriptor => + typeof error === 'object' && + error !== null && + (error as ServiceErrorDescriptor).isServiceError === true && + (types.length === 0 || types.includes((error as ServiceErrorDescriptor).type)) + +// eslint-reason type inference requires any +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export type ServiceResponseMethod = AnyFunction>> + +export const UnwrapServiceResult = (method: M) => async ( + ...args: Parameters +): Promise extends Promise> ? R : never> => { + const response = await method(...args) + if (response.error) { + throw response.error + } + return response.result +} diff --git a/packages/mds-service-helpers/index.ts b/packages/mds-service-helpers/index.ts new file mode 100644 index 000000000..797a307f6 --- /dev/null +++ b/packages/mds-service-helpers/index.ts @@ -0,0 +1,19 @@ +/* + Copyright 2019-2020 City of Los Angeles. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +export * from './@types' +export * from './client' +export * from './server' diff --git a/packages/mds-service-helpers/package.json b/packages/mds-service-helpers/package.json new file mode 100644 index 000000000..242a6b1d9 --- /dev/null +++ b/packages/mds-service-helpers/package.json @@ -0,0 +1,28 @@ +{ + "name": "@mds-core/mds-service-helpers", + "version": "0.1.0", + "description": "Mobility Data Specification Service Helpers", + "main": "dist/index.js", + "types": "dist/index.d.ts", + "files": [ + "dist/" + ], + "keywords": [ + "mds" + ], + "scripts": { + "build": "tsc --build tsconfig.build.json", + "test": "yarn test:eslint && yarn test:unit", + "test:eslint": "eslint --ignore-path ../../.gitignore '**/*.ts'", + "test:unit": "DOTENV_CONFIG_PATH=../../.env nyc ts-mocha --project ../../tsconfig.json" + }, + "author": "City of Los Angeles", + "license": "Apache-2.0", + "dependencies": { + "@mds-core/mds-logger": "0.1.24", + "@mds-core/mds-types": "0.1.23", + "@mds-core/mds-utils": "0.1.26", + "@types/async-retry": "1.4.1", + "async-retry": "1.3.1" + } +} diff --git a/packages/mds-service-helpers/server/index.ts b/packages/mds-service-helpers/server/index.ts new file mode 100644 index 000000000..b80bfb048 --- /dev/null +++ b/packages/mds-service-helpers/server/index.ts @@ -0,0 +1,153 @@ +/* + Copyright 2019-2020 City of Los Angeles. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +import logger from '@mds-core/mds-logger' +import { hours, minutes, seconds, NotFoundError, ValidationError, ConflictError } from '@mds-core/mds-utils' +import { Nullable } from '@mds-core/mds-types' +import retry, { Options as RetryOptions } from 'async-retry' +import { ServiceResultType, ServiceErrorDescriptor, ServiceErrorType, ProcessController } from '../@types' + +type ProcessMonitorOptions = Partial< + Omit & { + interval: number + signals: NodeJS.Signals[] + } +> + +const ProcessMonitor = async ( + controller: ProcessController, + options: ProcessMonitorOptions = {} +): Promise => { + const { + interval = hours(1), + signals = ['SIGINT', 'SIGTERM'], + retries = 15, + minTimeout = seconds(2), + maxTimeout = minutes(2), + ...retryOptions + } = options + + const { + env: { npm_package_name, npm_package_version, npm_package_git_commit } + } = process + + const version = `${npm_package_name} v${npm_package_version} (${npm_package_git_commit ?? 'local'})` + + // Initialize the service + try { + await retry( + async () => { + logger.info(`Initializing process ${version}`) + await controller.start() + }, + { + retries, + minTimeout, + maxTimeout, + + ...retryOptions, + onRetry: (error, attempt) => { + /* istanbul ignore next */ + logger.error( + `Initializing process ${version} failed: ${error.message}, Retrying ${attempt} of ${retries}....` + ) + } + } + ) + } catch (error) /* istanbul ignore next */ { + logger.error(`Initializing process ${version} failed: ${error.message}, Exiting...`) + await controller.stop() + process.exit(1) + } + + // Keep NodeJS process alive + logger.info(`Monitoring process ${version} for ${signals.join(', ')}`) + const timeout = setInterval(() => { + logger.info(`Monitoring process ${version} for ${signals.join(', ')}`) + }, interval) + + const terminate = async (signal: NodeJS.Signals) => { + clearInterval(timeout) + logger.info(`Terminating process ${version} on ${signal}`) + await controller.stop() + process.exit(0) + } + + // Monitor process for signals + signals.forEach(signal => + process.on(signal, async () => { + await terminate(signal) + }) + ) + + return { + start: async () => undefined, + stop: async () => terminate('SIGUSR1') + } +} + +export const ProcessManager = (controller: ProcessController, options: ProcessMonitorOptions = {}) => ({ + monitor: () => { + // eslint-reason disable in this one location until top-level await + // eslint-disable-next-line @typescript-eslint/no-floating-promises + ProcessMonitor(controller, options) + }, + controller: (): ProcessController => { + let monitor: Nullable = null + return { + start: async () => { + if (!monitor) { + monitor = await ProcessMonitor(controller, options) + } + }, + stop: async () => { + if (monitor) { + await monitor.stop() + monitor = null + } + } + } + } +}) + +export const ServiceResult = (result: R): ServiceResultType => ({ error: null, result }) + +export const ServiceError = ( + error: Omit, 'isServiceError'> +): ServiceErrorType => ({ + error: { isServiceError: true, ...error } +}) + +export const ServiceException = (message: string, error?: unknown) => { + const details = (error instanceof Error && error.message) || undefined + + /* istanbul ignore if */ + if (error instanceof NotFoundError) { + return ServiceError({ type: 'NotFoundError', message, details }) + } + + /* istanbul ignore if */ + if (error instanceof ValidationError) { + return ServiceError({ type: 'ValidationError', message, details }) + } + + /* istanbul ignore if */ + if (error instanceof ConflictError) { + return ServiceError({ type: 'ConflictError', message, details }) + } + + return ServiceError({ type: 'ServiceException', message, details }) +} diff --git a/packages/mds-service-helpers/tests/index.spec.ts b/packages/mds-service-helpers/tests/index.spec.ts new file mode 100644 index 000000000..9e2cb0bb8 --- /dev/null +++ b/packages/mds-service-helpers/tests/index.spec.ts @@ -0,0 +1,104 @@ +/* + Copyright 2019-2020 City of Los Angeles. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +import test from 'unit.js' +import { ServiceResult, ServiceError, ServiceException, isServiceError, ProcessManager } from '../index' +import { UnwrapServiceResult } from '../client' + +describe('Tests Service Helpers', () => { + it('ServiceResult', async () => { + const { result } = ServiceResult('success') + test.value(result).is('success') + }) + + it('ServiceError', async () => { + const { error } = ServiceError({ type: 'ValidationError', message: 'Validation Error' }) + test.value(error.type).is('ValidationError') + test.value(error.message).is('Validation Error') + test.value(error.details).is(undefined) + }) + + it('ServiceException', async () => { + const { error } = ServiceException('Validation Error') + test.value(error.type).is('ServiceException') + test.value(error.message).is('Validation Error') + test.value(error.details).is(undefined) + }) + + it('ServiceException (with Error)', async () => { + const { error } = ServiceException('Validation Error', Error('Error Message')) + test.value(error.type).is('ServiceException') + test.value(error.message).is('Validation Error') + test.value(error.details).is('Error Message') + }) + + it('UnwrapServiceResult ServiceResult', async () => { + try { + const result = await UnwrapServiceResult(async () => ServiceResult('success'))() + test.value(result).is('success') + } catch (error) { + test.value(error).is(null) + } + }) + + it('Catch ServiceError', async () => { + try { + const result = await UnwrapServiceResult(async () => + ServiceError({ type: 'ValidationError', message: 'Validation Error' }) + )() + test.value(result).is(null) + } catch (error) { + test.value(isServiceError(error)).is(true) + if (isServiceError(error)) { + test.value(error.type).is('ValidationError') + test.value(error.message).is('Validation Error') + test.value(error.details).is(undefined) + } + } + }) + + it('Custom ServiceError type', async () => { + const { error } = ServiceError({ type: 'CustomError', message: 'Custom Error Message' }) + test.value(isServiceError(error, 'CustomError')).is(true) + }) + + it('ServiceError type guard', async () => { + try { + const error = Error('Error') + test.value(isServiceError(ServiceException('Error', error).error)).is(true) + throw error + } catch (error) { + test.value(isServiceError(error)).is(false) + test.value(error instanceof Error).is(true) + } + }) + + it('Test ServiceManager Controller', async () => { + let started = false + const controller = ProcessManager({ + start: async () => { + started = true + }, + stop: async () => { + started = false + } + }).controller() + await controller.start() + test.value(started).is(true) + await controller.stop() + test.value(started).is(false) + }) +}) diff --git a/packages/mds-cache/tsconfig.build.json b/packages/mds-service-helpers/tsconfig.build.json similarity index 100% rename from packages/mds-cache/tsconfig.build.json rename to packages/mds-service-helpers/tsconfig.build.json diff --git a/packages/mds-service-helpers/tsconfig.eslint.json b/packages/mds-service-helpers/tsconfig.eslint.json new file mode 100644 index 000000000..2a400942a --- /dev/null +++ b/packages/mds-service-helpers/tsconfig.eslint.json @@ -0,0 +1 @@ +{ "extends": "../../tsconfig.eslint.json" } diff --git a/container-images/mds-daily/index.ts b/packages/mds-stream/agency-stream-interface.ts similarity index 55% rename from container-images/mds-daily/index.ts rename to packages/mds-stream/agency-stream-interface.ts index 77535b685..93f2e7c0c 100644 --- a/container-images/mds-daily/index.ts +++ b/packages/mds-stream/agency-stream-interface.ts @@ -14,15 +14,12 @@ limitations under the License. */ -// Express local -import { ApiServer } from '@mds-core/mds-api-server' -import { api } from '@mds-core/mds-daily' -import { env } from '@container-images/env-inject' +import { VehicleEvent, Telemetry, Device } from '@mds-core/mds-types' -const { npm_package_name, npm_package_version, npm_package_git_commit, PORT = 4005 } = env() - -ApiServer(api).listen(PORT, () => - /* eslint-reason avoids import of logger */ - /* eslint-disable-next-line no-console */ - console.log(`${npm_package_name} v${npm_package_version} (${npm_package_git_commit}) running on port ${PORT}`) -) +export interface AgencyStreamInterface { + writeEvent: (event: VehicleEvent) => Promise + writeTelemetry: (telemetry: Telemetry[]) => Promise + writeDevice: (device: Device) => Promise + shutdown: () => Promise + initialize: () => Promise +} diff --git a/packages/mds-stream/helpers.ts b/packages/mds-stream/helpers.ts new file mode 100644 index 000000000..e69de29bb diff --git a/packages/mds-stream/index.ts b/packages/mds-stream/index.ts index d670e87bf..edb8b9b0a 100644 --- a/packages/mds-stream/index.ts +++ b/packages/mds-stream/index.ts @@ -17,7 +17,6 @@ import logger from '@mds-core/mds-logger' import redis from 'redis' import bluebird from 'bluebird' -import Cloudevent from 'cloudevents-sdk' import { Device, VehicleEvent, Telemetry } from '@mds-core/mds-types' import { Stream, @@ -25,39 +24,21 @@ import { ReadStreamResult, DEVICE_INDEX_STREAM, DEVICE_RAW_STREAM, - PROVIDER_EVENT_STREAM, ReadStreamOptions, StreamItemID } from './types' +import { AgencyStreamKafka } from './kafka/agency-stream-kafka' +import { KafkaStreamConsumer, KafkaStreamProducer } from './kafka' -const { env } = process +import { NatsStreamConsumer } from './nats/stream-consumer' +import { NatsStreamProducer } from './nats/stream-producer' -/* eslint-reason no cloud-event typings */ -/* eslint-disable-next-line @typescript-eslint/no-explicit-any */ -let binding: any = null +import { AgencyStreamNats } from './nats/agency-stream-nats' -function getBinding() { - if (!binding) { - const config = { - method: 'POST', - url: env.SINK - } +export { KafkaStreamConsumerOptions, KafkaStreamProducerOptions } from './kafka' +export { StreamConsumer, StreamProducer } from './stream-interface' - // eslint-disable-next-line new-cap - binding = new Cloudevent.bindings['http-binary0.2'](config) - } - - return binding -} - -async function writeCloudEvent(type: string, data: string) { - const cloudevent = new Cloudevent(Cloudevent.specs['0.2']) - .type(type) - .source(env.CE_NAME) - .data(data) - - return getBinding().emit(cloudevent) -} +const { env } = process declare module 'redis' { interface RedisClient { @@ -95,7 +76,7 @@ declare module 'redis' { interface Multi { xadd: (...args: unknown[]) => string - execAsync: () => Promise + execAsync: () => Promise } } @@ -108,8 +89,7 @@ let cachedClient: redis.RedisClient | null = null const STREAM_MAXLEN: { [S in Stream]: number } = { 'device:index': 10_000, - 'device:raw': 1_000_000, - 'provider:event': 100_000 + 'device:raw': 1_000_000 } async function getClient() { @@ -126,7 +106,7 @@ async function getClient() { logger.info(`connecting to redis on ${host}:${port}`) cachedClient = redis.createClient(Number(port), host) cachedClient.on('error', async err => { - await logger.error(`redis error ${err}`) + logger.error(`redis error ${err}`) }) await cachedClient.dbsizeAsync().then(size => logger.info(`redis has ${size} keys`)) } @@ -134,11 +114,9 @@ async function getClient() { } async function initialize() { - if (env.SINK) { - getBinding() - } else { - await getClient() - } + await AgencyStreamKafka.initialize() + await AgencyStreamNats.initialize() + await getClient() } async function reset() { @@ -155,6 +133,8 @@ async function shutdown() { await cachedClient.quit() cachedClient = null } + await AgencyStreamKafka.shutdown() + await AgencyStreamNats.shutdown() } async function writeStream(stream: Stream, field: string, value: unknown) { @@ -171,24 +151,32 @@ async function writeStreamBatch(stream: Stream, field: string, values: unknown[] // put basics of vehicle in the cache async function writeDevice(device: Device) { - if (env.SINK) { - return writeCloudEvent('mds.device', JSON.stringify(device)) + if (env.NATS) { + await AgencyStreamNats.writeDevice(device) + } + if (env.KAFKA_HOST) { + await AgencyStreamKafka.writeDevice(device) } return writeStream(DEVICE_INDEX_STREAM, 'data', device) } async function writeEvent(event: VehicleEvent) { - if (env.SINK) { - return writeCloudEvent('mds.event', JSON.stringify(event)) + if (env.NATS) { + await AgencyStreamNats.writeEvent(event) + } + if (env.KAFKA_HOST) { + await AgencyStreamKafka.writeEvent(event) } - return Promise.all([DEVICE_RAW_STREAM, PROVIDER_EVENT_STREAM].map(stream => writeStream(stream, 'event', event))) + return writeStream(DEVICE_RAW_STREAM, 'event', event) } // put latest locations in the cache async function writeTelemetry(telemetry: Telemetry[]) { - if (env.SINK) { - await Promise.all(telemetry.map(item => writeCloudEvent('mds.telemetry', JSON.stringify(item)))) - return + if (env.NATS) { + await AgencyStreamNats.writeTelemetry(telemetry) + } + if (env.KAFKA_HOST) { + await AgencyStreamKafka.writeTelemetry(telemetry) } const start = now() await writeStreamBatch(DEVICE_RAW_STREAM, 'telemetry', telemetry) @@ -291,7 +279,7 @@ async function health() { return { using: 'redis', status } } -export = { +export default { createStreamGroup, getStreamInfo, health, @@ -305,5 +293,9 @@ export = { writeEvent, writeStream, writeStreamBatch, - writeTelemetry + writeTelemetry, + KafkaStreamConsumer, + KafkaStreamProducer, + NatsStreamConsumer, + NatsStreamProducer } diff --git a/packages/mds-stream/kafka/agency-stream-kafka.ts b/packages/mds-stream/kafka/agency-stream-kafka.ts new file mode 100644 index 000000000..2908bcfac --- /dev/null +++ b/packages/mds-stream/kafka/agency-stream-kafka.ts @@ -0,0 +1,39 @@ +/* + Copyright 2019 City of Los Angeles. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +import { VehicleEvent, Telemetry, Device } from '@mds-core/mds-types' +import { getEnvVar } from '@mds-core/mds-utils' +import { KafkaStreamProducer } from './stream-producer' +import { AgencyStreamInterface } from '../agency-stream-interface' + +const { TENANT_ID } = getEnvVar({ + TENANT_ID: 'mds' +}) +const deviceProducer = KafkaStreamProducer(`${TENANT_ID}.device`) +const eventProducer = KafkaStreamProducer(`${TENANT_ID}.event`) +const telemetryProducer = KafkaStreamProducer(`${TENANT_ID}.telemetry`) + +export const AgencyStreamKafka: AgencyStreamInterface = { + initialize: async () => { + await Promise.all([deviceProducer.initialize(), eventProducer.initialize(), telemetryProducer.initialize()]) + }, + writeEvent: eventProducer.write, + writeTelemetry: telemetryProducer.write, + writeDevice: deviceProducer.write, + shutdown: async () => { + await Promise.all([deviceProducer.shutdown(), eventProducer.shutdown(), telemetryProducer.shutdown()]) + } +} diff --git a/container-images/mds-metrics-sheet/vehicle-counts.ts b/packages/mds-stream/kafka/helpers.ts similarity index 82% rename from container-images/mds-metrics-sheet/vehicle-counts.ts rename to packages/mds-stream/kafka/helpers.ts index 5b61fb211..a220ae47f 100644 --- a/container-images/mds-metrics-sheet/vehicle-counts.ts +++ b/packages/mds-stream/kafka/helpers.ts @@ -14,6 +14,9 @@ limitations under the License. */ -import { VehicleCountsHandler as handler } from '@mds-core/mds-metrics-sheet' - -export { handler } +export const getKafkaBrokers = () => { + const { + env: { KAFKA_HOST = 'localhost:9092' } + } = process + return [KAFKA_HOST] +} diff --git a/packages/mds-stream/kafka/index.ts b/packages/mds-stream/kafka/index.ts new file mode 100644 index 000000000..b60776172 --- /dev/null +++ b/packages/mds-stream/kafka/index.ts @@ -0,0 +1,18 @@ +/* + Copyright 2019 City of Los Angeles. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +export { KafkaStreamConsumer, KafkaStreamConsumerOptions } from './stream-consumer' +export { KafkaStreamProducer, KafkaStreamProducerOptions } from './stream-producer' diff --git a/packages/mds-stream/kafka/stream-consumer.ts b/packages/mds-stream/kafka/stream-consumer.ts new file mode 100644 index 000000000..1a2699f6b --- /dev/null +++ b/packages/mds-stream/kafka/stream-consumer.ts @@ -0,0 +1,70 @@ +/* + Copyright 2019 City of Los Angeles. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +import { Kafka, EachMessagePayload, Consumer } from 'kafkajs' +import { Nullable } from '@mds-core/mds-types' +import logger from '@mds-core/mds-logger' +import { isDefined } from '@mds-core/mds-utils' +import { StreamConsumer } from '../stream-interface' +import { getKafkaBrokers } from './helpers' + +export interface KafkaStreamConsumerOptions { + clientId: string + groupId: string +} + +const createStreamConsumer = async ( + topic: string, + eachMessage: (payload: EachMessagePayload) => Promise, + { clientId = 'client', groupId = 'group' }: Partial = {} +) => { + try { + const kafka = new Kafka({ clientId, brokers: getKafkaBrokers() }) + const consumer = kafka.consumer({ groupId }) + await consumer.connect() + await consumer.subscribe({ topic }) + await consumer.run({ eachMessage }) + return consumer + } catch (err) { + logger.error(err) + } + return null +} + +const disconnectConsumer = async (consumer: Nullable) => { + if (isDefined(consumer)) { + await consumer.disconnect() + } +} + +export const KafkaStreamConsumer = ( + topic: string, + eachMessage: (payload: EachMessagePayload) => Promise, + options?: Partial +): StreamConsumer => { + let consumer: Nullable = null + return { + initialize: async () => { + if (!consumer) { + consumer = await createStreamConsumer(topic, eachMessage, options) + } + }, + shutdown: async () => { + await disconnectConsumer(consumer) + consumer = null + } + } +} diff --git a/packages/mds-stream/kafka/stream-producer.ts b/packages/mds-stream/kafka/stream-producer.ts new file mode 100644 index 000000000..af05f1e26 --- /dev/null +++ b/packages/mds-stream/kafka/stream-producer.ts @@ -0,0 +1,77 @@ +/* + Copyright 2019 City of Los Angeles. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +import { Kafka, Producer } from 'kafkajs' +import { isArray } from 'util' +import { Nullable } from '@mds-core/mds-types' +import logger from '@mds-core/mds-logger' +import { isDefined, ClientDisconnectedError, ExceptionMessages } from '@mds-core/mds-utils' +import { StreamProducer } from '../stream-interface' +import { getKafkaBrokers } from './helpers' + +export interface KafkaStreamProducerOptions { + clientId: string +} + +const createStreamProducer = async ({ clientId = 'writer' }: Partial = {}) => { + try { + const kafka = new Kafka({ clientId, brokers: getKafkaBrokers() }) + const producer = kafka.producer() + await producer.connect() + return producer + } catch (err) { + logger.error(err) + } + return null +} + +const disconnectProducer = async (producer: Nullable) => { + if (isDefined(producer)) { + await producer.disconnect() + } +} + +export const KafkaStreamProducer = ( + topic: string, + options?: Partial +): StreamProducer => { + let producer: Nullable = null + return { + initialize: async () => { + if (!producer) { + producer = await createStreamProducer(options) + } + }, + write: async (message: TMessage[] | TMessage) => { + if (isDefined(producer)) { + const messages = (isArray(message) ? message : [message]).map(msg => { + return { value: JSON.stringify(msg) } + }) + + await producer.send({ + topic, + messages + }) + return + } + throw new ClientDisconnectedError(ExceptionMessages.INITIALIZE_CLIENT_MESSAGE) + }, + shutdown: async () => { + await disconnectProducer(producer) + producer = null + } + } +} diff --git a/packages/mds-stream/nats/agency-stream-nats.ts b/packages/mds-stream/nats/agency-stream-nats.ts new file mode 100644 index 000000000..577fa21ce --- /dev/null +++ b/packages/mds-stream/nats/agency-stream-nats.ts @@ -0,0 +1,23 @@ +import { VehicleEvent, Telemetry, Device } from '@mds-core/mds-types' +import { getEnvVar } from '@mds-core/mds-utils' +import { AgencyStreamInterface } from '../agency-stream-interface' +import { NatsStreamProducer } from './stream-producer' + +const { TENANT_ID } = getEnvVar({ + TENANT_ID: 'mds' +}) +const deviceProducer = NatsStreamProducer(`${TENANT_ID}.device`) +const eventProducer = NatsStreamProducer(`${TENANT_ID}.event`) +const telemetryProducer = NatsStreamProducer(`${TENANT_ID}.telemetry`) + +export const AgencyStreamNats: AgencyStreamInterface = { + initialize: async () => { + await Promise.all([deviceProducer.initialize(), eventProducer.initialize(), telemetryProducer.initialize()]) + }, + writeEvent: eventProducer.write, + writeTelemetry: telemetryProducer.write, + writeDevice: deviceProducer.write, + shutdown: async () => { + await Promise.all([deviceProducer.shutdown(), eventProducer.shutdown(), telemetryProducer.shutdown()]) + } +} diff --git a/packages/mds-stream/nats/helpers.ts b/packages/mds-stream/nats/helpers.ts new file mode 100644 index 000000000..5b44b92bc --- /dev/null +++ b/packages/mds-stream/nats/helpers.ts @@ -0,0 +1,35 @@ +import { connect, MsgCallback, SubscriptionOptions, Client } from 'ts-nats' +import logger from '@mds-core/mds-logger' +import { getEnvVar } from '@mds-core/mds-utils' + +const initializeNatsClient = () => { + const { NATS } = getEnvVar({ NATS: 'localhost' }) + return connect({ + url: `nats://${NATS}:4222`, + reconnect: true, + waitOnFirstConnect: true, + maxReconnectAttempts: -1 // Retry forever + }) +} + +export const createStreamConsumer = async ( + topic: string, + processor: MsgCallback, + options: SubscriptionOptions = {} +) => { + const natsClient = await initializeNatsClient() + + try { + await natsClient.subscribe(topic, processor, options) + } catch (err) { + logger.error(err) + } + + return natsClient +} + +export const createStreamProducer = async () => { + return initializeNatsClient() +} + +export const disconnectClient = (consumer: Client) => consumer.close() diff --git a/packages/mds-stream/nats/stream-consumer.ts b/packages/mds-stream/nats/stream-consumer.ts new file mode 100644 index 000000000..4ac4ef8ae --- /dev/null +++ b/packages/mds-stream/nats/stream-consumer.ts @@ -0,0 +1,23 @@ +import { SubscriptionOptions, Client, MsgCallback } from 'ts-nats' +import { Nullable } from '@mds-core/mds-types' +import { createStreamConsumer, disconnectClient } from './helpers' +import { StreamConsumer } from '../stream-interface' + +export const NatsStreamConsumer = ( + topic: string, + eachMessage: MsgCallback, + options?: Partial +): StreamConsumer => { + let consumer: Nullable = null + return { + initialize: async () => { + if (!consumer) { + consumer = await createStreamConsumer(topic, eachMessage, options) + } + }, + shutdown: async () => { + if (consumer) await disconnectClient(consumer) + consumer = null + } + } +} diff --git a/packages/mds-stream/nats/stream-producer.ts b/packages/mds-stream/nats/stream-producer.ts new file mode 100644 index 000000000..becbf3131 --- /dev/null +++ b/packages/mds-stream/nats/stream-producer.ts @@ -0,0 +1,27 @@ +import { Client } from 'ts-nats' +import { isArray } from 'util' +import { Nullable } from '@mds-core/mds-types' +import { StreamProducer } from '../stream-interface' +import { createStreamProducer, disconnectClient } from './helpers' + +export const NatsStreamProducer = (topic: string): StreamProducer => { + let producer: Nullable = null + return { + initialize: async () => { + if (!producer) { + producer = await createStreamProducer() + } + }, + write: async (message: TMessage[] | TMessage) => { + const messages = (isArray(message) ? message : [message]).map(msg => { + return JSON.stringify(msg) + }) + + await Promise.all(messages.map(msg => producer?.publish(topic, msg))) + }, + shutdown: async () => { + if (producer) await disconnectClient(producer) + producer = null + } + } +} diff --git a/packages/mds-stream/package.json b/packages/mds-stream/package.json index 7e2f2578e..54fc042b5 100644 --- a/packages/mds-stream/package.json +++ b/packages/mds-stream/package.json @@ -1,6 +1,6 @@ { "name": "@mds-core/mds-stream", - "version": "0.1.18", + "version": "0.1.26", "description": "Mobility Data Specification stream interface", "main": "dist/index.js", "types": "dist/index.d.ts", @@ -14,18 +14,22 @@ "author": "City of Los Angeles", "license": "Apache-2.0", "dependencies": { - "@mds-core/mds-logger": "0.1.16", - "@mds-core/mds-types": "0.1.15", - "bluebird": "3.7.1", - "cloudevents-sdk": "0.3.2", + "@types/bluebird": "3.5.30", + "@types/moment-timezone": "0.5.13", + "@types/redis": "2.8.20", + "@mds-core/mds-logger": "0.1.24", + "@mds-core/mds-types": "0.1.23", + "@mds-core/mds-utils": "0.1.26", + "bluebird": "3.7.2", "flat": "5.0.0", - "node-gzip": "1.1.2", - "redis": "2.8.0" + "kafkajs": "1.12.0", + "ts-nats": "1.2.14-2", + "redis": "3.0.2" }, "scripts": { "build": "tsc --build tsconfig.build.json", "test": "yarn test:eslint && yarn test:unit", - "test:eslint": "eslint --fix --ignore-path ../../.gitignore '**/*.ts'", + "test:eslint": "eslint --ignore-path ../../.gitignore '**/*.ts'", "test:unit": "exit 0" } } diff --git a/container-images/mds-audit/index.ts b/packages/mds-stream/stream-interface.ts similarity index 55% rename from container-images/mds-audit/index.ts rename to packages/mds-stream/stream-interface.ts index d0fab1ea7..dd901953b 100644 --- a/container-images/mds-audit/index.ts +++ b/packages/mds-stream/stream-interface.ts @@ -14,15 +14,13 @@ limitations under the License. */ -// Express local -import { ApiServer } from '@mds-core/mds-api-server' -import { api } from '@mds-core/mds-audit' -import { env } from '@container-images/env-inject' +export interface StreamProducer { + initialize: () => Promise + write: (message: TMessage[] | TMessage) => Promise + shutdown: () => Promise +} -const { npm_package_name, npm_package_version, npm_package_git_commit, PORT = 4002 } = env() - -ApiServer(api).listen(PORT, () => - /* eslint-reason avoids import of logger */ - /* eslint-disable-next-line no-console */ - console.log(`${npm_package_name} v${npm_package_version} (${npm_package_git_commit}) running on port ${PORT}`) -) +export interface StreamConsumer { + initialize: () => Promise + shutdown: () => Promise +} diff --git a/packages/mds-stream/tsconfig.build.json b/packages/mds-stream/tsconfig.build.json index 0bc2bf8a4..0f316b454 100644 --- a/packages/mds-stream/tsconfig.build.json +++ b/packages/mds-stream/tsconfig.build.json @@ -3,5 +3,9 @@ "compilerOptions": { "outDir": "dist" }, - "references": [{ "path": "../../packages/mds-types/tsconfig.build.json" }] + "references": [ + { "path": "../../packages/mds-logger/tsconfig.build.json" }, + { "path": "../../packages/mds-types/tsconfig.build.json" }, + { "path": "../../packages/mds-utils/tsconfig.build.json" } + ] } diff --git a/packages/mds-stream/tsconfig.eslint.json b/packages/mds-stream/tsconfig.eslint.json new file mode 100644 index 000000000..2a400942a --- /dev/null +++ b/packages/mds-stream/tsconfig.eslint.json @@ -0,0 +1 @@ +{ "extends": "../../tsconfig.eslint.json" } diff --git a/packages/mds-stream/types.ts b/packages/mds-stream/types.ts index d719f8daf..2944f1e78 100644 --- a/packages/mds-stream/types.ts +++ b/packages/mds-stream/types.ts @@ -1,11 +1,27 @@ +/* + Copyright 2019 City of Los Angeles. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + export type ReadStreamOptions = Partial<{ count: number block: number noack: boolean }> -export const Streams = ['device:index', 'device:raw', 'provider:event'] as const -export const [DEVICE_INDEX_STREAM, DEVICE_RAW_STREAM, PROVIDER_EVENT_STREAM] = Streams +export const Streams = ['device:index', 'device:raw'] as const +export const [DEVICE_INDEX_STREAM, DEVICE_RAW_STREAM] = Streams export type Stream = typeof Streams[number] export type StreamItemID = string diff --git a/packages/mds-test-data/index.ts b/packages/mds-test-data/index.ts index 9e1029a15..51a71b85c 100644 --- a/packages/mds-test-data/index.ts +++ b/packages/mds-test-data/index.ts @@ -25,15 +25,9 @@ import { Timestamp, Telemetry, VehicleEvent, - Policy, - PROVIDER_EVENT, - PROVIDER_REASON, - PROVIDER_EVENTS, - PROVIDER_REASONS, - AccessTokenScope + Policy } from '@mds-core/mds-types' import { Geometry } from 'geojson' -import { StatusChange, Trip } from '@mds-core/mds-db/types' import { addDistanceBearing, @@ -41,25 +35,16 @@ import { makePointInShape, now, pointInShape, - randomElement, range, rangeRandom, - rangeRandomInt + rangeRandomInt, + uuid } from '@mds-core/mds-utils' -import { serviceAreaMap } from 'ladot-service-areas' +import logger from '@mds-core/mds-logger' -import uuid from 'uuid' - -import log from '@mds-core/mds-logger' - -import { - JUMP_PROVIDER_ID, - LIME_PROVIDER_ID, - BIRD_PROVIDER_ID, - TEST1_PROVIDER_ID, - providerName -} from '@mds-core/mds-providers' +import { JUMP_PROVIDER_ID, LIME_PROVIDER_ID, BIRD_PROVIDER_ID, TEST1_PROVIDER_ID } from '@mds-core/mds-providers' +import { serviceAreaMap, restrictedAreas, veniceSpecOps } from './test-areas/test-areas' import { LA_CITY_BOUNDARY } from './la-city-boundary' import { DISTRICT_SEVEN } from './district-seven' @@ -237,7 +222,29 @@ const POLICY4_JSON: Policy = { rules: [ { name: 'Greater LA', - rule_id: 'bfd790d3-87d6-41ec-afa0-98fa443ee0d3', + rule_id: uuid(), + rule_type: 'speed', + rule_units: 'mph', + geographies: [GEOGRAPHY_UUID], + statuses: { trip: [] }, + vehicle_types: [VEHICLE_TYPES.bicycle, VEHICLE_TYPES.scooter], + maximum: 25 + } + ] +} + +const POLICY5_JSON: Policy = { + policy_id: uuid(), + name: 'Policy 5', + description: 'just here to enable testing for policies by start date', + start_date: START_ONE_MONTH_AGO, + end_date: null, + prev_policies: null, + provider_ids: [], + rules: [ + { + name: 'Greater LA', + rule_id: uuid(), rule_type: 'speed', rule_units: 'mph', geographies: [GEOGRAPHY_UUID], @@ -255,6 +262,28 @@ const POLICY_JSON_MISSING_POLICY_ID = { end_date: null, prev_policies: null, provider_ids: [], + rules: [ + { + name: 'Greater LA', + rule_id: uuid(), + rule_type: 'speed', + rule_units: 'mph', + geographies: [NONEXISTENT_GEOGRAPHY_UUID], + statuses: { trip: [] }, + vehicle_types: [VEHICLE_TYPES.bicycle, VEHICLE_TYPES.scooter], + maximum: 25 + } + ] +} + +const POLICY_WITH_DUPE_RULE: Policy = { + policy_id: uuid(), + name: 'I am a no good copycat', + description: 'LADOT Pilot Speed Limit Limitations', + start_date: now(), + end_date: null, + prev_policies: null, + provider_ids: [], rules: [ { name: 'Greater LA', @@ -269,6 +298,29 @@ const POLICY_JSON_MISSING_POLICY_ID = { ] } +const PUBLISHED_POLICY: Policy = { + policy_id: uuid(), + name: 'I am published but do not do much', + description: 'LADOT Pilot Speed Limit Limitations', + start_date: START_ONE_MONTH_AGO, + publish_date: START_ONE_MONTH_AGO, + end_date: null, + prev_policies: null, + provider_ids: [], + rules: [ + { + name: 'Greater LA', + rule_id: uuid(), + rule_type: 'speed', + rule_units: 'mph', + geographies: [GEOGRAPHY_UUID], + statuses: { trip: [] }, + vehicle_types: [VEHICLE_TYPES.bicycle, VEHICLE_TYPES.scooter], + maximum: 25 + } + ] +} + function makeTelemetry(devices: Device[], timestamp: Timestamp): Telemetry[] { let i = 0 const serviceAreaKeys = Object.keys(serviceAreaMap) @@ -278,7 +330,7 @@ function makeTelemetry(devices: Device[], timestamp: Timestamp): Telemetry[] { [key: string]: { num_clusters: number; cluster_radii: number[]; cluster_centers: { lat: number; lng: number }[] } } = {} - log.info('clustering') + logger.info('clustering') serviceAreaKeys.slice(0, 1).map(key => { const serviceArea = serviceAreaMap[key] const serviceAreaMultipoly = serviceArea.area @@ -496,64 +548,7 @@ function makeDevices(count: number, timestamp: Timestamp, provider_id = TEST1_PR return devices } -function makeStatusChange(device: Device, timestamp: Timestamp): StatusChange { - const event_type = randomElement(Object.keys(PROVIDER_EVENTS) as PROVIDER_EVENT[]) - const event_type_reason = randomElement(Object.keys(PROVIDER_REASONS) as PROVIDER_REASON[]) - - return { - provider_id: device.provider_id, - provider_name: providerName(device.provider_id), - device_id: device.device_id, - vehicle_id: device.vehicle_id, - event_type, - event_type_reason, - event_location: null, - battery_pct: rangeRandomInt(1, 100), - associated_trip: uuid(), - event_time: timestamp, - vehicle_type: device.type, - propulsion_type: device.propulsion, - recorded: now() - } -} - -function makeTrip(device: Device): Trip { - return { - provider_id: device.provider_id, - provider_name: providerName(device.provider_id), - device_id: device.device_id, - vehicle_id: device.vehicle_id, - vehicle_type: device.type, - propulsion_type: device.propulsion, - provider_trip_id: uuid(), - trip_duration: rangeRandomInt(5), - trip_distance: rangeRandomInt(5), - route: { - type: 'FeatureCollection', - features: [ - { - type: 'Feature', - properties: { - timestamp: now() - }, - geometry: { - type: 'Point', - coordinates: [Math.random() * 10, Math.random() * 10] - } - } - ] - }, - accuracy: Math.random() * 3, - trip_start: now() - 1000 * Math.random(), - trip_end: now(), - parking_verification_url: 'http://iamverified.com', - standard_cost: rangeRandomInt(5), - actual_cost: rangeRandomInt(5), - recorded: now() - } -} - -const SCOPED_AUTH = (scopes: AccessTokenScope[], principalId = TEST1_PROVIDER_ID) => +const SCOPED_AUTH = (scopes: AccessTokenScope[], principalId = TEST1_PROVIDER_ID) => `basic ${Buffer.from(`${principalId}|${scopes.join(' ')}`).toString('base64')}` export { @@ -567,8 +562,11 @@ export { POLICY2_JSON, POLICY3_JSON, POLICY4_JSON, + POLICY5_JSON, POLICY_JSON_MISSING_POLICY_ID, + POLICY_WITH_DUPE_RULE, POLICY_UUID, + PUBLISHED_POLICY, SUPERSEDING_POLICY_UUID, POLICY2_UUID, POLICY3_UUID, @@ -586,7 +584,8 @@ export { makeTelemetryInArea, makeTelemetryInShape, makeTelemetryStream, - makeStatusChange, - makeTrip, - SCOPED_AUTH + SCOPED_AUTH, + serviceAreaMap, + restrictedAreas, + veniceSpecOps } diff --git a/packages/mds-test-data/package.json b/packages/mds-test-data/package.json index ca620bc0d..03e384798 100644 --- a/packages/mds-test-data/package.json +++ b/packages/mds-test-data/package.json @@ -1,6 +1,6 @@ { "name": "@mds-core/mds-test-data", - "version": "0.1.18", + "version": "0.1.26", "description": "Mobility Data Specification test data generator", "main": "dist/index.js", "types": "dist/index.d.ts", @@ -14,15 +14,14 @@ "author": "City of Los Angeles", "license": "Apache-2.0", "dependencies": { - "@mds-core/mds-logger": "0.1.16", - "@mds-core/mds-types": "0.1.15", - "@mds-core/mds-utils": "0.1.18", - "ladot-service-areas": "0.1.9", - "uuid": "3.3.3" + "@mds-core/mds-logger": "0.1.24", + "@mds-core/mds-providers": "0.1.26", + "@mds-core/mds-types": "0.1.23", + "@mds-core/mds-utils": "0.1.26" }, "scripts": { "build": "tsc --build tsconfig.build.json", - "test:eslint": "eslint --fix --ignore-path ../../.gitignore '**/*.ts'", + "test:eslint": "eslint --ignore-path ../../.gitignore '**/*.ts'", "test:unit": "exit 0" } } diff --git a/packages/ladot-service-areas/council-district-11.js b/packages/mds-test-data/test-areas/council-district-11.ts similarity index 99% rename from packages/ladot-service-areas/council-district-11.js rename to packages/mds-test-data/test-areas/council-district-11.ts index 37d5c9916..ea02ee17c 100644 --- a/packages/ladot-service-areas/council-district-11.js +++ b/packages/mds-test-data/test-areas/council-district-11.ts @@ -1,4 +1,6 @@ -module.exports = { +import { FeatureCollection } from 'geojson' + +export default { type: 'FeatureCollection', features: [ { @@ -2889,4 +2891,4 @@ module.exports = { } } ] -} +} as FeatureCollection diff --git a/packages/mds-compliance/tests/la-city-boundary.ts b/packages/mds-test-data/test-areas/la-city-boundary.ts similarity index 99% rename from packages/mds-compliance/tests/la-city-boundary.ts rename to packages/mds-test-data/test-areas/la-city-boundary.ts index 30c54b8e8..3967de1d5 100644 --- a/packages/mds-compliance/tests/la-city-boundary.ts +++ b/packages/mds-test-data/test-areas/la-city-boundary.ts @@ -1,11 +1,23 @@ import { FeatureCollection } from 'geojson' -const la_city_boundary: FeatureCollection = { +export default { type: 'FeatureCollection', + crs: { + type: 'name', + properties: { + name: 'EPSG:4326' + } + }, features: [ { type: 'Feature', id: 1, + properties: { + name: 'City Boundaries', + OBJECTID_1: 1, + OBJECTID: 1, + CITY: 'IN' + }, geometry: { type: 'Polygon', coordinates: [ @@ -8757,15 +8769,7 @@ const la_city_boundary: FeatureCollection = { [-118.443819758262, 34.2733849274111] ] ] - }, - properties: { - name: 'City Boundaries', - OBJECTID_1: 1, - OBJECTID: 1, - CITY: 'IN' } } ] -} - -export { la_city_boundary } +} as FeatureCollection diff --git a/packages/ladot-service-areas/la-dacs.js b/packages/mds-test-data/test-areas/la-dacs.ts similarity index 99% rename from packages/ladot-service-areas/la-dacs.js rename to packages/mds-test-data/test-areas/la-dacs.ts index 8a96d6417..f16d3a581 100644 --- a/packages/ladot-service-areas/la-dacs.js +++ b/packages/mds-test-data/test-areas/la-dacs.ts @@ -1,4 +1,6 @@ -module.exports = { +import { FeatureCollection } from 'geojson' + +export default { type: 'FeatureCollection', features: [ { @@ -7863,4 +7865,4 @@ module.exports = { } } ] -} +} as FeatureCollection diff --git a/packages/ladot-service-areas/non-san-fernando-dac.js b/packages/mds-test-data/test-areas/non-san-fernando-dac.ts similarity index 99% rename from packages/ladot-service-areas/non-san-fernando-dac.js rename to packages/mds-test-data/test-areas/non-san-fernando-dac.ts index 67b80b461..55aa44a86 100644 --- a/packages/ladot-service-areas/non-san-fernando-dac.js +++ b/packages/mds-test-data/test-areas/non-san-fernando-dac.ts @@ -1,4 +1,6 @@ -module.exports = { +import { FeatureCollection } from 'geojson' + +export default { type: 'FeatureCollection', crs: { type: 'name', @@ -2516,4 +2518,4 @@ module.exports = { } } ] -} +} as FeatureCollection diff --git a/packages/ladot-service-areas/restricted-areas.js b/packages/mds-test-data/test-areas/restricted-areas.ts similarity index 97% rename from packages/ladot-service-areas/restricted-areas.js rename to packages/mds-test-data/test-areas/restricted-areas.ts index e31c1aca7..51ba53f19 100644 --- a/packages/ladot-service-areas/restricted-areas.js +++ b/packages/mds-test-data/test-areas/restricted-areas.ts @@ -1,4 +1,6 @@ -module.exports = { +import { FeatureCollection } from 'geojson' + +export default { type: 'FeatureCollection', features: [ { @@ -85,4 +87,4 @@ module.exports = { } } ] -} +} as FeatureCollection diff --git a/packages/ladot-service-areas/san-fernando-dac.js b/packages/mds-test-data/test-areas/san-fernando-dac.ts similarity index 99% rename from packages/ladot-service-areas/san-fernando-dac.js rename to packages/mds-test-data/test-areas/san-fernando-dac.ts index 9f3931923..c755d130c 100644 --- a/packages/ladot-service-areas/san-fernando-dac.js +++ b/packages/mds-test-data/test-areas/san-fernando-dac.ts @@ -1,4 +1,6 @@ -module.exports = { +import { FeatureCollection } from 'geojson' + +export default { type: 'FeatureCollection', crs: { type: 'name', @@ -665,4 +667,4 @@ module.exports = { } } ] -} +} as FeatureCollection diff --git a/packages/ladot-service-areas/service-area-new.js b/packages/mds-test-data/test-areas/service-area-new.ts similarity index 89% rename from packages/ladot-service-areas/service-area-new.js rename to packages/mds-test-data/test-areas/service-area-new.ts index 3d6eab625..d3789578e 100644 --- a/packages/ladot-service-areas/service-area-new.js +++ b/packages/mds-test-data/test-areas/service-area-new.ts @@ -1,4 +1,6 @@ -module.exports = { +import { FeatureCollection } from 'geojson' + +export default { type: 'FeatureCollection', features: [ { @@ -22,4 +24,4 @@ module.exports = { } } ] -} +} as FeatureCollection diff --git a/packages/ladot-service-areas/service-area-old.js b/packages/mds-test-data/test-areas/service-area-old.ts similarity index 86% rename from packages/ladot-service-areas/service-area-old.js rename to packages/mds-test-data/test-areas/service-area-old.ts index c3e790bd8..008daafe0 100644 --- a/packages/ladot-service-areas/service-area-old.js +++ b/packages/mds-test-data/test-areas/service-area-old.ts @@ -1,4 +1,6 @@ -module.exports = { +import { FeatureCollection } from 'geojson' + +export default { type: 'FeatureCollection', features: [ { @@ -19,4 +21,4 @@ module.exports = { } } ] -} +} as FeatureCollection diff --git a/packages/ladot-service-areas/service-areas.js b/packages/mds-test-data/test-areas/service-areas.ts similarity index 99% rename from packages/ladot-service-areas/service-areas.js rename to packages/mds-test-data/test-areas/service-areas.ts index 585c95f03..56df321d8 100644 --- a/packages/ladot-service-areas/service-areas.js +++ b/packages/mds-test-data/test-areas/service-areas.ts @@ -1,6 +1,8 @@ // data obtained from https://data.lacity.org/A-Well-Run-City/Council-Districts/5v3h-vptv -module.exports = { +import { FeatureCollection } from 'geojson' + +export default { type: 'FeatureCollection', features: [ { @@ -21697,4 +21699,4 @@ module.exports = { } } ] -} +} as FeatureCollection diff --git a/packages/ladot-service-areas/ladot-service-areas.js b/packages/mds-test-data/test-areas/test-areas.ts similarity index 66% rename from packages/ladot-service-areas/ladot-service-areas.js rename to packages/mds-test-data/test-areas/test-areas.ts index dfc2ea756..6d360b5c7 100644 --- a/packages/ladot-service-areas/ladot-service-areas.js +++ b/packages/mds-test-data/test-areas/test-areas.ts @@ -1,11 +1,24 @@ -const laCityBoundary = require('./la-city-boundary') -const restrictedAreas = require('./restricted-areas') -const laDacs = require('./la-dacs') -const veniceSpecOps = require('./venice-special-ops-zone') -const venice = require('./venice') -const councilDistrict11 = require('./council-district-11') +import { UUID, Nullable } from '@mds-core/mds-types' +import { Geometry } from 'geojson' -const serviceAreaMap = { +import laCityBoundary from './la-city-boundary' +import restrictedAreas from './restricted-areas' +import laDacs from './la-dacs' +import veniceSpecOps from './venice-special-ops-zone' +import venice from './venice' +import councilDistrict11 from './council-district-11' + +const serviceAreaMap: { + [key: string]: { + start_date: number + end_date: Nullable + prev_area: Nullable + replacement_area: Nullable + type: string + description: string + area: Geometry + } +} = { // LA city boundary '1f943d59-ccc9-4d91-b6e2-0c5e771cbc49': { start_date: 0, @@ -95,31 +108,4 @@ const serviceAreaMap = { } } -function readServiceAreas(provider_id, service_area_id) { - // ignore provider_id for now - return new Promise(resolve => { - // see if service_area_id is non-null - - if (typeof service_area_id === 'string') { - const service_area_record = serviceAreaMap[service_area_id] - if (service_area_record) { - service_area_record.service_area_id = service_area_id - resolve([service_area_record]) - } else { - resolve(null) - } - } else { - const areas = Object.keys(serviceAreaMap).map(key => { - const service_area_record = serviceAreaMap[key] - service_area_record.service_area_id = key - return service_area_record - }) - resolve(areas) - } - }) -} - -module.exports = { - readServiceAreas, - serviceAreaMap -} +export { serviceAreaMap, restrictedAreas, veniceSpecOps } diff --git a/packages/ladot-service-areas/venice-special-ops-zone.js b/packages/mds-test-data/test-areas/venice-special-ops-zone.ts similarity index 98% rename from packages/ladot-service-areas/venice-special-ops-zone.js rename to packages/mds-test-data/test-areas/venice-special-ops-zone.ts index 40820d705..d8f0ee750 100644 --- a/packages/ladot-service-areas/venice-special-ops-zone.js +++ b/packages/mds-test-data/test-areas/venice-special-ops-zone.ts @@ -1,4 +1,6 @@ -module.exports = { +import { FeatureCollection } from 'geojson' + +export default { type: 'FeatureCollection', crs: { type: 'name', properties: { name: 'EPSG:4326' } }, features: [ @@ -294,4 +296,4 @@ module.exports = { } } ] -} +} as FeatureCollection diff --git a/packages/ladot-service-areas/venice.js b/packages/mds-test-data/test-areas/venice.ts similarity index 99% rename from packages/ladot-service-areas/venice.js rename to packages/mds-test-data/test-areas/venice.ts index f56da0585..c132c1823 100644 --- a/packages/ladot-service-areas/venice.js +++ b/packages/mds-test-data/test-areas/venice.ts @@ -1,4 +1,6 @@ -module.exports = { +import { FeatureCollection } from 'geojson' + +export default { type: 'FeatureCollection', crs: { type: 'name', properties: { name: 'EPSG:4326' } }, features: [ @@ -271,4 +273,4 @@ module.exports = { properties: { OBJECTID_1: 1, OBJECTID: 98, name: 'Venice' } } ] -} +} as FeatureCollection diff --git a/packages/mds-test-data/tsconfig.build.json b/packages/mds-test-data/tsconfig.build.json index 768fcdefe..85898afcf 100644 --- a/packages/mds-test-data/tsconfig.build.json +++ b/packages/mds-test-data/tsconfig.build.json @@ -4,6 +4,8 @@ "outDir": "dist" }, "references": [ + { "path": "../../packages/mds-logger/tsconfig.build.json" }, + { "path": "../../packages/mds-providers/tsconfig.build.json" }, { "path": "../../packages/mds-types/tsconfig.build.json" }, { "path": "../../packages/mds-utils/tsconfig.build.json" } ] diff --git a/packages/mds-test-data/tsconfig.eslint.json b/packages/mds-test-data/tsconfig.eslint.json new file mode 100644 index 000000000..2a400942a --- /dev/null +++ b/packages/mds-test-data/tsconfig.eslint.json @@ -0,0 +1 @@ +{ "extends": "../../tsconfig.eslint.json" } diff --git a/packages/mds-types/index.ts b/packages/mds-types/index.ts index 12fe424d1..9216d33c3 100644 --- a/packages/mds-types/index.ts +++ b/packages/mds-types/index.ts @@ -15,20 +15,20 @@ */ import { FeatureCollection } from 'geojson' -export { AccessTokenScope, AccessTokenScopes, ScopeDescriptions } from './scopes' - export const Enum = (...keys: T[]) => - Object.freeze(keys.reduce((e, key) => { - return { ...e, [key]: key } - }, {}) as { [K in T]: K }) + Object.freeze( + keys.reduce((e, key) => { + return { ...e, [key]: key } + }, {}) as { [K in T]: K } + ) export const isEnum = (enums: { [key: string]: string }, value: unknown) => typeof value === 'string' && typeof enums === 'object' && enums[value] === value -export const VEHICLE_TYPES = Enum('carshare', 'bicycle', 'scooter', 'recumbent') +export const VEHICLE_TYPES = Enum('car', 'bicycle', 'scooter', 'moped', 'recumbent') export type VEHICLE_TYPE = keyof typeof VEHICLE_TYPES -export const RULE_TYPES = Enum('count', 'speed', 'time') +export const RULE_TYPES = Enum('count', 'speed', 'time', 'user') export type RULE_TYPE = keyof typeof RULE_TYPES export const RULE_UNIT_MAP = { @@ -42,6 +42,8 @@ export type PROPULSION_TYPE = keyof typeof PROPULSION_TYPES export const VEHICLE_STATUSES = Enum('available', 'reserved', 'unavailable', 'removed', 'inactive', 'trip', 'elsewhere') export type VEHICLE_STATUS = keyof typeof VEHICLE_STATUSES +export const RIGHT_OF_WAY_STATUSES = ['available', 'reserved', 'unavailable', 'trip'] + export const VEHICLE_EVENTS = Enum( 'register', 'service_start', @@ -58,6 +60,7 @@ export const VEHICLE_EVENTS = Enum( 'trip_end', 'deregister' ) + export type VEHICLE_EVENT = keyof typeof VEHICLE_EVENTS export const VEHICLE_REASONS = Enum( @@ -73,25 +76,6 @@ export const VEHICLE_REASONS = Enum( ) export type VEHICLE_REASON = keyof typeof VEHICLE_REASONS -export const PROVIDER_EVENTS = Enum('available', 'reserved', 'unavailable', 'removed') -export type PROVIDER_EVENT = keyof typeof PROVIDER_EVENTS - -export const PROVIDER_REASONS = Enum( - 'service_start', - 'user_drop_off', - 'rebalance_drop_off', - 'maintenance_drop_off', - 'agency_drop_off', - 'user_pick_up', - 'maintenance', - 'low_battery', - 'service_end', - 'rebalance_pick_up', - 'maintenance_pick_up', - 'agency_pick_up' -) -export type PROVIDER_REASON = keyof typeof PROVIDER_REASONS - export const AUDIT_EVENT_TYPES = Enum('start', 'note', 'summary', 'issue', 'telemetry', 'end') export type AUDIT_EVENT_TYPE = keyof typeof AUDIT_EVENT_TYPES @@ -143,6 +127,12 @@ export type UUID = string export type Timestamp = number export type Stringify = { [P in keyof T]: string } +export type Nullable = T | null +export type NullableProperties = { + [P in keyof T]-?: T[P] extends null ? T[P] : Nullable +} +export type SingleOrArray = T | T[] +export type Optional = Omit & Partial> // Represents a row in the "devices" table export interface Device { @@ -177,12 +167,6 @@ export interface VehicleEvent { recorded: Timestamp } -export interface VehicleEventSummary { - provider_event_id: number | null - provider_event_type: VEHICLE_EVENT | null - provider_event_type_reason?: VEHICLE_REASON | null -} - // Standard telemetry columns (used in more than one table) export interface TelemetryData { lat: number @@ -196,6 +180,8 @@ export interface TelemetryData { charge?: number | null } +export type GpsData = Omit + // While telemetry data is stored in a flattened format, when passed as a parameter it has // a different shape: { gps: { lat, lng, speed, heading, accurace, altitude } charge }. This // type alias defines the parameter shape using the types of the underlying flattened data. @@ -211,6 +197,23 @@ export interface Telemetry extends WithGpsProperty { recorded?: Timestamp } +// Represents a row in the "attachments" table +export interface Attachment { + attachment_filename: string + attachment_id: UUID + base_url: string + mimetype: string + thumbnail_filename?: string | null + thumbnail_mimetype?: string | null + recorded?: Timestamp | null +} + +export interface AttachmentSummary { + attachment_id: UUID + attachment_url: string + thumbnail_url?: string | null +} + // Represents a row in the "audits" table export interface Audit { audit_trip_id: UUID @@ -224,6 +227,13 @@ export interface Audit { recorded: Timestamp } +// Represents a row in the "audit_attachments" table +export interface AuditAttachment { + attachment_id: UUID + audit_trip_id: UUID + recorded: Timestamp +} + // Represents a row in the "audit_events" table export interface AuditEvent extends TelemetryData { audit_trip_id: UUID @@ -232,15 +242,18 @@ export interface AuditEvent extends TelemetryData { audit_issue_code?: string | null audit_subject_id: string note?: string | null - provider_event_id?: number | null - provider_event_type?: string | null - provider_event_type_reason?: string | null timestamp: Timestamp recorded: Timestamp } export interface AuditDetails extends Audit { events: WithGpsProperty[] + provider_event_type?: string | null + provider_event_type_reason?: string | null + provider_status?: string | null + provider_telemetry?: Telemetry | null + provider_event_time?: Timestamp | null + attachments: AttachmentSummary[] provider: null | { device: Device events: VehicleEvent[] @@ -248,6 +261,10 @@ export interface AuditDetails extends Audit { } } +export interface PolicyMessage { + [key: string]: string +} + interface BaseRule { name: string rule_id: UUID @@ -262,7 +279,7 @@ interface BaseRule { days?: DAY_OF_WEEK[] | null /* eslint-reason TODO: message types haven't been defined well yet */ /* eslint-disable-next-line @typescript-eslint/no-explicit-any */ - messages?: any + messages?: PolicyMessage value_url?: URL | null } @@ -276,7 +293,9 @@ export interface SpeedRule extends BaseRule<'speed'> { rule_units: 'kph' | 'mph' } -export type Rule = CountRule | TimeRule | SpeedRule +export type UserRule = BaseRule<'user'> + +export type Rule = CountRule | TimeRule | SpeedRule | UserRule export interface Policy { name: string @@ -401,3 +420,43 @@ export interface Provider { mds_api_url?: string gbfs_api_url?: string } + +export interface Stop { + stop_id: UUID + stop_name: string + short_name?: string + platform_code?: string + geography_id?: UUID + lat: number + lng: number + zone_id?: UUID + address?: string + post_code?: string + rental_methods?: string // TOOD: enum? + capacity: Partial<{ [S in VEHICLE_TYPE]: number }> + location_type?: string // TODO: enum? + timezone?: string + cross_street?: string + num_vehicles_available: Partial<{ [S in VEHICLE_TYPE]: number }> + num_vehicles_disabled?: Partial<{ [S in VEHICLE_TYPE]: number }> + num_spots_available: Partial<{ [S in VEHICLE_TYPE]: number }> + num_spots_disabled?: Partial<{ [S in VEHICLE_TYPE]: number }> + wheelchair_boarding?: boolean + reservation_cost?: Partial<{ [S in VEHICLE_TYPE]: number }> // Cost to reserve a spot per vehicle_type +} + +// eslint-reason recursive declarations require interfaces +// eslint-disable-next-line @typescript-eslint/no-empty-interface +export interface JsonArray extends Array {} + +export interface JsonObject { + [property: string]: Json +} + +export type JsonValue = string | number | boolean | JsonArray | JsonObject + +export type Json = Nullable +// eslint-reason Function and constructor inference must use a single rest parameter of type 'any[]' +/* eslint-disable @typescript-eslint/no-explicit-any */ +export type AnyFunction = (...args: any[]) => A +export type AnyConstructor = new (...args: any[]) => A diff --git a/packages/mds-types/package.json b/packages/mds-types/package.json index 3083b097a..30a658d5e 100644 --- a/packages/mds-types/package.json +++ b/packages/mds-types/package.json @@ -1,6 +1,6 @@ { "name": "@mds-core/mds-types", - "version": "0.1.15", + "version": "0.1.23", "description": "Mobility Data Specification common types", "main": "dist/index.js", "types": "dist/index.d.ts", @@ -15,7 +15,10 @@ "scripts": { "build": "tsc --build tsconfig.build.json", "test": "yarn test:eslint && yarn test:unit", - "test:eslint": "eslint --fix --ignore-path ../../.gitignore '**/*.ts'", - "test:unit": "DOTENV_CONFIG_PATH=../../.env nyc --check-coverage --exclude tests --extension .ts --lines 90 --reporter=text --reporter=html ts-mocha --project ../../tsconfig.json --require tsconfig-paths/register --require source-map-support/register --require dotenv/config --recursive --timeout 5000 tests/**/*.ts" + "test:eslint": "eslint --ignore-path ../../.gitignore '**/*.ts'", + "test:unit": "DOTENV_CONFIG_PATH=../../.env nyc ts-mocha --project ../../tsconfig.json" + }, + "dependencies": { + "@types/geojson": "7946.0.7" } } diff --git a/packages/mds-types/scopes.ts b/packages/mds-types/scopes.ts deleted file mode 100644 index bceb54508..000000000 --- a/packages/mds-types/scopes.ts +++ /dev/null @@ -1,51 +0,0 @@ -// Canonical list of MDS scopes -export const AccessTokenScopes = [ - 'admin:all', - 'audits:delete', - 'audits:read', - 'audits:vehicles:read', - 'audits:write', - 'compliance:read', - 'compliance:read:provider', - 'events:read', - 'events:read:provider', - 'events:write:provider', - 'policies:delete', - 'policies:publish', - 'policies:read', - 'policies:write', - 'providers:read', - 'service_areas:read', - 'status_changes:read', - 'telemetry:write:provider', - 'trips:read', - 'vehicles:read', - 'vehicles:read:provider', - 'vehicles:write:provider' -] as const -export type AccessTokenScope = typeof AccessTokenScopes[number] - -export const ScopeDescriptions: { [S in AccessTokenScope]: string } = { - 'admin:all': 'Administrator Access', - 'audits:delete': 'Delete Audits', - 'audits:read': 'Read Audits', - 'audits:vehicles:read': 'Read Vehicles (Audit Access)', - 'audits:write': 'Write Audits', - 'compliance:read': 'Read Compliance', - 'compliance:read:provider': 'Read Compliance (Provider Access)', - 'events:read': 'Read Events', - 'events:read:provider': 'Read Events (Provider Access)', - 'events:write:provider': 'Write Events (Provider Access)', - 'policies:delete': 'Delete Policies', - 'policies:publish': 'Publish Policies', - 'policies:read': 'Read Policies', - 'policies:write': 'Write Policies', - 'providers:read': 'Read Providers', - 'service_areas:read': 'Read Service Areas', - 'status_changes:read': 'Read Status Changes', - 'telemetry:write:provider': 'Write Telemetry (Provider Access)', - 'trips:read': 'Read Trips', - 'vehicles:read': 'Read Vehicles', - 'vehicles:read:provider': 'Read Vehicles (Provider Access)', - 'vehicles:write:provider': 'Write Vehicles (Provider Access)' -} diff --git a/packages/mds-types/tsconfig.eslint.json b/packages/mds-types/tsconfig.eslint.json new file mode 100644 index 000000000..2a400942a --- /dev/null +++ b/packages/mds-types/tsconfig.eslint.json @@ -0,0 +1 @@ +{ "extends": "../../tsconfig.eslint.json" } diff --git a/packages/mds-utils/date-time-utils.ts b/packages/mds-utils/date-time-utils.ts new file mode 100644 index 000000000..8476fa4d4 --- /dev/null +++ b/packages/mds-utils/date-time-utils.ts @@ -0,0 +1,148 @@ +import { Timestamp } from '@mds-core/mds-types' +import moment from 'moment-timezone' +import { BadParamsError } from './exceptions/exceptions' + +export const getCurrentDate = () => { + return new Date() +} + +export const getLocalTime = () => moment(getCurrentDate()).tz(process.env.TIMEZONE || 'America/Los_Angeles') + +const parseOperator = (offset: string): '+' | '-' => { + if (offset === 'today' || offset === 'yesterday' || offset === 'now') { + return '+' + } + + const operator = offset[0] + + if (operator !== '+' && operator !== '-') { + throw new BadParamsError(`Invalid time offset operator: ${offset}, ${operator}`) + } + + return operator +} + +const parseCount = (offset: string) => { + if (offset === 'today' || offset === 'now') { + return 0 + } + if (offset === 'yesterday') { + return 1 + } + + const count = Number(offset.slice(1, -1)) + if (Number.isNaN(count)) { + throw new BadParamsError(`Invalid time offset count: ${offset}, ${count}`) + } + return count +} + +const parseUnit = (offset: string): 'days' | 'hours' => { + const shorthand = offset.slice(-1) + const shorthandToUnit: { + [key: string]: 'days' | 'hours' + } = { + d: 'days', + h: 'hours' + } + if (offset === 'today' || offset === 'yesterday' || offset === 'now') { + return 'days' + } + const unit = shorthandToUnit[shorthand] + if (unit === undefined) { + throw new BadParamsError(`Invalid offset unit shorthand: ${offset}, ${shorthand}`) + } + return unit +} + +const parseIsRelative = (offset: string): boolean => { + if (offset === 'today' || offset === 'yesterday' || offset === 'now') { + return false + } + return true +} + +const parseOffset = ( + offset: string +): { + unit: 'days' | 'hours' + operator: '+' | '-' + count: number + relative: boolean +} => { + const operator = parseOperator(offset) + const count = parseCount(offset) + const unit = parseUnit(offset) + const relative = parseIsRelative(offset) + + return { + unit, + operator, + count, + relative + } +} + +const parseAnchorPoint = (offset: string) => { + const localTime = getLocalTime() + if (offset === 'today') { + return localTime.startOf('day') + } + if (offset === 'now') { + return localTime.startOf('hour') + } + if (offset === 'yesterday') { + return localTime.startOf('day').subtract(1, 'days') + } + throw new BadParamsError(`Invalid anchor point: ${offset}`) +} + +const parseRelative = ( + startOffset: string, + endOffset: string +): { + start_time: Timestamp + end_time: Timestamp +} => { + const parsedStartOffset = parseOffset(startOffset) + const parsedEndOffset = parseOffset(endOffset) + + if (!parsedStartOffset.relative && !parsedEndOffset.relative) { + return { + start_time: parseAnchorPoint(startOffset).valueOf(), + end_time: parseAnchorPoint(endOffset).valueOf() + } + } + + if (parsedStartOffset.relative && parsedEndOffset.relative) { + throw new BadParamsError(`Both start_offset and end_offset cannot be relative to each other`) + } + + if (parsedStartOffset.relative) { + const anchorPoint = parseAnchorPoint(endOffset) + const { operator, unit, count } = parsedStartOffset + if (operator === '-') { + return { + start_time: anchorPoint.clone().subtract(count, unit).valueOf(), + end_time: anchorPoint.valueOf() + } + } + throw new BadParamsError(`Invalid starting point: ${startOffset}`) + } + + if (parsedEndOffset.relative) { + const anchorPoint = parseAnchorPoint(startOffset) + const { operator, unit, count } = parsedEndOffset + if (operator === '+') { + return { + start_time: anchorPoint.valueOf(), + end_time: anchorPoint.clone().add(count, unit).valueOf() + } + } + throw new BadParamsError(`Invalid ending point: ${endOffset}`) + } + + throw new BadParamsError(`Both start_offset and end_offset cannot be relative to each other`) +} + +export { parseOperator, parseCount, parseUnit, parseOffset, parseAnchorPoint, parseRelative, parseIsRelative } diff --git a/packages/mds-utils/exceptions/exception-messages.ts b/packages/mds-utils/exceptions/exception-messages.ts new file mode 100644 index 000000000..394b6cc53 --- /dev/null +++ b/packages/mds-utils/exceptions/exception-messages.ts @@ -0,0 +1,3 @@ +export const ExceptionMessages = { + INITIALIZE_CLIENT_MESSAGE: 'Client is not connected! Have you tried initializing the client with .initialize()?' +} diff --git a/packages/mds-utils/exceptions.ts b/packages/mds-utils/exceptions/exceptions.ts similarity index 52% rename from packages/mds-utils/exceptions.ts rename to packages/mds-utils/exceptions/exceptions.ts index 52b97cbf7..41255b964 100644 --- a/packages/mds-utils/exceptions.ts +++ b/packages/mds-utils/exceptions/exceptions.ts @@ -10,54 +10,84 @@ const reason = (error?: Error | string) => (error instanceof Error ? error.messa /* istanbul ignore next */ export class ServerError extends BaseError { public constructor(error?: Error | string, public info?: unknown) { - super('ServerError', reason(error)) + super('ServerError', reason(error), info) } } /* istanbul ignore next */ export class NotFoundError extends BaseError { public constructor(error?: Error | string, public info?: unknown) { - super('NotFoundError', reason(error)) + super('NotFoundError', reason(error), info) } } /* istanbul ignore next */ export class ConflictError extends BaseError { public constructor(error?: Error | string, public info?: unknown) { - super('ConflictError', reason(error)) + super('ConflictError', reason(error), info) } } /* istanbul ignore next */ export class AuthorizationError extends BaseError { public constructor(error?: Error | string, public info?: unknown) { - super('AuthorizationError', reason(error)) + super('AuthorizationError', reason(error), info) } } /* istanbul ignore next */ export class RuntimeError extends BaseError { public constructor(error?: Error | string, public info?: unknown) { - super('RuntimeError', reason(error)) + super('RuntimeError', reason(error), info) } } /* istanbul ignore next */ export class ValidationError extends BaseError { public constructor(error?: Error | string, public info?: unknown) { - super('ValidationError', reason(error)) + super('ValidationError', reason(error), info) } } /* istanbul ignore next */ export class BadParamsError extends BaseError { public constructor(error?: Error | string, public info?: unknown) { - super('BadParamsError', reason(error)) + super('BadParamsError', reason(error), info) } } export class AlreadyPublishedError extends BaseError { public constructor(error?: Error | string, public info?: unknown) { - super('AlreadyPublishedError', reason(error)) + super('AlreadyPublishedError', reason(error), info) + } +} + +export class UnsupportedTypeError extends BaseError { + public constructor(error?: Error | string, public info?: unknown) { + super('UnsupportedTypeError', reason(error), info) + } +} + +export class ParseError extends BaseError { + public constructor(error?: Error | string, public info?: unknown) { + super('ParseError', reason(error), info) + } +} + +export class DependencyMissingError extends BaseError { + public constructor(error?: Error | string, public info?: unknown) { + super('DependencyMissingError', reason(error), info) + } +} + +export class InsufficientPermissionsError extends BaseError { + public constructor(error?: Error | string, public info?: unknown) { + super('InsufficientPermissionsError', reason(error), info) + } +} + +export class ClientDisconnectedError extends BaseError { + public constructor(error?: Error | string, public info?: unknown) { + super('ClientDisconnectedError', reason(error), info) } } diff --git a/packages/mds-utils/index.ts b/packages/mds-utils/index.ts index 70e0b53e2..57e2fe138 100644 --- a/packages/mds-utils/index.ts +++ b/packages/mds-utils/index.ts @@ -1,3 +1,8 @@ -export * from './exceptions' +import { v4 } from 'uuid' +import { UUID } from '@mds-core/mds-types' + +export * from './exceptions/exceptions' +export * from './exceptions/exception-messages' export * from './utils' -export * from './validators' + +export const uuid = (): UUID => v4() diff --git a/packages/mds-utils/package.json b/packages/mds-utils/package.json index a51912b1a..8b857bbfd 100644 --- a/packages/mds-utils/package.json +++ b/packages/mds-utils/package.json @@ -1,6 +1,6 @@ { "name": "@mds-core/mds-utils", - "version": "0.1.18", + "version": "0.1.26", "description": "Mobility Data Specification utility functions", "keywords": [ "mds", @@ -9,10 +9,17 @@ "author": "City of Los Angeles", "license": "Apache-2.0", "dependencies": { - "@hapi/joi": "15.1.1", - "@mds-core/mds-types": "0.1.15", - "circle-to-polygon": "1.0.2", - "point-in-polygon": "1.0.1" + "@mds-core/mds-logger": "0.1.24", + "@mds-core/mds-types": "0.1.23", + "@turf/boolean-point-in-polygon": "^6.0.1", + "@turf/helpers": "^6.1.4", + "@types/moment-timezone": "0.5.13", + "@types/point-in-polygon": "1.0.0", + "@types/uuid": "7.0.3", + "circle-to-polygon": "2.0.2", + "moment-timezone": "0.5.28", + "point-in-polygon": "1.0.1", + "uuid": "8.0.0" }, "main": "dist/index.js", "types": "dist/index.d.ts", @@ -22,7 +29,7 @@ "scripts": { "build": "tsc --build tsconfig.build.json", "test": "yarn test:eslint && yarn test:unit", - "test:eslint": "eslint --fix --ignore-path ../../.gitignore '**/*.ts'", - "test:unit": "DOTENV_CONFIG_PATH=../../.env nyc --check-coverage --exclude tests --extension .ts --lines 35 --reporter=text --reporter=html ts-mocha --project ../../tsconfig.json --require tsconfig-paths/register --require source-map-support/register --require dotenv/config --recursive --timeout 5000 tests/**/*.ts" + "test:eslint": "eslint --ignore-path ../../.gitignore '**/*.ts'", + "test:unit": "DOTENV_CONFIG_PATH=../../.env nyc --lines 40 ts-mocha --project ../../tsconfig.json" } } diff --git a/packages/mds-utils/state-machine.ts b/packages/mds-utils/state-machine.ts new file mode 100644 index 000000000..9bc198a11 --- /dev/null +++ b/packages/mds-utils/state-machine.ts @@ -0,0 +1,76 @@ +import { VEHICLE_STATUSES, VEHICLE_EVENTS, VEHICLE_STATUS, VEHICLE_EVENT } from '@mds-core/mds-types' + +const stateTransitionDict: { + [S in VEHICLE_STATUS]: Partial< + { + [E in VEHICLE_EVENT]: VEHICLE_STATUS + } + > +} = { + [VEHICLE_STATUSES.available]: { + [VEHICLE_EVENTS.deregister]: VEHICLE_STATUSES.inactive, + [VEHICLE_EVENTS.agency_pick_up]: VEHICLE_STATUSES.removed, + [VEHICLE_EVENTS.service_end]: VEHICLE_STATUSES.unavailable, + [VEHICLE_EVENTS.trip_start]: VEHICLE_STATUSES.trip + }, + [VEHICLE_STATUSES.elsewhere]: { + [VEHICLE_EVENTS.trip_enter]: VEHICLE_STATUSES.trip, + [VEHICLE_EVENTS.provider_pick_up]: VEHICLE_STATUSES.removed, + [VEHICLE_EVENTS.deregister]: VEHICLE_STATUSES.inactive, + [VEHICLE_EVENTS.provider_drop_off]: VEHICLE_STATUSES.available + }, + [VEHICLE_STATUSES.inactive]: { + [VEHICLE_EVENTS.register]: VEHICLE_STATUSES.removed + }, + [VEHICLE_STATUSES.removed]: { + [VEHICLE_EVENTS.trip_enter]: VEHICLE_STATUSES.trip, + [VEHICLE_EVENTS.provider_drop_off]: VEHICLE_STATUSES.available, + [VEHICLE_EVENTS.deregister]: VEHICLE_STATUSES.inactive + }, + [VEHICLE_STATUSES.reserved]: { + [VEHICLE_EVENTS.trip_start]: VEHICLE_STATUSES.trip, + [VEHICLE_EVENTS.cancel_reservation]: VEHICLE_STATUSES.available + }, + [VEHICLE_STATUSES.trip]: { + [VEHICLE_EVENTS.trip_leave]: VEHICLE_STATUSES.elsewhere, + [VEHICLE_EVENTS.trip_end]: VEHICLE_STATUSES.available + }, + [VEHICLE_STATUSES.unavailable]: { + [VEHICLE_EVENTS.service_start]: VEHICLE_STATUSES.available, + [VEHICLE_EVENTS.deregister]: VEHICLE_STATUSES.inactive, + [VEHICLE_EVENTS.agency_pick_up]: VEHICLE_STATUSES.removed, + [VEHICLE_EVENTS.provider_pick_up]: VEHICLE_STATUSES.removed + } +} + +const getNextState = (currStatus: VEHICLE_STATUS, nextEvent: VEHICLE_EVENT): VEHICLE_STATUS | undefined => { + return stateTransitionDict[currStatus]?.[nextEvent] +} + +const generateTransitionLabel = ( + status: VEHICLE_STATUS, + nextStatus: VEHICLE_STATUS, + transitionEvent: VEHICLE_EVENT +) => { + return `${status} -> ${nextStatus} [ label = ${transitionEvent} ]` +} + +// Punch this output into http://www.webgraphviz.com/ +const generateGraph = () => { + const graphEntries = [] + const statuses: VEHICLE_STATUS[] = Object.values(VEHICLE_STATUSES) + for (const status of statuses) { + const eventTransitions: VEHICLE_EVENT[] = Object.keys(stateTransitionDict[status]) as VEHICLE_EVENT[] + for (const event of eventTransitions) { + if (event) { + const nextStatus: VEHICLE_STATUS | undefined = stateTransitionDict[status][event] + if (nextStatus) { + graphEntries.push(`\t${generateTransitionLabel(status, nextStatus, event)}`) + } + } + } + } + return `digraph G {\n${graphEntries.join('\n')}\n}` +} + +export { stateTransitionDict, getNextState, generateGraph } diff --git a/packages/mds-utils/tests/date-time-utils.spec.ts b/packages/mds-utils/tests/date-time-utils.spec.ts new file mode 100644 index 000000000..b6219c0f3 --- /dev/null +++ b/packages/mds-utils/tests/date-time-utils.spec.ts @@ -0,0 +1,73 @@ +import assert from 'assert' +// import Sinon from 'sinon' +import moment from 'moment-timezone' +import Sinon from 'sinon' +import * as dt from '../date-time-utils' + +describe('Date/time API utils', () => { + describe('parses operators', () => { + it('Gets the operator', () => { + assert.strictEqual(dt.parseOperator('+5d'), '+') + assert.strictEqual(dt.parseOperator('-49d'), '-') + assert.strictEqual(dt.parseOperator('today'), '+') + assert.strictEqual(dt.parseOperator('yesterday'), '+') + assert.strictEqual(dt.parseOperator('now'), '+') + }) + + it('Rejects malformed strings', () => { + assert.throws(() => dt.parseOperator('bad-offset')) + }) + }) + + describe('parses counts', () => { + it('parses counts', () => { + assert.strictEqual(dt.parseCount('+5d'), 5) + assert.strictEqual(dt.parseCount('-49d'), 49) + assert.strictEqual(dt.parseCount('today'), 0) + assert.strictEqual(dt.parseCount('yesterday'), 1) + assert.strictEqual(dt.parseCount('now'), 0) + }) + + it('Rejects malformed strings', () => { + assert.throws(() => dt.parseCount('bad-offset')) + }) + }) + + describe('parses units', () => { + it('parses units', () => { + assert.strictEqual(dt.parseUnit('+5d'), 'days') + assert.strictEqual(dt.parseUnit('-49d'), 'days') + assert.strictEqual(dt.parseUnit('-49h'), 'hours') + assert.strictEqual(dt.parseUnit('today'), 'days') + assert.strictEqual(dt.parseUnit('yesterday'), 'days') + assert.strictEqual(dt.parseUnit('now'), 'days') + }) + + it('Rejects malformed strings', () => { + assert.throws(() => dt.parseUnit('bad-offset')) + }) + }) + + describe('parses is relative', () => { + it('parses is relative', () => { + assert.strictEqual(dt.parseIsRelative('+5d'), true) + assert.strictEqual(dt.parseIsRelative('-49d'), true) + assert.strictEqual(dt.parseIsRelative('-49h'), true) + assert.strictEqual(dt.parseIsRelative('today'), false) + assert.strictEqual(dt.parseIsRelative('yesterday'), false) + assert.strictEqual(dt.parseIsRelative('now'), false) + }) + }) + + describe('parses relative dates', () => { + it('parses -7d/yesterday', () => { + Sinon.replace(dt, 'getLocalTime', Sinon.fake.returns(moment('2020-01-15').clone())) + const result = dt.parseRelative('-7d', 'yesterday') + assert.deepStrictEqual(result, { + start_time: moment('2020-01-15').clone().subtract(8, 'd').valueOf(), + end_time: moment('2020-01-15').clone().subtract(1, 'd').valueOf() + }) + Sinon.restore() + }) + }) +}) diff --git a/packages/mds-utils/tests/state-transition-expected.ts b/packages/mds-utils/tests/state-transition-expected.ts new file mode 100644 index 000000000..593749736 --- /dev/null +++ b/packages/mds-utils/tests/state-transition-expected.ts @@ -0,0 +1,232 @@ +import { VEHICLE_EVENT } from '@mds-core/mds-types' + +export const expectedTransitions: { + [A in VEHICLE_EVENT]: { + [B in VEHICLE_EVENT]: boolean + } +} = { + register: { + register: false, + service_start: false, + service_end: false, + provider_drop_off: true, + provider_pick_up: false, + agency_pick_up: false, + agency_drop_off: false, + reserve: false, + cancel_reservation: false, + trip_start: false, + trip_enter: true, + trip_leave: false, + trip_end: false, + deregister: true + }, + service_start: { + register: false, + service_start: false, + service_end: true, + provider_drop_off: false, + provider_pick_up: false, + agency_pick_up: true, + agency_drop_off: false, + reserve: false, + cancel_reservation: false, + trip_start: true, + trip_enter: false, + trip_leave: false, + trip_end: false, + deregister: true + }, + service_end: { + register: false, + service_start: true, + service_end: false, + provider_drop_off: false, + provider_pick_up: true, + agency_pick_up: true, + agency_drop_off: false, + reserve: false, + cancel_reservation: false, + trip_start: false, + trip_enter: false, + trip_leave: false, + trip_end: false, + deregister: true + }, + provider_drop_off: { + register: false, + service_start: false, + service_end: true, + provider_drop_off: false, + provider_pick_up: false, + agency_pick_up: true, + agency_drop_off: false, + reserve: false, + cancel_reservation: false, + trip_start: true, + trip_enter: false, + trip_leave: false, + trip_end: false, + deregister: true + }, + provider_pick_up: { + register: false, + service_start: false, + service_end: false, + provider_drop_off: true, + provider_pick_up: false, + agency_pick_up: false, + agency_drop_off: false, + reserve: false, + cancel_reservation: false, + trip_start: false, + trip_enter: true, + trip_leave: false, + trip_end: false, + deregister: true + }, + agency_pick_up: { + register: false, + service_start: false, + service_end: false, + provider_drop_off: true, + provider_pick_up: false, + agency_pick_up: false, + agency_drop_off: false, + reserve: false, + cancel_reservation: false, + trip_start: false, + trip_enter: true, + trip_leave: false, + trip_end: false, + deregister: true + }, + agency_drop_off: { + register: false, + service_start: false, + service_end: true, + provider_drop_off: false, + provider_pick_up: false, + agency_pick_up: true, + agency_drop_off: false, + reserve: false, + cancel_reservation: false, + trip_start: true, + trip_enter: false, + trip_leave: false, + trip_end: false, + deregister: true + }, + reserve: { + register: false, + service_start: false, + service_end: false, + provider_drop_off: false, + provider_pick_up: false, + agency_pick_up: false, + agency_drop_off: false, + reserve: false, + cancel_reservation: true, + trip_start: true, + trip_enter: false, + trip_leave: false, + trip_end: false, + deregister: false + }, + cancel_reservation: { + register: false, + service_start: false, + service_end: true, + provider_drop_off: false, + provider_pick_up: false, + agency_pick_up: true, + agency_drop_off: false, + reserve: false, + cancel_reservation: false, + trip_start: true, + trip_enter: false, + trip_leave: false, + trip_end: false, + deregister: true + }, + trip_start: { + register: false, + service_start: false, + service_end: false, + provider_drop_off: false, + provider_pick_up: false, + agency_pick_up: false, + agency_drop_off: false, + reserve: false, + cancel_reservation: false, + trip_start: false, + trip_enter: false, + trip_leave: true, + trip_end: true, + deregister: false + }, + trip_enter: { + register: false, + service_start: false, + service_end: false, + provider_drop_off: false, + provider_pick_up: false, + agency_pick_up: false, + agency_drop_off: false, + reserve: false, + cancel_reservation: false, + trip_start: false, + trip_enter: false, + trip_leave: true, + trip_end: true, + deregister: false + }, + trip_leave: { + register: false, + service_start: false, + service_end: false, + provider_drop_off: true, + provider_pick_up: true, + agency_pick_up: false, + agency_drop_off: false, + reserve: false, + cancel_reservation: false, + trip_start: false, + trip_enter: true, + trip_leave: false, + trip_end: false, + deregister: true + }, + trip_end: { + register: false, + service_start: false, + service_end: true, + provider_drop_off: false, + provider_pick_up: false, + agency_pick_up: true, + agency_drop_off: false, + reserve: false, + cancel_reservation: false, + trip_start: true, + trip_enter: false, + trip_leave: false, + trip_end: false, + deregister: true + }, + deregister: { + register: true, + service_start: false, + service_end: false, + provider_drop_off: false, + provider_pick_up: false, + agency_pick_up: false, + agency_drop_off: false, + reserve: false, + cancel_reservation: false, + trip_start: false, + trip_enter: false, + trip_leave: false, + trip_end: false, + deregister: false + } +} diff --git a/packages/mds-utils/tests/utils.spec.ts b/packages/mds-utils/tests/utils.spec.ts index de0c5424c..549326d68 100644 --- a/packages/mds-utils/tests/utils.spec.ts +++ b/packages/mds-utils/tests/utils.spec.ts @@ -16,7 +16,9 @@ import test from 'unit.js' import assert from 'assert' -import { routeDistance, filterEmptyHelper } from '../utils' +import { VEHICLE_EVENTS, VehicleEvent } from '@mds-core/mds-types' +import { routeDistance, isStateTransitionValid, normalizeToArray, filterDefined } from '../utils' +import { expectedTransitions } from './state-transition-expected' const Boston = { lat: 42.360081, lng: -71.058884 } const LosAngeles = { lat: 34.052235, lng: -118.243683 } @@ -41,14 +43,14 @@ describe('Tests Utilities', () => { describe('Filter empty', () => { it('Filters out null/undefined elements', () => { const arr = [1, 2, null, 3, undefined, 4] - const actual = arr.filter(filterEmptyHelper()) + const actual = arr.filter(filterDefined()) const expected = [1, 2, 3, 4] assert.deepStrictEqual(actual, expected) }) it('Does not filter 0 or "" (empty string) or [] (empty array)', () => { const arr = [1, 2, '', 3, [], 0] - const actual = arr.filter(filterEmptyHelper>()) + const actual = arr.filter(filterDefined()) const expected = arr assert.deepStrictEqual(actual, expected) }) @@ -56,18 +58,45 @@ describe('Tests Utilities', () => { // Can't seem to get TS to go along with Sinon.spy() // See https://sinonjs.org/releases/latest/spies/ - // it('Calls log.warn', () => { - // const spy = Sinon.spy(log.warn) - // const oldLogWarn = log.warn - // log.warn = spy + // it('Calls logger.warn', () => { + // const spy = Sinon.spy(logger.warn) + // const oldLogWarn = logger.warn + // logger.warn = spy // const arr = [1, 2, null, 3, undefined, 4] - // const actual = arr.filter(filterEmptyHelper()) + // const actual = arr.filter(filterDefined()) // const expected = [1, 2, 3, 4] // assert.deepStrictEqual(actual, expected) // assert.equal(spy.calledTwice, true) // Sinon.restore() - // log.warn = oldLogWarn + // logger.warn = oldLogWarn // }) }) + + describe('Normalize to array', () => { + it('Normalizes undefined to empty array', () => { + assert.deepStrictEqual(normalizeToArray(undefined), []) + }) + it('Normalizes single element into singleton array', () => { + assert.deepStrictEqual(normalizeToArray('test'), ['test']) + }) + it('Leaves array untouched', () => { + assert.deepStrictEqual(normalizeToArray(['test1', 'test2']), ['test1', 'test2']) + }) + }) + + describe('State machine', () => { + it('Tests state transitions', () => { + const events = Object.keys(VEHICLE_EVENTS) + for (const event_type_A of events) { + for (const event_type_B of events) { + const eventA = { event_type: event_type_A } as VehicleEvent + const eventB = { event_type: event_type_B } as VehicleEvent + const actual = isStateTransitionValid(eventA, eventB) + const transitionKey = `${eventA.event_type}, ${eventB.event_type}` + assert.strictEqual(actual, expectedTransitions[eventA.event_type][eventB.event_type], transitionKey) + } + } + }) + }) }) diff --git a/packages/mds-utils/tsconfig.build.json b/packages/mds-utils/tsconfig.build.json index 0bc2bf8a4..43590e1ff 100644 --- a/packages/mds-utils/tsconfig.build.json +++ b/packages/mds-utils/tsconfig.build.json @@ -3,5 +3,8 @@ "compilerOptions": { "outDir": "dist" }, - "references": [{ "path": "../../packages/mds-types/tsconfig.build.json" }] + "references": [ + { "path": "../../packages/mds-logger/tsconfig.build.json" }, + { "path": "../../packages/mds-types/tsconfig.build.json" } + ] } diff --git a/packages/mds-utils/tsconfig.eslint.json b/packages/mds-utils/tsconfig.eslint.json new file mode 100644 index 000000000..2a400942a --- /dev/null +++ b/packages/mds-utils/tsconfig.eslint.json @@ -0,0 +1 @@ +{ "extends": "../../tsconfig.eslint.json" } diff --git a/packages/mds-utils/utils.ts b/packages/mds-utils/utils.ts index d422b499b..24e3a5998 100644 --- a/packages/mds-utils/utils.ts +++ b/packages/mds-utils/utils.ts @@ -26,16 +26,19 @@ import { BoundingBox, Geography, Rule, - VEHICLE_EVENTS, - VEHICLE_STATUSES, EVENT_STATUS_MAP, VEHICLE_STATUS, - BBox + BBox, + VEHICLE_EVENT, + SingleOrArray } from '@mds-core/mds-types' -import { TelemetryRecord } from '@mds-core/mds-db/types' -import log from '@mds-core/mds-logger' +import logger from '@mds-core/mds-logger' import { MultiPolygon, Polygon, FeatureCollection, Geometry, Feature } from 'geojson' +import { isArray } from 'util' +import { getNextState } from './state-machine' +import { parseRelative, getCurrentDate } from './date-time-utils' + const RADIUS = 30.48 // 100 feet, in meters const NUMBER_OF_EDGES = 32 // Number of edges to add, geojson doesn't support real circles const UUID_REGEX = /[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}/ @@ -63,11 +66,11 @@ function isPct(val: unknown): val is number { // this is a real-time API, so timestamps should be now +/- some factor, let's start with 24h function isTimestamp(val: unknown): val is Timestamp { if (typeof val !== 'number') { - log.info('timestamp not an number') + logger.info('timestamp not an number') return false } if (val < 1420099200000) { - log.info('timestamp is prior to 1/1/2015; this is almost certainly seconds, not milliseconds') + logger.info('timestamp is prior to 1/1/2015; this is almost certainly seconds, not milliseconds') return false } return true @@ -92,6 +95,13 @@ function days(n: number) { return hours(n) * 24 } +// Based on a bin size (in ms), calculate the start/end of +// the frame containing the timestamp. +function timeframe(size: Timestamp, timestamp: Timestamp) { + const start_time = timestamp - (timestamp % size) + return { start_time, end_time: start_time + size - 1 } +} + /** * @param {number} minimum * @param {number} maximum @@ -451,31 +461,6 @@ function csv(list: T[] | Readonly): string { function inc(map: { [key: string]: number }, key: string) { return Object.assign(map, { [key]: map[key] ? map[key] + 1 : 1 }) } -function convertTelemetryToTelemetryRecord(telemetry: Telemetry): TelemetryRecord { - const { - gps: { lat, lng, altitude, heading, speed, accuracy }, - recorded = now(), - ...props - } = telemetry - return { - ...props, - lat, - lng, - altitude, - heading, - speed, - accuracy, - recorded - } -} - -function convertTelemetryRecordToTelemetry(telemetryRecord: TelemetryRecord): Telemetry { - const { lat, lng, altitude, heading, speed, accuracy, ...props } = telemetryRecord - return { - ...props, - gps: { lat, lng, altitude, heading, speed, accuracy } - } -} function pathsFor(path: string): string[] { const { PATH_PREFIX } = process.env @@ -498,86 +483,13 @@ function isInsideBoundingBox(telemetry: Telemetry | undefined | null, bbox: Boun return false } -function isStateTransitionValid(eventA: VehicleEvent, eventB: VehicleEvent) { - switch (EVENT_STATUS_MAP[eventA.event_type]) { - case VEHICLE_STATUSES.available: - switch (eventB.event_type) { - case VEHICLE_EVENTS.deregister: - return true - case VEHICLE_EVENTS.agency_pick_up: - return true - case VEHICLE_EVENTS.service_end: - return true - case VEHICLE_EVENTS.trip_start: - return true - default: - return false - } - case VEHICLE_STATUSES.elsewhere: - switch (eventB.event_type) { - case VEHICLE_EVENTS.trip_enter: - return true - case VEHICLE_EVENTS.provider_pick_up: - return true - case VEHICLE_EVENTS.deregister: - return true - case VEHICLE_EVENTS.provider_drop_off: - return true - default: - return false - } - case VEHICLE_STATUSES.inactive: - switch (eventB.event_type) { - default: - return false - } - case VEHICLE_STATUSES.removed: - switch (eventB.event_type) { - case VEHICLE_EVENTS.register: - return true - case VEHICLE_EVENTS.trip_enter: - return true - case VEHICLE_EVENTS.provider_drop_off: - return true - case VEHICLE_EVENTS.deregister: - return true - default: - return false - } - case VEHICLE_STATUSES.reserved: - switch (eventB.event_type) { - case VEHICLE_EVENTS.trip_start: - return true - case VEHICLE_EVENTS.cancel_reservation: - return true - default: - return false - } - case VEHICLE_STATUSES.trip: - switch (eventB.event_type) { - case VEHICLE_EVENTS.trip_leave: - return true - case VEHICLE_EVENTS.trip_end: - return true - default: - return false - } - case VEHICLE_STATUSES.unavailable: - switch (eventB.event_type) { - case VEHICLE_EVENTS.service_start: - return true - case VEHICLE_EVENTS.deregister: - return true - case VEHICLE_EVENTS.agency_pick_up: - return true - case VEHICLE_EVENTS.provider_pick_up: - return true - default: - return false - } - default: - return false - } +function isStateTransitionValid( + eventA: VehicleEvent & { event_type: VEHICLE_EVENT }, + eventB: VehicleEvent & { event_type: VEHICLE_EVENT } +) { + const currState = EVENT_STATUS_MAP[eventA.event_type] + const nextState = getNextState(currState, eventB.event_type) + return nextState !== undefined } function getPolygon(geographies: Geography[], geography: string): Geometry | FeatureCollection { @@ -622,20 +534,71 @@ function clone(obj: T): T { return JSON.parse(JSON.stringify(obj)) } -// T is the non-null and non-undefined type -function filterEmptyHelper(warnOnEmpty?: boolean) { - // https://stackoverflow.com/a/51577579 to remove null/undefined in typesafe way - return (elem: T | undefined | null, idx: number): elem is T => { - if (elem !== undefined && elem !== null) { - return true - } - if (warnOnEmpty) { - log.warn(`Encountered empty element at index: ${idx}`) // eslint-disable-line @typescript-eslint/no-floating-promises +type isDefinedOptions = Partial<{ warnOnEmpty: boolean }> + +const isDefined = (elem: T | undefined | null, options: isDefinedOptions = {}, index?: number): elem is T => { + const { warnOnEmpty } = options + if (elem !== undefined && elem !== null) { + return true + } + if (warnOnEmpty) { + logger.warn(`Encountered empty element at index: ${index}`) + } + return false +} + +const filterDefined = (options: isDefinedOptions = {}) => ( + value: T | undefined | null, + index: number, + array: (T | undefined | null)[] +): value is T => isDefined(value, options, index) + +function moved(latA: number, lngA: number, latB: number, lngB: number) { + const limit = 0.00001 // arbitrary amount + const latDiff = Math.abs(latA - latB) + const lngDiff = Math.abs(lngA - lngB) + return lngDiff > limit || latDiff > limit // very computational efficient basic check (better than sqrts & trig) +} + +function normalizeToArray(elementToNormalize: T | T[] | undefined): T[] { + if (elementToNormalize === undefined) { + return [] + } + if (isArray(elementToNormalize)) { + return elementToNormalize + } + return [elementToNormalize] +} + +const getEnvVar = (props: TProps): TProps => + Object.keys(props).reduce((env, key) => { + return { + ...env, + [key]: process.env[key] || props[key] } - return false + }, {} as TProps) + +export type ParseObjectPropertiesOptions = Partial<{ + parser: (value: string) => T +}> + +const parseObjectProperties = ( + obj: { [k: string]: unknown }, + { parser }: ParseObjectPropertiesOptions = {} +) => { + return { + keys: (first: TKey, ...rest: TKey[]): Partial<{ [P in TKey]: T }> => + [first, ...rest] + .map(key => ({ key, value: obj[key] })) + .filter((param): param is { key: TKey; value: string } => typeof param.value === 'string') + .reduce((params, { key, value }) => ({ ...params, [key]: parser ? parser(value) : value }), {}) } } +const asArray = (value: SingleOrArray): T[] => (Array.isArray(value) ? value : [value]) + +const pluralize = (count: number, singular: string, plural: string) => (count === 1 ? singular : plural) + export { UUID_REGEX, isUUID, @@ -661,6 +624,7 @@ export { hours, minutes, seconds, + timeframe, yesterday, csv, inc, @@ -670,11 +634,18 @@ export { tail, isStateTransitionValid, pointInGeometry, - convertTelemetryToTelemetryRecord, - convertTelemetryRecordToTelemetry, getPolygon, isInStatesOrEvents, routeDistance, clone, - filterEmptyHelper + isDefined, + moved, + normalizeToArray, + parseRelative, + getCurrentDate, + getEnvVar, + parseObjectProperties, + asArray, + pluralize, + filterDefined } diff --git a/packages/mds-utils/validators.ts b/packages/mds-utils/validators.ts deleted file mode 100644 index c271cf96e..000000000 --- a/packages/mds-utils/validators.ts +++ /dev/null @@ -1,176 +0,0 @@ -/* - Copyright 2019 City of Los Angeles. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - */ - -import { providers } from '@mds-core/mds-providers' // map of uuids -> obb -import { - AUDIT_EVENT_TYPES, - VEHICLE_EVENTS, - AUDIT_EVENT_TYPE, - VEHICLE_EVENT, - UUID, - Timestamp, - Telemetry -} from '@mds-core/mds-types' -import * as Joi from '@hapi/joi' -import { - StringifiedTelemetry, - StringifiedEventWithTelemetry, - StringifiedCacheReadDeviceResult -} from '@mds-core/mds-cache/types' -import { ValidationError } from './exceptions' - -interface ValidatorOptions { - property: string - assert: boolean - required: boolean -} - -// Convert empty string to undefined so required/optional works as expected -const stringSchema = Joi.string().empty('') - -// Don't allow type conversion -const numberSchema = Joi.number().options({ convert: false }) - -const uuidSchema = stringSchema.guid() - -const timestampSchema = numberSchema.min(1420099200000) - -const providerIdSchema = uuidSchema.valid(Object.keys(providers)) - -const vehicleIdSchema = stringSchema.max(255) - -const telemetrySchema = Joi.object().keys({ - gps: Joi.object() - .keys({ - lat: numberSchema - .min(-90) - .max(90) - .required(), - lng: numberSchema - .min(-180) - .max(180) - .required(), - speed: numberSchema.optional(), - heading: numberSchema.optional(), - accuracy: numberSchema.optional(), - altitude: numberSchema.optional() - }) - .required(), - charge: numberSchema.optional(), - provider_id: providerIdSchema.optional(), - device_id: uuidSchema.optional(), - timestamp: timestampSchema.required() -}) - -const vehicleEventTypeSchema = stringSchema.valid(Object.keys(VEHICLE_EVENTS)) - -const auditEventTypeSchema = (accept?: AUDIT_EVENT_TYPE[]): Joi.StringSchema => - stringSchema.valid(accept || Object.keys(AUDIT_EVENT_TYPES)) - -const auditIssueCodeSchema = stringSchema.max(31) - -const auditNoteSchema = stringSchema.max(255) - -const Format = (property: string, error: Joi.ValidationError): string => { - const [{ message, path }] = error.details - const [, ...details] = message.split(' ') - return `${[property, ...path].join('.')} ${details.join(' ')}` -} - -const Validate = (value: unknown, schema: Joi.Schema, options: Partial): boolean => { - const { assert = true, required = true, property = 'value' } = options - const { error } = Joi.validate(value, schema, { presence: required ? 'required' : 'optional' }) - if (error && assert) { - throw new ValidationError(`invalid_${property}`.toLowerCase(), { - [property]: value, - details: Format(property, error) - }) - } - return !error -} - -interface NumberValidatorOptions extends ValidatorOptions { - min: number - max: number -} - -export const isValidNumber = (value: unknown, options: Partial = {}): value is number => - Validate( - value, - numberSchema - .min(options.min === undefined ? Number.MIN_SAFE_INTEGER : options.min) - .max(options.max === undefined ? Number.MAX_SAFE_INTEGER : options.max), - options - ) - -export const isValidAuditTripId = ( - audit_trip_id: unknown, - options: Partial = {} -): audit_trip_id is UUID => Validate(audit_trip_id, uuidSchema, { property: 'audit_trip_id', ...options }) - -interface AuditEventValidatorOptions extends ValidatorOptions { - accept: AUDIT_EVENT_TYPE[] -} - -export const isValidDeviceId = (value: unknown, options: Partial = {}): value is UUID => - Validate(value, uuidSchema, { property: 'device_id', ...options }) - -export const isValidAuditEventType = ( - value: unknown, - { accept, ...options }: Partial = {} -): value is AUDIT_EVENT_TYPE => - Validate(value, auditEventTypeSchema(accept), { property: 'audit_event_type', ...options }) - -export const isValidTimestamp = (value: unknown, options: Partial = {}): value is Timestamp => - Validate(value, timestampSchema, { property: 'timestamp', ...options }) - -export const isValidProviderId = (value: unknown, options: Partial = {}): value is UUID => - Validate(value, providerIdSchema, { property: 'provider_id', ...options }) - -export const isValidProviderVehicleId = (value: unknown, options: Partial = {}): value is string => - Validate(value, vehicleIdSchema, { property: 'provider_vehicle_id', ...options }) - -export const isValidAuditEventId = (value: unknown, options: Partial = {}): value is UUID => - Validate(value, uuidSchema, { property: 'audit_event_id', ...options }) - -export const isValidAuditDeviceId = (value: unknown, options: Partial = {}): value is UUID => - Validate(value, uuidSchema, { property: 'audit_device_id', ...options }) - -export const isValidTelemetry = (value: unknown, options: Partial = {}): value is Telemetry => - Validate(value, telemetrySchema, { property: 'telemetry', ...options }) - -export const isValidVehicleEventType = ( - value: unknown, - options: Partial = {} -): value is VEHICLE_EVENT => Validate(value, vehicleEventTypeSchema, { property: 'vehicle_event_type', ...options }) - -export const isValidAuditIssueCode = (value: unknown, options: Partial = {}): value is string => - Validate(value, auditIssueCodeSchema, { property: 'audit_issue_code', ...options }) - -export const isValidAuditNote = (value: unknown, options: Partial = {}): value is string => - Validate(value, auditNoteSchema, { property: 'note', ...options }) - -const HasPropertyAssertion = (obj: unknown, ...props: (keyof T)[]): obj is T => - typeof obj === 'object' && obj !== null && props.every(prop => prop in obj) - -export const isStringifiedTelemetry = (telemetry: unknown): telemetry is StringifiedTelemetry => - HasPropertyAssertion(telemetry, 'gps') - -export const isStringifiedEventWithTelemetry = (event: unknown): event is StringifiedEventWithTelemetry => - HasPropertyAssertion(event, 'event_type', 'telemetry') - -export const isStringifiedCacheReadDeviceResult = (device: unknown): device is StringifiedCacheReadDeviceResult => - HasPropertyAssertion(device, 'device_id', 'provider_id', 'type', 'propulsion') diff --git a/packages/mds-web-sockets/client.ts b/packages/mds-web-sockets/client.ts new file mode 100644 index 000000000..addfaac6c --- /dev/null +++ b/packages/mds-web-sockets/client.ts @@ -0,0 +1,66 @@ +import WebSocket from 'ws' +import { VehicleEvent, Telemetry } from '@mds-core/mds-types' +import logger from '@mds-core/mds-logger' +import { setWsHeartbeat, WebSocketBase } from 'ws-heartbeat/client' +import requestPromise from 'request-promise' +import { ENTITY_TYPE } from './types' + +const { TOKEN, URL = 'mds-web-sockets:4000' } = process.env + +let connection: WebSocket + +/* Authenticate */ +async function sendAuth() { + return connection.send(`AUTH%Bearer ${TOKEN}`) +} + +async function getClient() { + if (connection && connection.readyState === 1) { + return connection + } + + try { + const res = await requestPromise(`http://${URL}`) + + if (res.statusCode === 503) { + throw new Error('Could not connect to WebSocket server') + } + connection = new WebSocket(`ws://${URL}`) + + setWsHeartbeat(connection as WebSocketBase, 'PING') + + connection.onopen = async () => { + await sendAuth() + } + + connection.onerror = async err => { + return logger.error(err) + } + + return connection + } catch (err) { + throw new Error(`Could not connect to WebSocket server ${err}`) + } +} + +/* Force test event to be send back to client */ +async function sendPush(entity: ENTITY_TYPE, data: VehicleEvent | Telemetry) { + try { + const client = await getClient() + return client.send(`PUSH%${entity}%${JSON.stringify(data)}`) + } catch (err) { + logger.warn(err) + } +} + +export function writeTelemetry(telemetries: Telemetry[]) { + return telemetries.map(telemetry => sendPush('telemetry', telemetry)) +} + +export function writeEvent(event: VehicleEvent) { + return sendPush('event', event) +} + +export function shutdown() { + if (connection) connection.close() +} diff --git a/packages/mds-web-sockets/clients.ts b/packages/mds-web-sockets/clients.ts new file mode 100644 index 000000000..e4381267e --- /dev/null +++ b/packages/mds-web-sockets/clients.ts @@ -0,0 +1,99 @@ +import WebSocket from 'ws' +import { WebSocketAuthorizer } from '@mds-core/mds-api-authorizer' +import { AuthorizationError } from '@mds-core/mds-utils' +import logger from '@mds-core/mds-logger' +import jwt from 'jsonwebtoken' +import jwks from 'jwks-rsa' +import { promisify } from 'util' + +export class Clients { + authenticatedClients: WebSocket[] + + subList: { [key: string]: WebSocket[] } + + public static getKey = async (header: { kid: string }) => { + const { JWKS_URI } = process.env + + if (!JWKS_URI) throw new Error('No JWKS_URI defined!') + + const client = jwks({ + jwksUri: JWKS_URI + }) + + /* Technically, this typedef is slightly incorrect, but is to coerce the compiler to happiness without type guarding. One of publicKey or rsaPublicKey *always* exists. */ + const key: { publicKey?: string; rsaPublicKey?: string } = await promisify(client.getSigningKey)( + header.kid ?? 'null' + ) + + return key.publicKey || key.rsaPublicKey + } + + public constructor(supportedEntities: readonly string[]) { + // Initialize subscription list with configured entities + this.subList = Object.fromEntries(supportedEntities.map(e => [e, []])) + this.authenticatedClients = [] + this.saveClient = this.saveClient.bind(this) + } + + public isAuthenticated(client: WebSocket) { + return this.authenticatedClients.includes(client) + } + + public saveClient(entities: string[], client: WebSocket) { + if (!this.authenticatedClients.includes(client)) { + return + } + + const trimmedEntities = entities.map(entity => entity.trim()) + + return Promise.all( + trimmedEntities.map(entity => { + try { + this.subList[entity].push(client) + client.send(`SUB%${JSON.stringify({ status: 'Success' })}`) + } catch { + client.send(`SUB%${JSON.stringify({ status: 'Failure' })}`) + return logger.error(`failed to push ${entity}`) + } + }) + ) + } + + public async saveAuth(authorizer: string, client: WebSocket) { + try { + const [, token] = authorizer.split(' ') + + const auth = WebSocketAuthorizer(authorizer) + + const validateAuth = await Clients.checkAuth(token) + if (!validateAuth) { + client.send(JSON.stringify({ err: new AuthorizationError() })) + return + } + + const scopes = auth?.scope.split(' ') ?? [] + + if (scopes.includes('admin:all')) { + this.authenticatedClients.push(client) + client.send(`AUTH%${JSON.stringify({ status: 'Success' })}`) + } else { + client.send(`AUTH%${JSON.stringify({ status: 'Failure' })}`) + } + } catch (err) { + logger.warn(err) + client.send(JSON.stringify(err)) + } + } + + public static async checkAuth(token: string) { + try { + const { JWT_ISSUER, JWT_AUDIENCE } = process.env + const { header } = jwt.decode(token, { complete: true, json: true }) as { header: { kid: string } } + const key = (await this.getKey(header)) as string + return jwt.verify(token, key, { audience: JWT_AUDIENCE, issuer: JWT_ISSUER }) + } catch (err) { + logger.warn(err) + return false + } + } +} diff --git a/packages/mds-web-sockets/index.ts b/packages/mds-web-sockets/index.ts new file mode 100644 index 000000000..068434551 --- /dev/null +++ b/packages/mds-web-sockets/index.ts @@ -0,0 +1,4 @@ +import { writeEvent, writeTelemetry, shutdown } from './client' +import { WebSocketServer } from './ws-server' + +export { writeEvent, writeTelemetry, WebSocketServer, shutdown } diff --git a/packages/mds-web-sockets/package.json b/packages/mds-web-sockets/package.json new file mode 100644 index 000000000..1f095256e --- /dev/null +++ b/packages/mds-web-sockets/package.json @@ -0,0 +1,46 @@ +{ + "name": "@mds-core/mds-web-sockets", + "version": "0.0.1", + "description": "Mobility Data Specification web sockets interface", + "main": "dist/index.js", + "types": "dist/index.d.ts", + "files": [ + "dist/" + ], + "keywords": [ + "mds", + "stream" + ], + "author": "City of Los Angeles", + "license": "Apache-2.0", + "dependencies": { + "@mds-core/mds-api-authorizer": "0.1.26", + "@mds-core/mds-api-server": "0.1.26", + "@mds-core/mds-logger": "0.1.24", + "@mds-core/mds-stream": "0.1.26", + "@mds-core/mds-types": "0.1.23", + "@mds-core/mds-utils": "0.1.26", + "@types/jsonwebtoken": "8.5.0", + "@types/node-rsa": "1.0.0", + "@types/request-promise": "4.1.46", + "@types/ws": "7.2.4", + "jsonwebtoken": "8.5.1", + "jwks-rsa": "1.8.0", + "node-rsa": "1.0.8", + "request-promise": "4.2.5", + "ts-nats": "1.2.14-2", + "ws": "7.3.0", + "ws-heartbeat": "1.1.0" + }, + "scripts": { + "build": "tsc --build tsconfig.build.json", + "client": "yarn watch client", + "server": "yarn watch launch_server", + "start": "yarn watch launch_server", + "test": "yarn test:eslint && yarn test:unit", + "test:eslint": "eslint --ignore-path ../../.gitignore '**/*.ts'", + "test:unit": "DOTENV_CONFIG_PATH=../../.env nyc --lines 50 ts-mocha --project ../../tsconfig.json --exit", + "ts-node": "yarn build && DOTENV_CONFIG_PATH=../../.env ts-node -r dotenv/config", + "watch": "nodemon --watch '../../packages' --ext 'ts' --ignore '*.d.ts' --exec yarn ts-node --" + } +} diff --git a/packages/mds-web-sockets/server.ts b/packages/mds-web-sockets/server.ts new file mode 100644 index 000000000..dd9f13d56 --- /dev/null +++ b/packages/mds-web-sockets/server.ts @@ -0,0 +1,6 @@ +import { WebSocketServer } from './ws-server' + +WebSocketServer() + .then(() => console.log(`WebSockerServer running`)) + // eslint-disable-next-line promise/prefer-await-to-callbacks + .catch(err => console.log(err)) diff --git a/packages/mds-web-sockets/tests/ws.spec.ts b/packages/mds-web-sockets/tests/ws.spec.ts new file mode 100644 index 000000000..6c4bc4088 --- /dev/null +++ b/packages/mds-web-sockets/tests/ws.spec.ts @@ -0,0 +1,107 @@ +/* eslint-reason need to have multiline RSA key */ +/* eslint-disable no-multi-str */ +import WebSocket from 'ws' +import { MOCHA_PROVIDER_ID } from '@mds-core/mds-providers' +import { PROVIDER_SCOPES } from '@mds-core/mds-test-data' +import Sinon from 'sinon' +import jwt from 'jsonwebtoken' +import NodeRSA from 'node-rsa' +import { WebSocketServer } from '../ws-server' +import { Clients } from '../clients' + +const JWT_AUDIENCE = 'https://example.com' +const JWT_ISSUER = 'https://example.com' + +process.env.JWT_AUDIENCE = JWT_AUDIENCE +process.env.TOKEN_ISSUER = JWT_ISSUER + +const key = new NodeRSA({ b: 512 }) + +const RSA_PRIVATE_KEY = key.exportKey('private') +const RSA_PUBLIC_KEY = key.exportKey('public') + +const returnRsaPublicKey = async () => RSA_PUBLIC_KEY + +const goodToken = jwt.sign({ provider_id: MOCHA_PROVIDER_ID, scope: PROVIDER_SCOPES }, RSA_PRIVATE_KEY, { + algorithm: 'RS256', + audience: 'https://example.com', + issuer: 'https://example.com' +}) + +const ADMIN_AUTH = `Bearer ${goodToken}` + +before(() => { + Sinon.stub(Clients, 'getKey').returns(returnRsaPublicKey()) + /* eslint-disable-next-line @typescript-eslint/no-floating-promises */ + WebSocketServer() +}) + +describe('Tests MDS-Web-Sockets', () => { + describe('Tests Authentication', () => { + it('Tests admin:all scoped tokens can authenticate successfully', done => { + const client = new WebSocket(`ws://localhost:${process.env.PORT || 4000}`) + client.onopen = () => { + client.send(`AUTH%${ADMIN_AUTH}`) + } + + client.on('message', data => { + if (data === 'AUTH%{"status":"Success"}') { + client.close() + return done() + } + client.close() + return done(data) + }) + }) + + it('Tests invalid audience tokens cannot authenticate successfully', done => { + const badToken = jwt.sign({ provider_id: MOCHA_PROVIDER_ID, scope: PROVIDER_SCOPES }, RSA_PRIVATE_KEY, { + algorithm: 'RS256', + audience: 'https://foo.com', + issuer: 'https://foo.com' + }) + + const BAD_AUTH = `Bearer ${badToken}` + + const client = new WebSocket(`ws://localhost:${process.env.PORT || 4000}`) + client.onopen = () => { + client.send(`AUTH%${BAD_AUTH}`) + } + + client.on('message', data => { + if (data === '{"err":{"name":"AuthorizationError"}}') { + client.close() + return done() + } + client.close() + return done(data) + }) + }) + + it('Subscribe and send event', done => { + const client = new WebSocket(`ws://localhost:${process.env.PORT || 4000}`) + client.onopen = () => { + client.send(`AUTH%${ADMIN_AUTH}`) + } + + client.on('message', data => { + if (data === 'AUTH%{"status":"Success"}') { + client.send('SUB%event') + return + } + + if (data === 'SUB%{"status":"Success"}') { + client.send(`PUSH%event%${JSON.stringify({ foo: 'bar' })}`) + return + } + + if (data === 'event%{"foo":"bar"}') { + client.close() + return done() + } + + return done + }) + }) + }) +}) diff --git a/packages/mds-web-sockets/tsconfig.build.json b/packages/mds-web-sockets/tsconfig.build.json new file mode 100644 index 000000000..6695f6378 --- /dev/null +++ b/packages/mds-web-sockets/tsconfig.build.json @@ -0,0 +1,14 @@ +{ + "extends": "../../tsconfig.build.json", + "compilerOptions": { + "outDir": "dist" + }, + "references": [ + { "path": "../../packages/mds-api-authorizer/tsconfig.build.json" }, + { "path": "../../packages/mds-api-server/tsconfig.build.json" }, + { "path": "../../packages/mds-stream/tsconfig.build.json" }, + { "path": "../../packages/mds-logger/tsconfig.build.json" }, + { "path": "../../packages/mds-types/tsconfig.build.json" }, + { "path": "../../packages/mds-utils/tsconfig.build.json" } + ] +} diff --git a/packages/mds-web-sockets/tsconfig.eslint.json b/packages/mds-web-sockets/tsconfig.eslint.json new file mode 100644 index 000000000..2a400942a --- /dev/null +++ b/packages/mds-web-sockets/tsconfig.eslint.json @@ -0,0 +1 @@ +{ "extends": "../../tsconfig.eslint.json" } diff --git a/packages/mds-web-sockets/types.ts b/packages/mds-web-sockets/types.ts new file mode 100644 index 000000000..2110f93b3 --- /dev/null +++ b/packages/mds-web-sockets/types.ts @@ -0,0 +1,2 @@ +export const ENTITY_TYPES = ['event', 'telemetry'] as const +export type ENTITY_TYPE = typeof ENTITY_TYPES[number] diff --git a/packages/mds-web-sockets/ws-server.ts b/packages/mds-web-sockets/ws-server.ts new file mode 100644 index 000000000..0cb8da80c --- /dev/null +++ b/packages/mds-web-sockets/ws-server.ts @@ -0,0 +1,110 @@ +import logger from '@mds-core/mds-logger' +import { seconds, getEnvVar } from '@mds-core/mds-utils' +import WebSocket from 'ws' +import { setWsHeartbeat } from 'ws-heartbeat/server' +import { Nullable } from '@mds-core/mds-types' +import { ApiServer, HttpServer } from '@mds-core/mds-api-server' +import stream from '@mds-core/mds-stream' +import { NatsError, Msg } from 'ts-nats' +import { ENTITY_TYPES } from './types' +import { Clients } from './clients' + +/** + * Web Socket Server that autosubscribes to Nats stream and allows socket subscription by entity type + * @param entityTypes - entity names to support + */ +export const WebSocketServer = async (entityTypes?: T) => { + const supportedEntities = entityTypes || ENTITY_TYPES + const server = HttpServer(ApiServer(app => app)) + + logger.info('Creating WS server') + const wss = new WebSocket.Server({ server }) + logger.info('WS Server created!') + + setWsHeartbeat( + wss, + (ws, data) => { + if (data === 'PING') { + ws.send('PONG') + } + }, + seconds(60) + ) + + const clients = new Clients(supportedEntities) + + function isSupported(entity: string) { + return supportedEntities.some(e => e === entity) + } + + function pushToClients(entity: string, message: string) { + const staleClients: WebSocket[] = [] + if (clients.subList[entity]) { + clients.subList[entity].forEach(client => { + if (client.readyState !== 1) staleClients.push(client) + else { + client.send(`${entity}%${message}`) + client.emit(entity, message) + } + }) + } + + Object.keys(clients.subList).map(entityKey => { + clients.subList[entityKey] = clients.subList[entityKey].filter(client => !staleClients.includes(client)) + }) + + staleClients.forEach(client => client.close()) + } + + wss.on('connection', (ws: WebSocket) => { + ws.on('message', async (data: WebSocket.Data) => { + const message = data.toString().trim().split('%') + const [header, ...args] = message + + /* Testing message, also useful in a NATS-less environment */ + if (header === 'PUSH') { + if (clients.isAuthenticated(ws)) { + if (args.length === 2) { + const [entity, payload] = args + // Limit messages to only supported entities + if (isSupported(entity)) { + await pushToClients(entity, payload) + return + } + return ws.send(`Invalid entity: ${entity}`) + } + } + } + + if (header === 'AUTH') { + const [token] = args + if (token) { + return clients.saveAuth(token, ws) + } + } + + if (header === 'SUB') { + return clients.saveClient(args, ws) + } + + if (header === 'PING') { + return + } + + return ws.send('Invalid request!') + }) + }) + + const { TENANT_ID } = getEnvVar({ + TENANT_ID: 'mds' + }) + + const processor = async (err: Nullable, msg: Msg) => { + const entity = msg.subject.split('.')?.[1] + await pushToClients(entity, JSON.stringify(msg.data)) + } + + supportedEntities.forEach(async e => { + await stream.NatsStreamConsumer(`${TENANT_ID}.event`, processor).initialize() + }) +} diff --git a/packages/mds-webpack-config/index.ts b/packages/mds-webpack-config/index.ts new file mode 100644 index 000000000..f9a6da934 --- /dev/null +++ b/packages/mds-webpack-config/index.ts @@ -0,0 +1,142 @@ +/* + Copyright 2019-2020 City of Los Angeles. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +import { Configuration, ConfigurationFactory, ContextReplacementPlugin, IgnorePlugin } from 'webpack' +import GitRevisionPlugin from 'git-revision-webpack-plugin' +import WrapperWebpackPlugin from 'wrapper-webpack-plugin' +import WebpackMerge from 'webpack-merge' +import { parse, resolve } from 'path' + +const gitRevisionPlugin = new GitRevisionPlugin({ commithashCommand: 'rev-parse --short HEAD' }) + +type CustomConfiguration = Omit + +const MergeConfigurations = (name: string, path: string, config: CustomConfiguration = {}): ConfigurationFactory => ( + env, + argv +) => { + const dirname = process.cwd() + + const entry = { + [name]: path || `${dirname}/${name}.ts` + } + + const { npm_package_name = '', npm_package_version = '' } = typeof env === 'string' ? {} : env + + console.log('BUNDLE:', resolve(entry[name])) /* eslint-disable-line no-console */ + + return WebpackMerge( + { + entry, + output: { path: `${dirname}/dist`, filename: `${name}.js`, libraryTarget: 'commonjs' }, + module: { + rules: [ + { + test: /\.ts$/, + loader: 'ts-loader', + options: { onlyCompileBundledFiles: true } + } + ] + }, + plugins: [ + // Ignore Critical Dependency Warnings + // https://medium.com/tomincode/hiding-critical-dependency-warnings-from-webpack-c76ccdb1f6c1 + ...['app-root-path', 'express', 'google-spreadsheet', 'optional', 'typeorm'].map( + module => + new ContextReplacementPlugin( + new RegExp(`node_modules/${module}`), + (data: { dependencies: { critical: unknown }[] }) => { + // eslint-disable-next-line no-param-reassign + data.dependencies = data.dependencies.map(dependency => { + // eslint-disable-next-line no-param-reassign + delete dependency.critical + return dependency + }) + } + ) + ), + // Ignore Optional Dependencies, + ...[ + 'pg-native', // Postgres + 'hiredis', // Redis + ...['bufferutil', 'utf-8-validate'], // https://github.com/adieuadieu/serverless-chrome/issues/103#issuecomment-358261003 + ...[ + '@sap/hdbext', + 'mongodb', + 'mssql', + 'mysql', + 'mysql2', + 'oracledb', + 'pg-query-stream', + 'react-native-sqlite-storage', + 'sql.js', + 'sqlite3', + 'typeorm-aurora-data-api-driver' + ] // TypeORM + ].map(dependency => new IgnorePlugin(new RegExp(`^${dependency}$`))), + // Make npm package name/version available to bundle + new WrapperWebpackPlugin({ + header: () => + `Object.assign(process.env, { + npm_package_name: '${npm_package_name}', + npm_package_version: '${npm_package_version}', + npm_package_git_branch: '${gitRevisionPlugin.branch()}', + npm_package_git_commit: '${gitRevisionPlugin.commithash()}', + npm_package_build_date: '${new Date().toISOString()}' + });` + }) + ], + resolve: { + extensions: ['.ts', '.js'] + }, + externals: { + sharp: 'commonjs sharp' + }, + target: 'node', + stats: { + all: false, + assets: true, + errors: true, + warnings: true + } + }, + config + ) +} + +type WebpackConfigurationBuilderOptions = Partial<{ + name: string +}> + +class WebpackConfigurationBuilder { + private name: string + + constructor(private path: string, { name }: WebpackConfigurationBuilderOptions = {}) { + this.name = name || parse(path).name + } + + public UsingDefaultConfig() { + return MergeConfigurations(this.name, this.path) + } + + public UsingCustomConfig(config: CustomConfiguration) { + return MergeConfigurations(this.name, this.path, config) + } +} + +export default { + Bundle: (path: string, options?: WebpackConfigurationBuilderOptions) => new WebpackConfigurationBuilder(path, options) +} diff --git a/packages/mds-webpack-config/package.json b/packages/mds-webpack-config/package.json new file mode 100644 index 000000000..62572688c --- /dev/null +++ b/packages/mds-webpack-config/package.json @@ -0,0 +1,30 @@ +{ + "name": "@mds-core/mds-webpack-config", + "version": "0.1.0", + "description": "Webpack Configuration", + "main": "dist/index.js", + "types": "dist/index.d.ts", + "files": [ + "dist/" + ], + "scripts": { + "build": "tsc --build tsconfig.build.json", + "test": "yarn test:eslint && yarn test:unit", + "test:eslint": "eslint --ignore-path ../../.gitignore '**/*.ts'", + "test:unit": "exit 0" + }, + "dependencies": { + "@types/git-revision-webpack-plugin": "3.0.0", + "@types/webpack": "4.41.13", + "@types/webpack-merge": "4.1.5", + "git-revision-webpack-plugin": "3.0.6", + "webpack": "4.43.0", + "webpack-merge": "4.2.2", + "wrapper-webpack-plugin": "2.1.0" + }, + "keywords": [ + "mds" + ], + "author": "City of Los Angeles", + "license": "Apache-2.0" +} diff --git a/container-images/env-inject/tsconfig.build.json b/packages/mds-webpack-config/tsconfig.build.json similarity index 100% rename from container-images/env-inject/tsconfig.build.json rename to packages/mds-webpack-config/tsconfig.build.json diff --git a/packages/mds-webpack-config/tsconfig.eslint.json b/packages/mds-webpack-config/tsconfig.eslint.json new file mode 100644 index 000000000..2a400942a --- /dev/null +++ b/packages/mds-webpack-config/tsconfig.eslint.json @@ -0,0 +1 @@ +{ "extends": "../../tsconfig.eslint.json" } diff --git a/tsconfig.build.json b/tsconfig.build.json index 900e96ec5..f1ccd3387 100644 --- a/tsconfig.build.json +++ b/tsconfig.build.json @@ -1,5 +1,5 @@ { - "extends": "./tsconfig.settings.json", + "extends": "./tsconfig.json", "compilerOptions": { "baseUrl": ".", "paths": { @@ -8,5 +8,6 @@ "listEmittedFiles": true, "declaration": true, "composite": true - } + }, + "exclude": ["**/tests", "**/dist"] } diff --git a/tsconfig.eslint.json b/tsconfig.eslint.json new file mode 100644 index 000000000..7e7c4052d --- /dev/null +++ b/tsconfig.eslint.json @@ -0,0 +1,9 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "baseUrl": ".", + "paths": { + "*": ["@types/*", "*"] + } + } +} diff --git a/tsconfig.json b/tsconfig.json index c1829fc8f..55c57e9f8 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,11 +1,21 @@ { - "extends": "./tsconfig.settings.json", "compilerOptions": { "baseUrl": ".", "paths": { "@container-images/*": ["container-images/*/index.ts"], "@mds-core/*": ["packages/*/index.ts"], + "packages/*": ["/dev/null"], "*": ["@types/*", "*"] - } + }, + "target": "ES2019", + "module": "commonjs", + "strict": true, + "esModuleInterop": true, + "inlineSourceMap": true, + "inlineSources": true, + "preserveSymlinks": true, + "emitDecoratorMetadata": true, + "experimentalDecorators": true, + "strictPropertyInitialization": false } } diff --git a/tsconfig.settings.json b/tsconfig.settings.json deleted file mode 100644 index 32311cd7d..000000000 --- a/tsconfig.settings.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "compilerOptions": { - "target": "es2018", - "module": "commonjs", - "strict": true, - "esModuleInterop": true, - "inlineSourceMap": true, - "inlineSources": true, - "preserveSymlinks": true - } -} diff --git a/yarn.lock b/yarn.lock index 46220c3f4..acf150adf 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2,112 +2,196 @@ # yarn lockfile v1 -"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.5.5": - version "7.5.5" - resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.5.5.tgz#bc0782f6d69f7b7d49531219699b988f669a8f9d" - integrity sha512-27d4lZoomVyo51VegxI20xZPuSHusqbQag/ztrBC7wegWoQ1nLREPVSKSW8byhTlzTKyNE4ifaTA6lCp7JjpFw== - dependencies: - "@babel/highlight" "^7.0.0" +"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.8.3.tgz#33e25903d7481181534e12ec0a25f16b6fcf419e" + integrity sha512-a9gxpmdXtZEInkCSHUJDLHZVBgb1QS0jhss4cPP93EW7s+uC5bikET2twEF3KV+7rDblJcmNvTR7VJejqd2C2g== + dependencies: + "@babel/highlight" "^7.8.3" + +"@babel/core@^7.7.5": + version "7.9.6" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.9.6.tgz#d9aa1f580abf3b2286ef40b6904d390904c63376" + integrity sha512-nD3deLvbsApbHAHttzIssYqgb883yU/d9roe4RZymBCDaZryMJDbptVpEpeQuRh4BJ+SYI8le9YGxKvFEvl1Wg== + dependencies: + "@babel/code-frame" "^7.8.3" + "@babel/generator" "^7.9.6" + "@babel/helper-module-transforms" "^7.9.0" + "@babel/helpers" "^7.9.6" + "@babel/parser" "^7.9.6" + "@babel/template" "^7.8.6" + "@babel/traverse" "^7.9.6" + "@babel/types" "^7.9.6" + convert-source-map "^1.7.0" + debug "^4.1.0" + gensync "^1.0.0-beta.1" + json5 "^2.1.2" + lodash "^4.17.13" + resolve "^1.3.2" + semver "^5.4.1" + source-map "^0.5.0" -"@babel/generator@^7.4.0", "@babel/generator@^7.5.5": - version "7.5.5" - resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.5.5.tgz#873a7f936a3c89491b43536d12245b626664e3cf" - integrity sha512-ETI/4vyTSxTzGnU2c49XHv2zhExkv9JHLTwDAFz85kmcwuShvYG2H08FwgIguQf4JC75CBnXAUM5PqeF4fj0nQ== +"@babel/generator@^7.9.6": + version "7.9.6" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.9.6.tgz#5408c82ac5de98cda0d77d8124e99fa1f2170a43" + integrity sha512-+htwWKJbH2bL72HRluF8zumBxzuX0ZZUFl3JLNyoUjM/Ho8wnVpPXM6aUz8cfKDqQ/h7zHqKt4xzJteUosckqQ== dependencies: - "@babel/types" "^7.5.5" + "@babel/types" "^7.9.6" jsesc "^2.5.1" lodash "^4.17.13" source-map "^0.5.0" - trim-right "^1.0.1" -"@babel/helper-function-name@^7.1.0": - version "7.1.0" - resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.1.0.tgz#a0ceb01685f73355d4360c1247f582bfafc8ff53" - integrity sha512-A95XEoCpb3TO+KZzJ4S/5uW5fNe26DjBGqf1o9ucyLyCmi1dXq/B3c8iaWTfBk3VvetUxl16e8tIrd5teOCfGw== +"@babel/helper-function-name@^7.9.5": + version "7.9.5" + resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.9.5.tgz#2b53820d35275120e1874a82e5aabe1376920a5c" + integrity sha512-JVcQZeXM59Cd1qanDUxv9fgJpt3NeKUaqBqUEvfmQ+BCOKq2xUgaWZW2hr0dkbyJgezYuplEoh5knmrnS68efw== dependencies: - "@babel/helper-get-function-arity" "^7.0.0" - "@babel/template" "^7.1.0" - "@babel/types" "^7.0.0" + "@babel/helper-get-function-arity" "^7.8.3" + "@babel/template" "^7.8.3" + "@babel/types" "^7.9.5" -"@babel/helper-get-function-arity@^7.0.0": - version "7.0.0" - resolved "https://registry.yarnpkg.com/@babel/helper-get-function-arity/-/helper-get-function-arity-7.0.0.tgz#83572d4320e2a4657263734113c42868b64e49c3" - integrity sha512-r2DbJeg4svYvt3HOS74U4eWKsUAMRH01Z1ds1zx8KNTPtpTL5JAsdFv8BNyOpVqdFhHkkRDIg5B4AsxmkjAlmQ== +"@babel/helper-get-function-arity@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/helper-get-function-arity/-/helper-get-function-arity-7.8.3.tgz#b894b947bd004381ce63ea1db9f08547e920abd5" + integrity sha512-FVDR+Gd9iLjUMY1fzE2SR0IuaJToR4RkCDARVfsBBPSP53GEqSFjD8gNyxg246VUyc/ALRxFaAK8rVG7UT7xRA== dependencies: - "@babel/types" "^7.0.0" + "@babel/types" "^7.8.3" -"@babel/helper-split-export-declaration@^7.4.4": - version "7.4.4" - resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.4.4.tgz#ff94894a340be78f53f06af038b205c49d993677" - integrity sha512-Ro/XkzLf3JFITkW6b+hNxzZ1n5OQ80NvIUdmHspih1XAhtN3vPTuUFT4eQnela+2MaZ5ulH+iyP513KJrxbN7Q== +"@babel/helper-member-expression-to-functions@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.8.3.tgz#659b710498ea6c1d9907e0c73f206eee7dadc24c" + integrity sha512-fO4Egq88utkQFjbPrSHGmGLFqmrshs11d46WI+WZDESt7Wu7wN2G2Iu+NMMZJFDOVRHAMIkB5SNh30NtwCA7RA== dependencies: - "@babel/types" "^7.4.4" + "@babel/types" "^7.8.3" -"@babel/highlight@^7.0.0": - version "7.5.0" - resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.5.0.tgz#56d11312bd9248fa619591d02472be6e8cb32540" - integrity sha512-7dV4eu9gBxoM0dAnj/BCFDW9LFU0zvTrkq0ugM7pnHEgguOEeOz1so2ZghEdzviYzQEED0r4EAgpsBChKy1TRQ== +"@babel/helper-module-imports@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.8.3.tgz#7fe39589b39c016331b6b8c3f441e8f0b1419498" + integrity sha512-R0Bx3jippsbAEtzkpZ/6FIiuzOURPcMjHp+Z6xPe6DtApDJx+w7UYyOLanZqO8+wKR9G10s/FmHXvxaMd9s6Kg== dependencies: - chalk "^2.0.0" - esutils "^2.0.2" - js-tokens "^4.0.0" + "@babel/types" "^7.8.3" -"@babel/parser@^7.0.0", "@babel/parser@^7.4.3", "@babel/parser@^7.4.4", "@babel/parser@^7.5.5": - version "7.5.5" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.5.5.tgz#02f077ac8817d3df4a832ef59de67565e71cca4b" - integrity sha512-E5BN68cqR7dhKan1SfqgPGhQ178bkVKpXTPEXnFJBrEt8/DKRZlybmy+IgYLTeN7tp1R5Ccmbm2rBk17sHYU3g== +"@babel/helper-module-transforms@^7.9.0": + version "7.9.0" + resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.9.0.tgz#43b34dfe15961918707d247327431388e9fe96e5" + integrity sha512-0FvKyu0gpPfIQ8EkxlrAydOWROdHpBmiCiRwLkUiBGhCUPRRbVD2/tm3sFr/c/GWFrQ/ffutGUAnx7V0FzT2wA== + dependencies: + "@babel/helper-module-imports" "^7.8.3" + "@babel/helper-replace-supers" "^7.8.6" + "@babel/helper-simple-access" "^7.8.3" + "@babel/helper-split-export-declaration" "^7.8.3" + "@babel/template" "^7.8.6" + "@babel/types" "^7.9.0" + lodash "^4.17.13" -"@babel/template@^7.1.0", "@babel/template@^7.4.0": - version "7.4.4" - resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.4.4.tgz#f4b88d1225689a08f5bc3a17483545be9e4ed237" - integrity sha512-CiGzLN9KgAvgZsnivND7rkA+AeJ9JB0ciPOD4U59GKbQP2iQl+olF1l76kJOupqidozfZ32ghwBEJDhnk9MEcw== +"@babel/helper-optimise-call-expression@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.8.3.tgz#7ed071813d09c75298ef4f208956006b6111ecb9" + integrity sha512-Kag20n86cbO2AvHca6EJsvqAd82gc6VMGule4HwebwMlwkpXuVqrNRj6CkCV2sKxgi9MyAUnZVnZ6lJ1/vKhHQ== dependencies: - "@babel/code-frame" "^7.0.0" - "@babel/parser" "^7.4.4" - "@babel/types" "^7.4.4" - -"@babel/traverse@^7.0.0", "@babel/traverse@^7.4.3": - version "7.5.5" - resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.5.5.tgz#f664f8f368ed32988cd648da9f72d5ca70f165bb" - integrity sha512-MqB0782whsfffYfSjH4TM+LMjrJnhCNEDMDIjeTpl+ASaUvxcjoiVCo/sM1GhS1pHOXYfWVCYneLjMckuUxDaQ== - dependencies: - "@babel/code-frame" "^7.5.5" - "@babel/generator" "^7.5.5" - "@babel/helper-function-name" "^7.1.0" - "@babel/helper-split-export-declaration" "^7.4.4" - "@babel/parser" "^7.5.5" - "@babel/types" "^7.5.5" + "@babel/types" "^7.8.3" + +"@babel/helper-replace-supers@^7.8.6": + version "7.9.6" + resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.9.6.tgz#03149d7e6a5586ab6764996cd31d6981a17e1444" + integrity sha512-qX+chbxkbArLyCImk3bWV+jB5gTNU/rsze+JlcF6Nf8tVTigPJSI1o1oBow/9Resa1yehUO9lIipsmu9oG4RzA== + dependencies: + "@babel/helper-member-expression-to-functions" "^7.8.3" + "@babel/helper-optimise-call-expression" "^7.8.3" + "@babel/traverse" "^7.9.6" + "@babel/types" "^7.9.6" + +"@babel/helper-simple-access@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.8.3.tgz#7f8109928b4dab4654076986af575231deb639ae" + integrity sha512-VNGUDjx5cCWg4vvCTR8qQ7YJYZ+HBjxOgXEl7ounz+4Sn7+LMD3CFrCTEU6/qXKbA2nKg21CwhhBzO0RpRbdCw== + dependencies: + "@babel/template" "^7.8.3" + "@babel/types" "^7.8.3" + +"@babel/helper-split-export-declaration@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.8.3.tgz#31a9f30070f91368a7182cf05f831781065fc7a9" + integrity sha512-3x3yOeyBhW851hroze7ElzdkeRXQYQbFIb7gLK1WQYsw2GWDay5gAJNw1sWJ0VFP6z5J1whqeXH/WCdCjZv6dA== + dependencies: + "@babel/types" "^7.8.3" + +"@babel/helper-validator-identifier@^7.9.0", "@babel/helper-validator-identifier@^7.9.5": + version "7.9.5" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.9.5.tgz#90977a8e6fbf6b431a7dc31752eee233bf052d80" + integrity sha512-/8arLKUFq882w4tWGj9JYzRpAlZgiWUJ+dtteNTDqrRBz9Iguck9Rn3ykuBDoUwh2TO4tSAJlrxDUOXWklJe4g== + +"@babel/helpers@^7.9.6": + version "7.9.6" + resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.9.6.tgz#092c774743471d0bb6c7de3ad465ab3d3486d580" + integrity sha512-tI4bUbldloLcHWoRUMAj4g1bF313M/o6fBKhIsb3QnGVPwRm9JsNf/gqMkQ7zjqReABiffPV6RWj7hEglID5Iw== + dependencies: + "@babel/template" "^7.8.3" + "@babel/traverse" "^7.9.6" + "@babel/types" "^7.9.6" + +"@babel/highlight@^7.8.3": + version "7.9.0" + resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.9.0.tgz#4e9b45ccb82b79607271b2979ad82c7b68163079" + integrity sha512-lJZPilxX7Op3Nv/2cvFdnlepPXDxi29wxteT57Q965oc5R9v86ztx0jfxVrTcBk8C2kcPkkDa2Z4T3ZsPPVWsQ== + dependencies: + "@babel/helper-validator-identifier" "^7.9.0" + chalk "^2.0.0" + js-tokens "^4.0.0" + +"@babel/parser@^7.7.0", "@babel/parser@^7.8.6", "@babel/parser@^7.9.6": + version "7.9.6" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.9.6.tgz#3b1bbb30dabe600cd72db58720998376ff653bc7" + integrity sha512-AoeIEJn8vt+d/6+PXDRPaksYhnlbMIiejioBZvvMQsOjW/JYK6k/0dKnvvP3EhK5GfMBWDPtrxRtegWdAcdq9Q== + +"@babel/runtime-corejs3@^7.8.3": + version "7.9.6" + resolved "https://registry.yarnpkg.com/@babel/runtime-corejs3/-/runtime-corejs3-7.9.6.tgz#67aded13fffbbc2cb93247388cf84d77a4be9a71" + integrity sha512-6toWAfaALQjt3KMZQc6fABqZwUDDuWzz+cAfPhqyEnzxvdWOAkjwPNxgF8xlmo7OWLsSjaKjsskpKHRLaMArOA== + dependencies: + core-js-pure "^3.0.0" + regenerator-runtime "^0.13.4" + +"@babel/runtime@^7.9.2": + version "7.9.6" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.9.6.tgz#a9102eb5cadedf3f31d08a9ecf294af7827ea29f" + integrity sha512-64AF1xY3OAkFHqOb9s4jpgk1Mm5vDZ4L3acHvAml+53nO1XbXLuDodsVpO4OIUsmemlUHMxNdYMNJmsvOwLrvQ== + dependencies: + regenerator-runtime "^0.13.4" + +"@babel/template@^7.8.3", "@babel/template@^7.8.6": + version "7.8.6" + resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.8.6.tgz#86b22af15f828dfb086474f964dcc3e39c43ce2b" + integrity sha512-zbMsPMy/v0PWFZEhQJ66bqjhH+z0JgMoBWuikXybgG3Gkd/3t5oQ1Rw2WQhnSrsOmsKXnZOx15tkC4qON/+JPg== + dependencies: + "@babel/code-frame" "^7.8.3" + "@babel/parser" "^7.8.6" + "@babel/types" "^7.8.6" + +"@babel/traverse@^7.7.0", "@babel/traverse@^7.9.6": + version "7.9.6" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.9.6.tgz#5540d7577697bf619cc57b92aa0f1c231a94f442" + integrity sha512-b3rAHSjbxy6VEAvlxM8OV/0X4XrG72zoxme6q1MOoe2vd0bEc+TwayhuC1+Dfgqh1QEG+pj7atQqvUprHIccsg== + dependencies: + "@babel/code-frame" "^7.8.3" + "@babel/generator" "^7.9.6" + "@babel/helper-function-name" "^7.9.5" + "@babel/helper-split-export-declaration" "^7.8.3" + "@babel/parser" "^7.9.6" + "@babel/types" "^7.9.6" debug "^4.1.0" globals "^11.1.0" lodash "^4.17.13" -"@babel/types@^7.0.0", "@babel/types@^7.4.0", "@babel/types@^7.4.4", "@babel/types@^7.5.5": - version "7.5.5" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.5.5.tgz#97b9f728e182785909aa4ab56264f090a028d18a" - integrity sha512-s63F9nJioLqOlW3UkyMd+BYhXt44YuaFm/VV0VwuteqjYwRrObkU7ra9pY4wAJR3oXi8hJrMcrcJdO/HH33vtw== +"@babel/types@^7.7.0", "@babel/types@^7.8.3", "@babel/types@^7.8.6", "@babel/types@^7.9.0", "@babel/types@^7.9.5", "@babel/types@^7.9.6": + version "7.9.6" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.9.6.tgz#2c5502b427251e9de1bd2dff95add646d95cc9f7" + integrity sha512-qxXzvBO//jO9ZnoasKF1uJzHd2+M6Q2ZPIVfnFps8JJvXy0ZBbwbNOmE6SGIY5XOY6d1Bo5lb9d9RJ8nv3WSeA== dependencies: - esutils "^2.0.2" + "@babel/helper-validator-identifier" "^7.9.5" lodash "^4.17.13" to-fast-properties "^2.0.0" -"@cypress/listr-verbose-renderer@0.4.1": - version "0.4.1" - resolved "https://registry.yarnpkg.com/@cypress/listr-verbose-renderer/-/listr-verbose-renderer-0.4.1.tgz#a77492f4b11dcc7c446a34b3e28721afd33c642a" - integrity sha1-p3SS9LEdzHxEajSz4ochr9M8ZCo= - dependencies: - chalk "^1.1.3" - cli-cursor "^1.0.2" - date-fns "^1.27.2" - figures "^1.7.0" - -"@cypress/xvfb@1.2.4": - version "1.2.4" - resolved "https://registry.yarnpkg.com/@cypress/xvfb/-/xvfb-1.2.4.tgz#2daf42e8275b39f4aa53c14214e557bd14e7748a" - integrity sha512-skbBzPggOVYCbnGgV+0dmBdW/s77ZkAOXIC1knS8NagwDjBrNC1LuXtQJeiN6l+m7lzmHtaoUw/ctJKdqkG57Q== - dependencies: - debug "^3.1.0" - lodash.once "^4.1.1" - "@evocateur/libnpmaccess@^3.1.2": version "3.1.2" resolved "https://registry.yarnpkg.com/@evocateur/libnpmaccess/-/libnpmaccess-3.1.2.tgz#ecf7f6ce6b004e9f942b098d92200be4a4b1c845" @@ -183,19 +267,19 @@ which "^1.3.1" "@hapi/address@2.x.x": - version "2.0.0" - resolved "https://registry.yarnpkg.com/@hapi/address/-/address-2.0.0.tgz#9f05469c88cb2fd3dcd624776b54ee95c312126a" - integrity sha512-mV6T0IYqb0xL1UALPFplXYQmR0twnXG0M6jUswpquqT2sD12BOiCiLy3EvMp/Fy7s3DZElC4/aPjEjo2jeZpvw== + version "2.1.4" + resolved "https://registry.yarnpkg.com/@hapi/address/-/address-2.1.4.tgz#5d67ed43f3fd41a69d4b9ff7b56e7c0d1d0a81e5" + integrity sha512-QD1PhQk+s31P1ixsX0H0Suoupp3VMXzIVMSwobR3F3MSUO2YCV0B7xqLcUw/Bh8yuvd3LhpyqLQWTNcRmp6IdQ== "@hapi/bourne@1.x.x": version "1.3.2" resolved "https://registry.yarnpkg.com/@hapi/bourne/-/bourne-1.3.2.tgz#0a7095adea067243ce3283e1b56b8a8f453b242a" integrity sha512-1dVNHT76Uu5N3eJNTYcvxee+jzX4Z9lfciqRRHCU27ihbUcYi+iSc2iml5Ke1LXe1SyJCLA0+14Jh4tXJgOppA== -"@hapi/hoek@8.x.x": - version "8.1.0" - resolved "https://registry.yarnpkg.com/@hapi/hoek/-/hoek-8.1.0.tgz#8f7627b23ed9bf67088fc7f9669e48c63ad421bd" - integrity sha512-b1J4jxYnW+n6lC91V6Pqg9imP9BZq0HNCeM+3sbXg05rQsE9cGYrKFpZjyztVesGmNRE6R+QaEoWGATeIiUVjA== +"@hapi/hoek@8.x.x", "@hapi/hoek@^8.3.0": + version "8.5.1" + resolved "https://registry.yarnpkg.com/@hapi/hoek/-/hoek-8.5.1.tgz#fde96064ca446dec8c55a8c2f130957b070c6e06" + integrity sha512-yN7kbciD87WzLGc5539Tn0sApjyiGHAJgKvG9W8C7O+6c7qmoQMfVs0W4bX17eqz6C78QJqqFrtgdK5EWf6Qow== "@hapi/joi@15.1.1": version "15.1.1" @@ -208,21 +292,43 @@ "@hapi/topo" "3.x.x" "@hapi/topo@3.x.x": - version "3.1.2" - resolved "https://registry.yarnpkg.com/@hapi/topo/-/topo-3.1.2.tgz#57cc1317be1a8c5f47c124f9b0e3c49cd78424d2" - integrity sha512-r+aumOqJ5QbD6aLPJWqVjMAPsx5pZKz+F5yPqXZ/WWG9JTtHbQqlzrJoknJ0iJxLj9vlXtmpSdjlkszseeG8OA== + version "3.1.6" + resolved "https://registry.yarnpkg.com/@hapi/topo/-/topo-3.1.6.tgz#68d935fa3eae7fdd5ab0d7f953f3205d8b2bfc29" + integrity sha512-tAag0jEcjwH+P2quUfipd7liWCNX2F8NvYjQp2wtInsZxnMlypdw0FtAOLxtvvkO+GSRRbmNi8m/5y42PQJYCQ== dependencies: - "@hapi/hoek" "8.x.x" + "@hapi/hoek" "^8.3.0" -"@lerna/add@3.18.0": - version "3.18.0" - resolved "https://registry.yarnpkg.com/@lerna/add/-/add-3.18.0.tgz#86e38f14d7a0a7c61315dccb402377feb1c9db83" - integrity sha512-Z5EaQbBnJn1LEPb0zb0Q2o9T8F8zOnlCsj6JYpY6aSke17UUT7xx0QMN98iBK+ueUHKjN/vdFdYlNCYRSIdujA== +"@istanbuljs/load-nyc-config@^1.0.0": + version "1.0.0" + resolved "https://registry.yarnpkg.com/@istanbuljs/load-nyc-config/-/load-nyc-config-1.0.0.tgz#10602de5570baea82f8afbfa2630b24e7a8cfe5b" + integrity sha512-ZR0rq/f/E4f4XcgnDvtMWXCUJpi8eO0rssVhmztsZqLIEFA9UUP9zmpE0VxlM+kv/E1ul2I876Fwil2ayptDVg== + dependencies: + camelcase "^5.3.1" + find-up "^4.1.0" + js-yaml "^3.13.1" + resolve-from "^5.0.0" + +"@istanbuljs/nyc-config-typescript@1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@istanbuljs/nyc-config-typescript/-/nyc-config-typescript-1.0.1.tgz#55172f5663b3635586add21b14d42ca94a163d58" + integrity sha512-/gz6LgVpky205LuoOfwEZmnUtaSmdk0QIMcNFj9OvxhiMhPpKftMgZmGN7jNj7jR+lr8IB1Yks3QSSSNSxfoaQ== + dependencies: + "@istanbuljs/schema" "^0.1.2" + +"@istanbuljs/schema@^0.1.2": + version "0.1.2" + resolved "https://registry.yarnpkg.com/@istanbuljs/schema/-/schema-0.1.2.tgz#26520bf09abe4a5644cd5414e37125a8954241dd" + integrity sha512-tsAQNx32a8CoFhjhijUIhI4kccIAgmGhy8LZMZgGfmXcpMbPRUqn5LWmgRttILi6yeGmBJd2xsPkFMs0PzgPCw== + +"@lerna/add@3.21.0": + version "3.21.0" + resolved "https://registry.yarnpkg.com/@lerna/add/-/add-3.21.0.tgz#27007bde71cc7b0a2969ab3c2f0ae41578b4577b" + integrity sha512-vhUXXF6SpufBE1EkNEXwz1VLW03f177G9uMOFMQkp6OJ30/PWg4Ekifuz9/3YfgB2/GH8Tu4Lk3O51P2Hskg/A== dependencies: "@evocateur/pacote" "^9.6.3" - "@lerna/bootstrap" "3.18.0" - "@lerna/command" "3.18.0" - "@lerna/filter-options" "3.18.0" + "@lerna/bootstrap" "3.21.0" + "@lerna/command" "3.21.0" + "@lerna/filter-options" "3.20.0" "@lerna/npm-conf" "3.16.0" "@lerna/validation-error" "3.13.0" dedent "^0.7.0" @@ -230,20 +336,20 @@ p-map "^2.1.0" semver "^6.2.0" -"@lerna/bootstrap@3.18.0": - version "3.18.0" - resolved "https://registry.yarnpkg.com/@lerna/bootstrap/-/bootstrap-3.18.0.tgz#705d9eb51a24d549518796a09f24d24526ed975b" - integrity sha512-3DZKWIaKvr7sUImoKqSz6eqn84SsOVMnA5QHwgzXiQjoeZ/5cg9x2r+Xj3+3w/lvLoh0j8U2GNtrIaPNis4bKQ== +"@lerna/bootstrap@3.21.0": + version "3.21.0" + resolved "https://registry.yarnpkg.com/@lerna/bootstrap/-/bootstrap-3.21.0.tgz#bcd1b651be5b0970b20d8fae04c864548123aed6" + integrity sha512-mtNHlXpmvJn6JTu0KcuTTPl2jLsDNud0QacV/h++qsaKbhAaJr/FElNZ5s7MwZFUM3XaDmvWzHKaszeBMHIbBw== dependencies: - "@lerna/command" "3.18.0" - "@lerna/filter-options" "3.18.0" + "@lerna/command" "3.21.0" + "@lerna/filter-options" "3.20.0" "@lerna/has-npm-version" "3.16.5" "@lerna/npm-install" "3.16.5" - "@lerna/package-graph" "3.18.0" + "@lerna/package-graph" "3.18.5" "@lerna/pulse-till-done" "3.13.0" "@lerna/rimraf-dir" "3.16.5" "@lerna/run-lifecycle" "3.16.2" - "@lerna/run-topologically" "3.18.0" + "@lerna/run-topologically" "3.18.5" "@lerna/symlink-binary" "3.17.0" "@lerna/symlink-dependencies" "3.17.0" "@lerna/validation-error" "3.13.0" @@ -259,16 +365,15 @@ read-package-tree "^5.1.6" semver "^6.2.0" -"@lerna/changed@3.18.3": - version "3.18.3" - resolved "https://registry.yarnpkg.com/@lerna/changed/-/changed-3.18.3.tgz#50529e8bd5d7fe2d0ace046a6e274d3de652a493" - integrity sha512-xZW7Rm+DlDIGc0EvKGyJZgT9f8FFa4d52mr/Y752dZuXR2qRmf9tXhVloRG39881s2A6yi3jqLtXZggKhsQW4Q== +"@lerna/changed@3.21.0": + version "3.21.0" + resolved "https://registry.yarnpkg.com/@lerna/changed/-/changed-3.21.0.tgz#108e15f679bfe077af500f58248c634f1044ea0b" + integrity sha512-hzqoyf8MSHVjZp0gfJ7G8jaz+++mgXYiNs9iViQGA8JlN/dnWLI5sWDptEH3/B30Izo+fdVz0S0s7ydVE3pWIw== dependencies: - "@lerna/collect-updates" "3.18.0" - "@lerna/command" "3.18.0" - "@lerna/listable" "3.18.0" + "@lerna/collect-updates" "3.20.0" + "@lerna/command" "3.21.0" + "@lerna/listable" "3.18.5" "@lerna/output" "3.13.0" - "@lerna/version" "3.18.3" "@lerna/check-working-tree@3.16.5": version "3.16.5" @@ -288,29 +393,29 @@ execa "^1.0.0" strong-log-transformer "^2.0.0" -"@lerna/clean@3.18.0": - version "3.18.0" - resolved "https://registry.yarnpkg.com/@lerna/clean/-/clean-3.18.0.tgz#cc67d7697db969a70e989992fdf077126308fb2e" - integrity sha512-BiwBELZNkarRQqj+v5NPB1aIzsOX+Y5jkZ9a5UbwHzEdBUQ5lQa0qaMLSOve/fSkaiZQxe6qnTyatN75lOcDMg== +"@lerna/clean@3.21.0": + version "3.21.0" + resolved "https://registry.yarnpkg.com/@lerna/clean/-/clean-3.21.0.tgz#c0b46b5300cc3dae2cda3bec14b803082da3856d" + integrity sha512-b/L9l+MDgE/7oGbrav6rG8RTQvRiZLO1zTcG17zgJAAuhlsPxJExMlh2DFwJEVi2les70vMhHfST3Ue1IMMjpg== dependencies: - "@lerna/command" "3.18.0" - "@lerna/filter-options" "3.18.0" - "@lerna/prompt" "3.13.0" + "@lerna/command" "3.21.0" + "@lerna/filter-options" "3.20.0" + "@lerna/prompt" "3.18.5" "@lerna/pulse-till-done" "3.13.0" "@lerna/rimraf-dir" "3.16.5" p-map "^2.1.0" p-map-series "^1.0.0" p-waterfall "^1.0.0" -"@lerna/cli@3.18.0": - version "3.18.0" - resolved "https://registry.yarnpkg.com/@lerna/cli/-/cli-3.18.0.tgz#2b6f8605bee299c6ada65bc2e4b3ed7bf715af3a" - integrity sha512-AwDyfGx7fxJgeaZllEuyJ9LZ6Tdv9yqRD9RX762yCJu+PCAFvB9bp6OYuRSGli7QQgM0CuOYnSg4xVNOmuGKDA== +"@lerna/cli@3.18.5": + version "3.18.5" + resolved "https://registry.yarnpkg.com/@lerna/cli/-/cli-3.18.5.tgz#c90c461542fcd35b6d5b015a290fb0dbfb41d242" + integrity sha512-erkbxkj9jfc89vVs/jBLY/fM0I80oLmJkFUV3Q3wk9J3miYhP14zgVEBsPZY68IZlEjT6T3Xlq2xO1AVaatHsA== dependencies: "@lerna/global-options" "3.13.0" dedent "^0.7.0" npmlog "^4.1.2" - yargs "^14.2.0" + yargs "^14.2.2" "@lerna/collect-uncommitted@3.16.5": version "3.16.5" @@ -322,10 +427,10 @@ figgy-pudding "^3.5.1" npmlog "^4.1.2" -"@lerna/collect-updates@3.18.0": - version "3.18.0" - resolved "https://registry.yarnpkg.com/@lerna/collect-updates/-/collect-updates-3.18.0.tgz#6086c64df3244993cc0a7f8fc0ddd6a0103008a6" - integrity sha512-LJMKgWsE/var1RSvpKDIxS8eJ7POADEc0HM3FQiTpEczhP6aZfv9x3wlDjaHpZm9MxJyQilqxZcasRANmRcNgw== +"@lerna/collect-updates@3.20.0": + version "3.20.0" + resolved "https://registry.yarnpkg.com/@lerna/collect-updates/-/collect-updates-3.20.0.tgz#62f9d76ba21a25b7d9fbf31c02de88744a564bd1" + integrity sha512-qBTVT5g4fupVhBFuY4nI/3FSJtQVcDh7/gEPOpRxoXB/yCSnT38MFHXWl+y4einLciCjt/+0x6/4AG80fjay2Q== dependencies: "@lerna/child-process" "3.16.5" "@lerna/describe-ref" "3.16.5" @@ -333,26 +438,26 @@ npmlog "^4.1.2" slash "^2.0.0" -"@lerna/command@3.18.0": - version "3.18.0" - resolved "https://registry.yarnpkg.com/@lerna/command/-/command-3.18.0.tgz#1e40399324a69d26a78969d59cf60e19b2f13fc3" - integrity sha512-JQ0TGzuZc9Ky8xtwtSLywuvmkU8X62NTUT3rMNrUykIkOxBaO+tE0O98u2yo/9BYOeTRji9IsjKZEl5i9Qt0xQ== +"@lerna/command@3.21.0": + version "3.21.0" + resolved "https://registry.yarnpkg.com/@lerna/command/-/command-3.21.0.tgz#9a2383759dc7b700dacfa8a22b2f3a6e190121f7" + integrity sha512-T2bu6R8R3KkH5YoCKdutKv123iUgUbW8efVjdGCDnCMthAQzoentOJfDeodBwn0P2OqCl3ohsiNVtSn9h78fyQ== dependencies: "@lerna/child-process" "3.16.5" - "@lerna/package-graph" "3.18.0" - "@lerna/project" "3.18.0" + "@lerna/package-graph" "3.18.5" + "@lerna/project" "3.21.0" "@lerna/validation-error" "3.13.0" "@lerna/write-log-file" "3.13.0" + clone-deep "^4.0.1" dedent "^0.7.0" execa "^1.0.0" is-ci "^2.0.0" - lodash "^4.17.14" npmlog "^4.1.2" -"@lerna/conventional-commits@3.16.4": - version "3.16.4" - resolved "https://registry.yarnpkg.com/@lerna/conventional-commits/-/conventional-commits-3.16.4.tgz#bf464f11b2f6534dad204db00430e1651b346a04" - integrity sha512-QSZJ0bC9n6FVaf+7KDIq5zMv8WnHXnwhyL5jG1Nyh3SgOg9q2uflqh7YsYB+G6FwaRfnPaKosh6obijpYg0llA== +"@lerna/conventional-commits@3.18.5": + version "3.18.5" + resolved "https://registry.yarnpkg.com/@lerna/conventional-commits/-/conventional-commits-3.18.5.tgz#08efd2e5b45acfaf3f151a53a3ec7ecade58a7bc" + integrity sha512-qcvXIEJ3qSgalxXnQ7Yxp5H9Ta5TVyai6vEor6AAEHc20WiO7UIdbLDCxBtiiHMdGdpH85dTYlsoYUwsCJu3HQ== dependencies: "@lerna/validation-error" "3.13.0" conventional-changelog-angular "^5.0.3" @@ -375,14 +480,14 @@ fs-extra "^8.1.0" npmlog "^4.1.2" -"@lerna/create@3.18.0": - version "3.18.0" - resolved "https://registry.yarnpkg.com/@lerna/create/-/create-3.18.0.tgz#78ba4af5eced661944a12b9d7da8553c096c390d" - integrity sha512-y9oS7ND5T13c+cCTJHa2Y9in02ppzyjsNynVWFuS40eIzZ3z058d9+3qSBt1nkbbQlVyfLoP6+bZPsjyzap5ig== +"@lerna/create@3.21.0": + version "3.21.0" + resolved "https://registry.yarnpkg.com/@lerna/create/-/create-3.21.0.tgz#e813832adf3488728b139e5a75c8b01b1372e62f" + integrity sha512-cRIopzKzE2vXJPmsiwCDMWo4Ct+KTmX3nvvkQLDoQNrrRK7w+3KQT3iiorbj1koD95RsVQA7mS2haWok9SIv0g== dependencies: "@evocateur/pacote" "^9.6.3" "@lerna/child-process" "3.16.5" - "@lerna/command" "3.18.0" + "@lerna/command" "3.21.0" "@lerna/npm-conf" "3.16.0" "@lerna/validation-error" "3.13.0" camelcase "^5.0.0" @@ -407,34 +512,35 @@ "@lerna/child-process" "3.16.5" npmlog "^4.1.2" -"@lerna/diff@3.18.0": - version "3.18.0" - resolved "https://registry.yarnpkg.com/@lerna/diff/-/diff-3.18.0.tgz#9638ff4b46e2a8b0d4ebf54cf2f267ac2f8fdb29" - integrity sha512-3iLNlpurc2nV9k22w8ini2Zjm2UPo3xtQgWyqdA6eJjvge0+5AlNAWfPoV6cV+Hc1xDbJD2YDSFpZPJ1ZGilRw== +"@lerna/diff@3.21.0": + version "3.21.0" + resolved "https://registry.yarnpkg.com/@lerna/diff/-/diff-3.21.0.tgz#e6df0d8b9916167ff5a49fcb02ac06424280a68d" + integrity sha512-5viTR33QV3S7O+bjruo1SaR40m7F2aUHJaDAC7fL9Ca6xji+aw1KFkpCtVlISS0G8vikUREGMJh+c/VMSc8Usw== dependencies: "@lerna/child-process" "3.16.5" - "@lerna/command" "3.18.0" + "@lerna/command" "3.21.0" "@lerna/validation-error" "3.13.0" npmlog "^4.1.2" -"@lerna/exec@3.18.0": - version "3.18.0" - resolved "https://registry.yarnpkg.com/@lerna/exec/-/exec-3.18.0.tgz#d9ec0b7ca06b7521f0b9f14a164e2d4ca5e1b3b9" - integrity sha512-hwkuzg1+38+pbzdZPhGtLIYJ59z498/BCNzR8d4/nfMYm8lFbw9RgJJajLcdbuJ9LJ08cZ93hf8OlzetL84TYg== +"@lerna/exec@3.21.0": + version "3.21.0" + resolved "https://registry.yarnpkg.com/@lerna/exec/-/exec-3.21.0.tgz#17f07533893cb918a17b41bcc566dc437016db26" + integrity sha512-iLvDBrIE6rpdd4GIKTY9mkXyhwsJ2RvQdB9ZU+/NhR3okXfqKc6py/24tV111jqpXTtZUW6HNydT4dMao2hi1Q== dependencies: "@lerna/child-process" "3.16.5" - "@lerna/command" "3.18.0" - "@lerna/filter-options" "3.18.0" - "@lerna/run-topologically" "3.18.0" + "@lerna/command" "3.21.0" + "@lerna/filter-options" "3.20.0" + "@lerna/profiler" "3.20.0" + "@lerna/run-topologically" "3.18.5" "@lerna/validation-error" "3.13.0" p-map "^2.1.0" -"@lerna/filter-options@3.18.0": - version "3.18.0" - resolved "https://registry.yarnpkg.com/@lerna/filter-options/-/filter-options-3.18.0.tgz#406667dc75a8fc813c26a91bde754b6a73e1a868" - integrity sha512-UGVcixs3TGzD8XSmFSbwUVVQnAjaZ6Rmt8Vuq2RcR98ULkGB1LiGNMY89XaNBhaaA8vx7yQWiLmJi2AfmD63Qg== +"@lerna/filter-options@3.20.0": + version "3.20.0" + resolved "https://registry.yarnpkg.com/@lerna/filter-options/-/filter-options-3.20.0.tgz#0f0f5d5a4783856eece4204708cc902cbc8af59b" + integrity sha512-bmcHtvxn7SIl/R9gpiNMVG7yjx7WyT0HSGw34YVZ9B+3xF/83N3r5Rgtjh4hheLZ+Q91Or0Jyu5O3Nr+AwZe2g== dependencies: - "@lerna/collect-updates" "3.18.0" + "@lerna/collect-updates" "3.20.0" "@lerna/filter-packages" "3.18.0" dedent "^0.7.0" figgy-pudding "^3.5.1" @@ -498,58 +604,67 @@ "@lerna/child-process" "3.16.5" semver "^6.2.0" -"@lerna/import@3.18.0": - version "3.18.0" - resolved "https://registry.yarnpkg.com/@lerna/import/-/import-3.18.0.tgz#c6b124b346a097e6c0f3f1ed4921a278d18bc80b" - integrity sha512-2pYIkkBTZsEdccfc+dPsKZeSw3tBzKSyl0b2lGrfmNX2Y41qqOzsJCyI1WO1uvEIP8aOaLy4hPpqRIBe4ee7hw== +"@lerna/import@3.21.0": + version "3.21.0" + resolved "https://registry.yarnpkg.com/@lerna/import/-/import-3.21.0.tgz#87b08f2a2bfeeff7357c6fd8490e638d3cd5b32d" + integrity sha512-aISkL4XD0Dqf5asDaOZWu65jgj8fWUhuQseZWuQe3UfHxav69fTS2YLIngUfencaOSZVOcVCom28YCzp61YDxw== dependencies: "@lerna/child-process" "3.16.5" - "@lerna/command" "3.18.0" - "@lerna/prompt" "3.13.0" + "@lerna/command" "3.21.0" + "@lerna/prompt" "3.18.5" "@lerna/pulse-till-done" "3.13.0" "@lerna/validation-error" "3.13.0" dedent "^0.7.0" fs-extra "^8.1.0" p-map-series "^1.0.0" -"@lerna/init@3.18.0": - version "3.18.0" - resolved "https://registry.yarnpkg.com/@lerna/init/-/init-3.18.0.tgz#b23b9170cce1f4630170dd744e8ee75785ea898d" - integrity sha512-/vHpmXkMlSaJaq25v5K13mcs/2L7E32O6dSsEkHaZCDRiV2BOqsZng9jjbE/4ynfsWfLLlU9ZcydwG72C3I+mQ== +"@lerna/info@3.21.0": + version "3.21.0" + resolved "https://registry.yarnpkg.com/@lerna/info/-/info-3.21.0.tgz#76696b676fdb0f35d48c83c63c1e32bb5e37814f" + integrity sha512-0XDqGYVBgWxUquFaIptW2bYSIu6jOs1BtkvRTWDDhw4zyEdp6q4eaMvqdSap1CG+7wM5jeLCi6z94wS0AuiuwA== + dependencies: + "@lerna/command" "3.21.0" + "@lerna/output" "3.13.0" + envinfo "^7.3.1" + +"@lerna/init@3.21.0": + version "3.21.0" + resolved "https://registry.yarnpkg.com/@lerna/init/-/init-3.21.0.tgz#1e810934dc8bf4e5386c031041881d3b4096aa5c" + integrity sha512-6CM0z+EFUkFfurwdJCR+LQQF6MqHbYDCBPyhu/d086LRf58GtYZYj49J8mKG9ktayp/TOIxL/pKKjgLD8QBPOg== dependencies: "@lerna/child-process" "3.16.5" - "@lerna/command" "3.18.0" + "@lerna/command" "3.21.0" fs-extra "^8.1.0" p-map "^2.1.0" write-json-file "^3.2.0" -"@lerna/link@3.18.0": - version "3.18.0" - resolved "https://registry.yarnpkg.com/@lerna/link/-/link-3.18.0.tgz#bc72dc62ef4d8fb842b3286887980f98b764781d" - integrity sha512-FbbIpH0EpsC+dpAbvxCoF3cn7F1MAyJjEa5Lh3XkDGATOlinMFuKCbmX0NLpOPQZ5zghvrui97cx+jz5F2IlHw== +"@lerna/link@3.21.0": + version "3.21.0" + resolved "https://registry.yarnpkg.com/@lerna/link/-/link-3.21.0.tgz#8be68ff0ccee104b174b5bbd606302c2f06e9d9b" + integrity sha512-tGu9GxrX7Ivs+Wl3w1+jrLi1nQ36kNI32dcOssij6bg0oZ2M2MDEFI9UF2gmoypTaN9uO5TSsjCFS7aR79HbdQ== dependencies: - "@lerna/command" "3.18.0" - "@lerna/package-graph" "3.18.0" + "@lerna/command" "3.21.0" + "@lerna/package-graph" "3.18.5" "@lerna/symlink-dependencies" "3.17.0" p-map "^2.1.0" slash "^2.0.0" -"@lerna/list@3.18.0": - version "3.18.0" - resolved "https://registry.yarnpkg.com/@lerna/list/-/list-3.18.0.tgz#6e5fe545ce4ba7c1eeb6d6cf69240d06c02bd496" - integrity sha512-mpB7Q6T+n2CaiPFz0LuOE+rXphDfHm0mKIwShnyS/XDcii8jXv+z9Iytj8p3rfCH2I1L80j2qL6jWzyGy/uzKA== +"@lerna/list@3.21.0": + version "3.21.0" + resolved "https://registry.yarnpkg.com/@lerna/list/-/list-3.21.0.tgz#42f76fafa56dea13b691ec8cab13832691d61da2" + integrity sha512-KehRjE83B1VaAbRRkRy6jLX1Cin8ltsrQ7FHf2bhwhRHK0S54YuA6LOoBnY/NtA8bHDX/Z+G5sMY78X30NS9tg== dependencies: - "@lerna/command" "3.18.0" - "@lerna/filter-options" "3.18.0" - "@lerna/listable" "3.18.0" + "@lerna/command" "3.21.0" + "@lerna/filter-options" "3.20.0" + "@lerna/listable" "3.18.5" "@lerna/output" "3.13.0" -"@lerna/listable@3.18.0": - version "3.18.0" - resolved "https://registry.yarnpkg.com/@lerna/listable/-/listable-3.18.0.tgz#752b014406a9a012486626d22e940edb8205973a" - integrity sha512-9gLGKYNLSKeurD+sJ2RA+nz4Ftulr91U127gefz0RlmAPpYSjwcJkxwa0UfJvpQTXv9C7yzHLnn0BjyAQRjuew== +"@lerna/listable@3.18.5": + version "3.18.5" + resolved "https://registry.yarnpkg.com/@lerna/listable/-/listable-3.18.5.tgz#e82798405b5ed8fc51843c8ef1e7a0e497388a1a" + integrity sha512-Sdr3pVyaEv5A7ZkGGYR7zN+tTl2iDcinryBPvtuv20VJrXBE8wYcOks1edBTcOWsPjCE/rMP4bo1pseyk3UTsg== dependencies: - "@lerna/query-graph" "3.18.0" + "@lerna/query-graph" "3.18.5" chalk "^2.3.1" columnify "^1.5.4" @@ -571,13 +686,13 @@ config-chain "^1.1.11" pify "^4.0.1" -"@lerna/npm-dist-tag@3.18.1": - version "3.18.1" - resolved "https://registry.yarnpkg.com/@lerna/npm-dist-tag/-/npm-dist-tag-3.18.1.tgz#d4dd82ea92e41e960b7117f83102ebcd7a23e511" - integrity sha512-vWkZh2T/O9OjPLDrba0BTWO7ug/C3sCwjw7Qyk1aEbxMBXB/eEJPqirwJTWT+EtRJQYB01ky3K8ZFOhElVyjLw== +"@lerna/npm-dist-tag@3.18.5": + version "3.18.5" + resolved "https://registry.yarnpkg.com/@lerna/npm-dist-tag/-/npm-dist-tag-3.18.5.tgz#9ef9abb7c104077b31f6fab22cc73b314d54ac55" + integrity sha512-xw0HDoIG6HreVsJND9/dGls1c+lf6vhu7yJoo56Sz5bvncTloYGLUppIfDHQr4ZvmPCK8rsh0euCVh2giPxzKQ== dependencies: "@evocateur/npm-registry-fetch" "^4.0.0" - "@lerna/otplease" "3.16.0" + "@lerna/otplease" "3.18.5" figgy-pudding "^3.5.1" npm-package-arg "^6.1.0" npmlog "^4.1.2" @@ -595,13 +710,13 @@ signal-exit "^3.0.2" write-pkg "^3.1.0" -"@lerna/npm-publish@3.16.2": - version "3.16.2" - resolved "https://registry.yarnpkg.com/@lerna/npm-publish/-/npm-publish-3.16.2.tgz#a850b54739446c4aa766a0ceabfa9283bb0be676" - integrity sha512-tGMb9vfTxP57vUV5svkBQxd5Tzc+imZbu9ZYf8Mtwe0+HYfDjNiiHLIQw7G95w4YRdc5KsCE8sQ0uSj+f2soIg== +"@lerna/npm-publish@3.18.5": + version "3.18.5" + resolved "https://registry.yarnpkg.com/@lerna/npm-publish/-/npm-publish-3.18.5.tgz#240e4039959fd9816b49c5b07421e11b5cb000af" + integrity sha512-3etLT9+2L8JAx5F8uf7qp6iAtOLSMj+ZYWY6oUgozPi/uLqU0/gsMsEXh3F0+YVW33q0M61RpduBoAlOOZnaTg== dependencies: "@evocateur/libnpmpublish" "^1.2.2" - "@lerna/otplease" "3.16.0" + "@lerna/otplease" "3.18.5" "@lerna/run-lifecycle" "3.16.2" figgy-pudding "^3.5.1" fs-extra "^8.1.0" @@ -619,12 +734,12 @@ "@lerna/get-npm-exec-opts" "3.13.0" npmlog "^4.1.2" -"@lerna/otplease@3.16.0": - version "3.16.0" - resolved "https://registry.yarnpkg.com/@lerna/otplease/-/otplease-3.16.0.tgz#de66aec4f3e835a465d7bea84b58a4ab6590a0fa" - integrity sha512-uqZ15wYOHC+/V0WnD2iTLXARjvx3vNrpiIeyIvVlDB7rWse9mL4egex/QSgZ+lDx1OID7l2kgvcUD9cFpbqB7Q== +"@lerna/otplease@3.18.5": + version "3.18.5" + resolved "https://registry.yarnpkg.com/@lerna/otplease/-/otplease-3.18.5.tgz#b77b8e760b40abad9f7658d988f3ea77d4fd0231" + integrity sha512-S+SldXAbcXTEDhzdxYLU0ZBKuYyURP/ND2/dK6IpKgLxQYh/z4ScljPDMyKymmEvgiEJmBsPZAAPfmNPEzxjog== dependencies: - "@lerna/prompt" "3.13.0" + "@lerna/prompt" "3.18.5" figgy-pudding "^3.5.1" "@lerna/output@3.13.0": @@ -648,10 +763,10 @@ tar "^4.4.10" temp-write "^3.4.0" -"@lerna/package-graph@3.18.0": - version "3.18.0" - resolved "https://registry.yarnpkg.com/@lerna/package-graph/-/package-graph-3.18.0.tgz#eb42d14404a55b26b2472081615e26b0817cd91a" - integrity sha512-BLYDHO5ihPh20i3zoXfLZ5ZWDCrPuGANgVhl7k5pCmRj90LCvT+C7V3zrw70fErGAfvkcYepMqxD+oBrAYwquQ== +"@lerna/package-graph@3.18.5": + version "3.18.5" + resolved "https://registry.yarnpkg.com/@lerna/package-graph/-/package-graph-3.18.5.tgz#c740e2ea3578d059e551633e950690831b941f6b" + integrity sha512-8QDrR9T+dBegjeLr+n9WZTVxUYUhIUjUgZ0gvNxUBN8S1WB9r6H5Yk56/MVaB64tA3oGAN9IIxX6w0WvTfFudA== dependencies: "@lerna/prerelease-id-from-version" "3.16.0" "@lerna/validation-error" "3.13.0" @@ -675,10 +790,20 @@ dependencies: semver "^6.2.0" -"@lerna/project@3.18.0": - version "3.18.0" - resolved "https://registry.yarnpkg.com/@lerna/project/-/project-3.18.0.tgz#56feee01daeb42c03cbdf0ed8a2a10cbce32f670" - integrity sha512-+LDwvdAp0BurOAWmeHE3uuticsq9hNxBI0+FMHiIai8jrygpJGahaQrBYWpwbshbQyVLeQgx3+YJdW2TbEdFWA== +"@lerna/profiler@3.20.0": + version "3.20.0" + resolved "https://registry.yarnpkg.com/@lerna/profiler/-/profiler-3.20.0.tgz#0f6dc236f4ea8f9ea5f358c6703305a4f32ad051" + integrity sha512-bh8hKxAlm6yu8WEOvbLENm42i2v9SsR4WbrCWSbsmOElx3foRnMlYk7NkGECa+U5c3K4C6GeBbwgqs54PP7Ljg== + dependencies: + figgy-pudding "^3.5.1" + fs-extra "^8.1.0" + npmlog "^4.1.2" + upath "^1.2.0" + +"@lerna/project@3.21.0": + version "3.21.0" + resolved "https://registry.yarnpkg.com/@lerna/project/-/project-3.21.0.tgz#5d784d2d10c561a00f20320bcdb040997c10502d" + integrity sha512-xT1mrpET2BF11CY32uypV2GPtPVm6Hgtha7D81GQP9iAitk9EccrdNjYGt5UBYASl4CIDXBRxwmTTVGfrCx82A== dependencies: "@lerna/package" "3.16.0" "@lerna/validation-error" "3.13.0" @@ -693,41 +818,41 @@ resolve-from "^4.0.0" write-json-file "^3.2.0" -"@lerna/prompt@3.13.0": - version "3.13.0" - resolved "https://registry.yarnpkg.com/@lerna/prompt/-/prompt-3.13.0.tgz#53571462bb3f5399cc1ca6d335a411fe093426a5" - integrity sha512-P+lWSFokdyvYpkwC3it9cE0IF2U5yy2mOUbGvvE4iDb9K7TyXGE+7lwtx2thtPvBAfIb7O13POMkv7df03HJeA== +"@lerna/prompt@3.18.5": + version "3.18.5" + resolved "https://registry.yarnpkg.com/@lerna/prompt/-/prompt-3.18.5.tgz#628cd545f225887d060491ab95df899cfc5218a1" + integrity sha512-rkKj4nm1twSbBEb69+Em/2jAERK8htUuV8/xSjN0NPC+6UjzAwY52/x9n5cfmpa9lyKf/uItp7chCI7eDmNTKQ== dependencies: inquirer "^6.2.0" npmlog "^4.1.2" -"@lerna/publish@3.18.3": - version "3.18.3" - resolved "https://registry.yarnpkg.com/@lerna/publish/-/publish-3.18.3.tgz#478bb94ee712a40b723413e437bcb9e307d3709c" - integrity sha512-XlfWOWIhaSK0Y2sX5ppNWI5Y3CDtlxMcQa1hTbZlC5rrDA6vD32iutbmH6Ix3c6wtvVbSkgA39GWsQEXxPS+7w== +"@lerna/publish@3.21.0": + version "3.21.0" + resolved "https://registry.yarnpkg.com/@lerna/publish/-/publish-3.21.0.tgz#0112393125f000484c3f50caba71a547f91bd7f4" + integrity sha512-JZ+ehZB9UCQ9nqH8Ld/Yqc/If++aK/7XIubkrB9sQ5hf2GeIbmI/BrJpMgLW/e9T5bKrUBZPUvoUN3daVipA5A== dependencies: "@evocateur/libnpmaccess" "^3.1.2" "@evocateur/npm-registry-fetch" "^4.0.0" "@evocateur/pacote" "^9.6.3" "@lerna/check-working-tree" "3.16.5" "@lerna/child-process" "3.16.5" - "@lerna/collect-updates" "3.18.0" - "@lerna/command" "3.18.0" + "@lerna/collect-updates" "3.20.0" + "@lerna/command" "3.21.0" "@lerna/describe-ref" "3.16.5" "@lerna/log-packed" "3.16.0" "@lerna/npm-conf" "3.16.0" - "@lerna/npm-dist-tag" "3.18.1" - "@lerna/npm-publish" "3.16.2" - "@lerna/otplease" "3.16.0" + "@lerna/npm-dist-tag" "3.18.5" + "@lerna/npm-publish" "3.18.5" + "@lerna/otplease" "3.18.5" "@lerna/output" "3.13.0" "@lerna/pack-directory" "3.16.4" "@lerna/prerelease-id-from-version" "3.16.0" - "@lerna/prompt" "3.13.0" + "@lerna/prompt" "3.18.5" "@lerna/pulse-till-done" "3.13.0" "@lerna/run-lifecycle" "3.16.2" - "@lerna/run-topologically" "3.18.0" + "@lerna/run-topologically" "3.18.5" "@lerna/validation-error" "3.13.0" - "@lerna/version" "3.18.3" + "@lerna/version" "3.21.0" figgy-pudding "^3.5.1" fs-extra "^8.1.0" npm-package-arg "^6.1.0" @@ -744,12 +869,12 @@ dependencies: npmlog "^4.1.2" -"@lerna/query-graph@3.18.0": - version "3.18.0" - resolved "https://registry.yarnpkg.com/@lerna/query-graph/-/query-graph-3.18.0.tgz#43801a2f1b80a0ea0bfd9d42d470605326a3035d" - integrity sha512-fgUhLx6V0jDuKZaKj562jkuuhrfVcjl5sscdfttJ8dXNVADfDz76nzzwLY0ZU7/0m69jDedohn5Fx5p7hDEVEg== +"@lerna/query-graph@3.18.5": + version "3.18.5" + resolved "https://registry.yarnpkg.com/@lerna/query-graph/-/query-graph-3.18.5.tgz#df4830bb5155273003bf35e8dda1c32d0927bd86" + integrity sha512-50Lf4uuMpMWvJ306be3oQDHrWV42nai9gbIVByPBYJuVW8dT8O8pA3EzitNYBUdLL9/qEVbrR0ry1HD7EXwtRA== dependencies: - "@lerna/package-graph" "3.18.0" + "@lerna/package-graph" "3.18.5" figgy-pudding "^3.5.1" "@lerna/resolve-symlink@3.16.0": @@ -781,25 +906,26 @@ npm-lifecycle "^3.1.2" npmlog "^4.1.2" -"@lerna/run-topologically@3.18.0": - version "3.18.0" - resolved "https://registry.yarnpkg.com/@lerna/run-topologically/-/run-topologically-3.18.0.tgz#9508604553cfbeba106cd84b711fade17947f94a" - integrity sha512-lrfEewwuUMC3ioxf9Z9NdHUakN6ihekcPfdYbzR2slmdbjYKmIA5srkWdrK8NwOpQCAuekpOovH2s8X3FGEopg== +"@lerna/run-topologically@3.18.5": + version "3.18.5" + resolved "https://registry.yarnpkg.com/@lerna/run-topologically/-/run-topologically-3.18.5.tgz#3cd639da20e967d7672cb88db0f756b92f2fdfc3" + integrity sha512-6N1I+6wf4hLOnPW+XDZqwufyIQ6gqoPfHZFkfWlvTQ+Ue7CuF8qIVQ1Eddw5HKQMkxqN10thKOFfq/9NQZ4NUg== dependencies: - "@lerna/query-graph" "3.18.0" + "@lerna/query-graph" "3.18.5" figgy-pudding "^3.5.1" p-queue "^4.0.0" -"@lerna/run@3.18.0": - version "3.18.0" - resolved "https://registry.yarnpkg.com/@lerna/run/-/run-3.18.0.tgz#b7069880f6313e4c6026b564b7b76e5d0f30a521" - integrity sha512-sblxHBZ9djaaG7wefPcfEicDqzrB7CP1m/jIB0JvPEQwG4C2qp++ewBpkjRw/mBtjtzg0t7v0nNMXzaWYrQckQ== +"@lerna/run@3.21.0": + version "3.21.0" + resolved "https://registry.yarnpkg.com/@lerna/run/-/run-3.21.0.tgz#2a35ec84979e4d6e42474fe148d32e5de1cac891" + integrity sha512-fJF68rT3veh+hkToFsBmUJ9MHc9yGXA7LSDvhziAojzOb0AI/jBDp6cEcDQyJ7dbnplba2Lj02IH61QUf9oW0Q== dependencies: - "@lerna/command" "3.18.0" - "@lerna/filter-options" "3.18.0" + "@lerna/command" "3.21.0" + "@lerna/filter-options" "3.20.0" "@lerna/npm-run-script" "3.16.5" "@lerna/output" "3.13.0" - "@lerna/run-topologically" "3.18.0" + "@lerna/profiler" "3.20.0" + "@lerna/run-topologically" "3.18.5" "@lerna/timer" "3.13.0" "@lerna/validation-error" "3.13.0" p-map "^2.1.0" @@ -839,23 +965,23 @@ dependencies: npmlog "^4.1.2" -"@lerna/version@3.18.3": - version "3.18.3" - resolved "https://registry.yarnpkg.com/@lerna/version/-/version-3.18.3.tgz#01344b39c0749fdeb6c178714733bacbde4d602f" - integrity sha512-IXXRlyM3Q/jrc+QZio+bgjG4ZaK+4LYmY4Yql1xyY0wZhAKsWP/Q6ho7e1EJNjNC5dUJO99Fq7qB05MkDf2OcQ== +"@lerna/version@3.21.0": + version "3.21.0" + resolved "https://registry.yarnpkg.com/@lerna/version/-/version-3.21.0.tgz#5bcc3d2de9eb8f4db18efb0d88973f9a509eccc3" + integrity sha512-nIT3u43fCNj6uSMN1dRxFnF4GhmIiOEqSTkGSjrMU+8kHKwzOqS/6X6TOzklBmCyEZOpF/fLlGqH3BZHnwLDzQ== dependencies: "@lerna/check-working-tree" "3.16.5" "@lerna/child-process" "3.16.5" - "@lerna/collect-updates" "3.18.0" - "@lerna/command" "3.18.0" - "@lerna/conventional-commits" "3.16.4" + "@lerna/collect-updates" "3.20.0" + "@lerna/command" "3.21.0" + "@lerna/conventional-commits" "3.18.5" "@lerna/github-client" "3.16.5" "@lerna/gitlab-client" "3.15.0" "@lerna/output" "3.13.0" "@lerna/prerelease-id-from-version" "3.16.0" - "@lerna/prompt" "3.13.0" + "@lerna/prompt" "3.18.5" "@lerna/run-lifecycle" "3.16.2" - "@lerna/run-topologically" "3.18.0" + "@lerna/run-topologically" "3.18.5" "@lerna/validation-error" "3.13.0" chalk "^2.3.1" dedent "^0.7.0" @@ -892,46 +1018,89 @@ resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-1.1.3.tgz#2b5a3ab3f918cca48a8c754c08168e3f03eba61b" integrity sha512-shAmDyaQC4H92APFoIaVDHCx5bStIocgvbwQyxPRrbUY20V1EYTbSDchWbuwlMG3V17cprZhA6+78JfB+3DTPw== -"@octokit/endpoint@^5.1.0": - version "5.3.6" - resolved "https://registry.yarnpkg.com/@octokit/endpoint/-/endpoint-5.3.6.tgz#58a67b75b853127568e0db533cdd10f3bdca2e23" - integrity sha512-XuerByak8H+jW9J/rVMEdBXfI4UTsDWUwAKgIP/uhQjXIUVdPRwt2Zg+SmbWQ+WY7pRkw/hFVES8C4G/Kle7oA== +"@octokit/auth-token@^2.4.0": + version "2.4.0" + resolved "https://registry.yarnpkg.com/@octokit/auth-token/-/auth-token-2.4.0.tgz#b64178975218b99e4dfe948253f0673cbbb59d9f" + integrity sha512-eoOVMjILna7FVQf96iWc3+ZtE/ZT6y8ob8ZzcqKY1ibSQCnu4O/B7pJvzMx5cyZ/RjAff6DAdEb0O0Cjcxidkg== dependencies: + "@octokit/types" "^2.0.0" + +"@octokit/endpoint@^6.0.1": + version "6.0.1" + resolved "https://registry.yarnpkg.com/@octokit/endpoint/-/endpoint-6.0.1.tgz#16d5c0e7a83e3a644d1ddbe8cded6c3d038d31d7" + integrity sha512-pOPHaSz57SFT/m3R5P8MUu4wLPszokn5pXcB/pzavLTQf2jbU+6iayTvzaY6/BiotuRS0qyEUkx3QglT4U958A== + dependencies: + "@octokit/types" "^2.11.1" is-plain-object "^3.0.0" - universal-user-agent "^4.0.0" + universal-user-agent "^5.0.0" "@octokit/plugin-enterprise-rest@^3.6.1": version "3.6.2" resolved "https://registry.yarnpkg.com/@octokit/plugin-enterprise-rest/-/plugin-enterprise-rest-3.6.2.tgz#74de25bef21e0182b4fa03a8678cd00a4e67e561" integrity sha512-3wF5eueS5OHQYuAEudkpN+xVeUsg8vYEMMenEzLphUZ7PRZ8OJtDcsreL3ad9zxXmBbaFWzLmFcdob5CLyZftA== -"@octokit/request-error@^1.0.1", "@octokit/request-error@^1.0.2": - version "1.0.4" - resolved "https://registry.yarnpkg.com/@octokit/request-error/-/request-error-1.0.4.tgz#15e1dc22123ba4a9a4391914d80ec1e5303a23be" - integrity sha512-L4JaJDXn8SGT+5G0uX79rZLv0MNJmfGa4vb4vy1NnpjSnWDLJRy6m90udGwvMmavwsStgbv2QNkPzzTCMmL+ig== +"@octokit/plugin-paginate-rest@^1.1.1": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-1.1.2.tgz#004170acf8c2be535aba26727867d692f7b488fc" + integrity sha512-jbsSoi5Q1pj63sC16XIUboklNw+8tL9VOnJsWycWYR78TKss5PVpIPb1TUUcMQ+bBh7cY579cVAWmf5qG+dw+Q== + dependencies: + "@octokit/types" "^2.0.1" + +"@octokit/plugin-request-log@^1.0.0": + version "1.0.0" + resolved "https://registry.yarnpkg.com/@octokit/plugin-request-log/-/plugin-request-log-1.0.0.tgz#eef87a431300f6148c39a7f75f8cfeb218b2547e" + integrity sha512-ywoxP68aOT3zHCLgWZgwUJatiENeHE7xJzYjfz8WI0goynp96wETBF+d95b8g/uL4QmS6owPVlaxiz3wyMAzcw== + +"@octokit/plugin-rest-endpoint-methods@2.4.0": + version "2.4.0" + resolved "https://registry.yarnpkg.com/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-2.4.0.tgz#3288ecf5481f68c494dd0602fc15407a59faf61e" + integrity sha512-EZi/AWhtkdfAYi01obpX0DF7U6b1VRr30QNQ5xSFPITMdLSfhcBqjamE3F+sKcxPbD7eZuMHu3Qkk2V+JGxBDQ== dependencies: + "@octokit/types" "^2.0.1" + deprecation "^2.3.1" + +"@octokit/request-error@^1.0.2": + version "1.2.1" + resolved "https://registry.yarnpkg.com/@octokit/request-error/-/request-error-1.2.1.tgz#ede0714c773f32347576c25649dc013ae6b31801" + integrity sha512-+6yDyk1EES6WK+l3viRDElw96MvwfJxCt45GvmjDUKWjYIb3PJZQkq3i46TwGwoPD4h8NmTrENmtyA1FwbmhRA== + dependencies: + "@octokit/types" "^2.0.0" deprecation "^2.0.0" once "^1.4.0" -"@octokit/request@^5.0.0": - version "5.1.0" - resolved "https://registry.yarnpkg.com/@octokit/request/-/request-5.1.0.tgz#5609dcc7b5323e529f29d535214383d9eaf0c05c" - integrity sha512-I15T9PwjFs4tbWyhtFU2Kq7WDPidYMvRB7spmxoQRZfxSmiqullG+Nz+KbSmpkfnlvHwTr1e31R5WReFRKMXjg== +"@octokit/request-error@^2.0.0": + version "2.0.0" + resolved "https://registry.yarnpkg.com/@octokit/request-error/-/request-error-2.0.0.tgz#94ca7293373654400fbb2995f377f9473e00834b" + integrity sha512-rtYicB4Absc60rUv74Rjpzek84UbVHGHJRu4fNVlZ1mCcyUPPuzFfG9Rn6sjHrd95DEsmjSt1Axlc699ZlbDkw== dependencies: - "@octokit/endpoint" "^5.1.0" - "@octokit/request-error" "^1.0.1" + "@octokit/types" "^2.0.0" + deprecation "^2.0.0" + once "^1.4.0" + +"@octokit/request@^5.2.0": + version "5.4.2" + resolved "https://registry.yarnpkg.com/@octokit/request/-/request-5.4.2.tgz#74f8e5bbd39dc738a1b127629791f8ad1b3193ee" + integrity sha512-zKdnGuQ2TQ2vFk9VU8awFT4+EYf92Z/v3OlzRaSh4RIP0H6cvW1BFPXq4XYvNez+TPQjqN+0uSkCYnMFFhcFrw== + dependencies: + "@octokit/endpoint" "^6.0.1" + "@octokit/request-error" "^2.0.0" + "@octokit/types" "^2.11.1" deprecation "^2.0.0" is-plain-object "^3.0.0" node-fetch "^2.3.0" once "^1.4.0" - universal-user-agent "^4.0.0" + universal-user-agent "^5.0.0" "@octokit/rest@^16.28.4": - version "16.30.2" - resolved "https://registry.yarnpkg.com/@octokit/rest/-/rest-16.30.2.tgz#da379a6f4f7e7e152733d38a9cba21da5c76ef7c" - integrity sha512-6YN4N/uWjjBUx4TNfWAxkrzCy1i7M4agdHs2g/sOTdSY/Va3T1v/f/ME5EnNfd64AMpBWee6rcLHQe5fki89lg== - dependencies: - "@octokit/request" "^5.0.0" + version "16.43.1" + resolved "https://registry.yarnpkg.com/@octokit/rest/-/rest-16.43.1.tgz#3b11e7d1b1ac2bbeeb23b08a17df0b20947eda6b" + integrity sha512-gfFKwRT/wFxq5qlNjnW2dh+qh74XgTQ2B179UX5K1HYCluioWj8Ndbgqw2PVqa1NnVJkGHp2ovMpVn/DImlmkw== + dependencies: + "@octokit/auth-token" "^2.4.0" + "@octokit/plugin-paginate-rest" "^1.1.1" + "@octokit/plugin-request-log" "^1.0.0" + "@octokit/plugin-rest-endpoint-methods" "2.4.0" + "@octokit/request" "^5.2.0" "@octokit/request-error" "^1.0.2" atob-lite "^2.0.0" before-after-hook "^2.0.0" @@ -944,38 +1113,49 @@ once "^1.4.0" universal-user-agent "^4.0.0" -"@sinonjs/commons@^1", "@sinonjs/commons@^1.0.2", "@sinonjs/commons@^1.4.0": - version "1.4.0" - resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-1.4.0.tgz#7b3ec2d96af481d7a0321252e7b1c94724ec5a78" - integrity sha512-9jHK3YF/8HtJ9wCAbG+j8cD0i0+ATS9A7gXFqS36TblLPNy6rEEc+SB0imo91eCboGaBYGV/MT1/br/J+EE7Tw== +"@octokit/types@^2.0.0", "@octokit/types@^2.0.1", "@octokit/types@^2.11.1": + version "2.16.2" + resolved "https://registry.yarnpkg.com/@octokit/types/-/types-2.16.2.tgz#4c5f8da3c6fecf3da1811aef678fda03edac35d2" + integrity sha512-O75k56TYvJ8WpAakWwYRN8Bgu60KrmX0z1KqFp1kNiFNkgW+JW+9EBKZ+S33PU6SLvbihqd+3drvPxKK68Ee8Q== dependencies: - type-detect "4.0.8" + "@types/node" ">= 8" -"@sinonjs/commons@^1.3.0": - version "1.6.0" - resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-1.6.0.tgz#ec7670432ae9c8eb710400d112c201a362d83393" - integrity sha512-w4/WHG7C4WWFyE5geCieFJF6MZkbW4VAriol5KlmQXpAQdxvV0p26sqNZOW6Qyw6Y0l9K4g+cHvvczR2sEEpqg== +"@sindresorhus/is@^0.14.0": + version "0.14.0" + resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-0.14.0.tgz#9fb3a3cf3132328151f353de4632e01e52102bea" + integrity sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ== + +"@sinonjs/commons@^1", "@sinonjs/commons@^1.3.0", "@sinonjs/commons@^1.4.0", "@sinonjs/commons@^1.6.0", "@sinonjs/commons@^1.7.0", "@sinonjs/commons@^1.7.2": + version "1.7.2" + resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-1.7.2.tgz#505f55c74e0272b43f6c52d81946bed7058fc0e2" + integrity sha512-+DUO6pnp3udV/v2VfUWgaY5BIE1IfT7lLfeDzPVeMT1XKkaAp9LgSI9x5RtrFQoZ9Oi0PgXQQHPaoKu7dCjVxw== dependencies: type-detect "4.0.8" +"@sinonjs/fake-timers@^6.0.0", "@sinonjs/fake-timers@^6.0.1": + version "6.0.1" + resolved "https://registry.yarnpkg.com/@sinonjs/fake-timers/-/fake-timers-6.0.1.tgz#293674fccb3262ac782c7aadfdeca86b10c75c40" + integrity sha512-MZPUxrmFubI36XS1DI3qmI0YdN1gks62JtFZvxR67ljjSNCeK6U08Zx4msEWOXuofgqUt6zPHSi1H9fbjR/NRA== + dependencies: + "@sinonjs/commons" "^1.7.0" + "@sinonjs/formatio@^3.2.1": - version "3.2.1" - resolved "https://registry.yarnpkg.com/@sinonjs/formatio/-/formatio-3.2.1.tgz#52310f2f9bcbc67bdac18c94ad4901b95fde267e" - integrity sha512-tsHvOB24rvyvV2+zKMmPkZ7dXX6LSLKZ7aOtXY6Edklp0uRcgGpOsQTTGTcWViFyx4uhWc6GV8QdnALbIbIdeQ== + version "3.2.2" + resolved "https://registry.yarnpkg.com/@sinonjs/formatio/-/formatio-3.2.2.tgz#771c60dfa75ea7f2d68e3b94c7e888a78781372c" + integrity sha512-B8SEsgd8gArBLMD6zpRw3juQ2FVSsmdd7qlevyDqzS9WTCtvF55/gAL+h6gue8ZvPYcdiPdvueM/qm//9XzyTQ== dependencies: "@sinonjs/commons" "^1" "@sinonjs/samsam" "^3.1.0" -"@sinonjs/samsam@^3.1.0": - version "3.3.2" - resolved "https://registry.yarnpkg.com/@sinonjs/samsam/-/samsam-3.3.2.tgz#63942e3d5eb0b79f6de3bef9abfad15fb4b6401b" - integrity sha512-ILO/rR8LfAb60Y1Yfp9vxfYAASK43NFC2mLzpvLUbCQY/Qu8YwReboseu8aheCEkyElZF2L2T9mHcR2bgdvZyA== +"@sinonjs/formatio@^5.0.1": + version "5.0.1" + resolved "https://registry.yarnpkg.com/@sinonjs/formatio/-/formatio-5.0.1.tgz#f13e713cb3313b1ab965901b01b0828ea6b77089" + integrity sha512-KaiQ5pBf1MpS09MuA0kp6KBQt2JUOQycqVG1NZXvzeaXe5LGFqAKueIS0bw4w0P9r7KuBSVdUk5QjXsUdu2CxQ== dependencies: - "@sinonjs/commons" "^1.0.2" - array-from "^2.1.1" - lodash "^4.17.11" + "@sinonjs/commons" "^1" + "@sinonjs/samsam" "^5.0.2" -"@sinonjs/samsam@^3.3.3": +"@sinonjs/samsam@^3.1.0", "@sinonjs/samsam@^3.3.3": version "3.3.3" resolved "https://registry.yarnpkg.com/@sinonjs/samsam/-/samsam-3.3.3.tgz#46682efd9967b259b81136b9f120fd54585feb4a" integrity sha512-bKCMKZvWIjYD0BLGnNrxVuw4dkWCYsLqFOUWw8VgKF/+5Y+mE7LfHWPIYoDXowH+3a9LsWDMo0uAP8YDosPvHQ== @@ -984,98 +1164,68 @@ array-from "^2.1.1" lodash "^4.17.15" +"@sinonjs/samsam@^5.0.2", "@sinonjs/samsam@^5.0.3": + version "5.0.3" + resolved "https://registry.yarnpkg.com/@sinonjs/samsam/-/samsam-5.0.3.tgz#86f21bdb3d52480faf0892a480c9906aa5a52938" + integrity sha512-QucHkc2uMJ0pFGjJUDP3F9dq5dx8QIaqISl9QgwLOh6P9yv877uONPGXh/OH/0zmM3tW1JjuJltAZV2l7zU+uQ== + dependencies: + "@sinonjs/commons" "^1.6.0" + lodash.get "^4.4.2" + type-detect "^4.0.8" + "@sinonjs/text-encoding@^0.7.1": version "0.7.1" resolved "https://registry.yarnpkg.com/@sinonjs/text-encoding/-/text-encoding-0.7.1.tgz#8da5c6530915653f3a1f38fd5f101d8c3f8079c5" integrity sha512-+iTbntw2IZPb/anVDbypzfQa+ay64MW0Zo8aJ8gZPWMMK6/OubMVb6lUPMagqjOPnmtauXnFCACVl3O7ogjeqQ== -"@slack/client@5.0.2": - version "5.0.2" - resolved "https://registry.yarnpkg.com/@slack/client/-/client-5.0.2.tgz#75a45bdd86a9eb8eaba2febb0bbdfeb281cf02e1" - integrity sha512-HurKTUBZlwj/1cnZ6QOHpYR7k+G62WlL+13DgYD7onVRnQWggkIyCg+ymX1kQn6txzNdUyDEaixyCvBvmhH8tQ== +"@szmarczak/http-timer@^1.1.2": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@szmarczak/http-timer/-/http-timer-1.1.2.tgz#b1665e2c461a2cd92f4c1bbf50d5454de0d4b421" + integrity sha512-XIB2XbzHTN6ieIjfIMV9hlVcfPU26s2vafYWQcZHWXHOxiaRZYEDKEwdl129Zyg50+foYV2jCgtrqSA6qNuNSA== dependencies: - "@slack/logger" "^1.0.0" - "@slack/rtm-api" "^5.0.2" - "@slack/types" "^1.1.0" - "@slack/web-api" "^5.1.0" - "@slack/webhook" "^5.0.1" + defer-to-connect "^1.0.1" -"@slack/logger@^1.0.0": - version "1.0.0" - resolved "https://registry.yarnpkg.com/@slack/logger/-/logger-1.0.0.tgz#e529974c7111f8a62d794a79cd807eee1628088b" - integrity sha512-QDYhQR/58xKfB5iquvQwfpxPsmKPP/5SuDp8hRhZeUluCHsP1qBCOc3sW2Xb3cydxK0PAEnkLbBJf/ezsGwtlA== +"@turf/boolean-point-in-polygon@^6.0.1": + version "6.0.1" + resolved "https://registry.yarnpkg.com/@turf/boolean-point-in-polygon/-/boolean-point-in-polygon-6.0.1.tgz#5836677afd77d2ee391af0056a0c29b660edfa32" + integrity sha512-FKLOZ124vkJhjzNSDcqpwp2NvfnsbYoUOt5iAE7uskt4svix5hcjIEgX9sELFTJpbLGsD1mUbKdfns8tZxcMNg== dependencies: - "@types/node" ">=8.9.0" + "@turf/helpers" "6.x" + "@turf/invariant" "6.x" -"@slack/rtm-api@^5.0.2": - version "5.0.2" - resolved "https://registry.yarnpkg.com/@slack/rtm-api/-/rtm-api-5.0.2.tgz#b87c71635fa59d306ec78f9e2bc3dfd22b2c6cd5" - integrity sha512-eKQM3RD5TM4G0+nGsL9NJUT4g9khdI9ACJjbPjHO2cq+KsB1rClct1zNWdF40E8QtlpOL5F9/dSap+H4UHlcRA== - dependencies: - "@slack/logger" "^1.0.0" - "@slack/web-api" "^5.1.0" - "@types/node" ">=8.9.0" - "@types/p-queue" "^2.3.2" - "@types/ws" "^5.1.1" - eventemitter3 "^3.1.0" - finity "^0.5.4" - p-cancelable "^1.1.0" - p-queue "^2.4.2" - ws "^5.2.0" +"@turf/helpers@6.x", "@turf/helpers@^6.1.4": + version "6.1.4" + resolved "https://registry.yarnpkg.com/@turf/helpers/-/helpers-6.1.4.tgz#d6fd7ebe6782dd9c87dca5559bda5c48ae4c3836" + integrity sha512-vJvrdOZy1ngC7r3MDA7zIGSoIgyrkWcGnNIEaqn/APmw+bVLF2gAW7HIsdTxd12s5wQMqEpqIQrmrbRRZ0xC7g== -"@slack/types@^1.1.0": - version "1.1.0" - resolved "https://registry.yarnpkg.com/@slack/types/-/types-1.1.0.tgz#64465b6c7e2db66498f335934335da81c15812e7" - integrity sha512-uak4Jbi8nlX8xmElkPt1ixxVQXMKdBRbzBayn5bRzZ9Yx3bQGlyJdFs6GjEKI+fvFP0ZTiGWKOzlJTH3Tm/9fg== - -"@slack/web-api@^5.1.0": - version "5.1.0" - resolved "https://registry.yarnpkg.com/@slack/web-api/-/web-api-5.1.0.tgz#ce04e0df08831d9ec5427df0abb3f461c3f8a2f5" - integrity sha512-bnzS1I8iOfNB4xSX0zQ/uRBnWTy7iPnAd0y+H/D8YRbEiXqAWaxlVQB8pio54hegSVcRRWRKFb2fyViSraMXqw== - dependencies: - "@slack/logger" "^1.0.0" - "@slack/types" "^1.1.0" - "@types/is-stream" "^1.1.0" - "@types/node" ">=8.9.0" - "@types/p-queue" "^2.3.2" - axios "^0.18.0" - eventemitter3 "^3.1.0" - form-data "^2.5.0" - is-stream "^1.1.0" - p-queue "^2.4.2" - p-retry "^4.0.0" - -"@slack/webhook@^5.0.1": - version "5.0.1" - resolved "https://registry.yarnpkg.com/@slack/webhook/-/webhook-5.0.1.tgz#be50fca83fae98b7005ac1a13859a78a0386520b" - integrity sha512-cTp0xOdd28gbMAGR80IzcG2QtwDPO0oq7NopxbitCUiDYVC40r7syJB86MBkt9spABOl1uNjB26tA9Lhe3XBbQ== +"@turf/invariant@6.x": + version "6.1.2" + resolved "https://registry.yarnpkg.com/@turf/invariant/-/invariant-6.1.2.tgz#6013ed6219f9ac2edada9b31e1dfa5918eb0a2f7" + integrity sha512-WU08Ph8j0J2jVGlQCKChXoCtI50BB3yEH21V++V0T4cR1T27HKCxkehV2sYMwTierfMBgjwSwDIsxnR4/2mWXg== dependencies: - "@slack/types" "^1.1.0" - "@types/node" ">=8.9.0" - axios "^0.18.0" - -"@types/bluebird@*": - version "3.5.27" - resolved "https://registry.yarnpkg.com/@types/bluebird/-/bluebird-3.5.27.tgz#61eb4d75dc6bfbce51cf49ee9bbebe941b2cb5d0" - integrity sha512-6BmYWSBea18+tSjjSC3QIyV93ZKAeNWGM7R6aYt1ryTZXrlHF+QLV0G2yV0viEGVyRkyQsWfMoJ0k/YghBX5sQ== + "@turf/helpers" "6.x" -"@types/bluebird@3.5.28": - version "3.5.28" - resolved "https://registry.yarnpkg.com/@types/bluebird/-/bluebird-3.5.28.tgz#04c1a520ff076649236bc8ca21198542ce2bdb09" - integrity sha512-0Vk/kqkukxPKSzP9c8WJgisgGDx5oZDbsLLWIP5t70yThO/YleE+GEm2S1GlRALTaack3O7U5OS5qEm7q2kciA== +"@types/anymatch@*": + version "1.3.1" + resolved "https://registry.yarnpkg.com/@types/anymatch/-/anymatch-1.3.1.tgz#336badc1beecb9dacc38bea2cf32adf627a8421a" + integrity sha512-/+CRPXpBDpo2RK9C68N3b2cOvO0Cf5B9aPijHsoDQTHivnGSObdOF2BRQOYjojWTDy6nQvMjmqRXIxH55VjxxA== -"@types/body-parser@*": - version "1.17.0" - resolved "https://registry.yarnpkg.com/@types/body-parser/-/body-parser-1.17.0.tgz#9f5c9d9bd04bb54be32d5eb9fc0d8c974e6cf58c" - integrity sha512-a2+YeUjPkztKJu5aIF2yArYFQQp8d51wZ7DavSHjFuY1mqVgidGyzEQ41JIVNy82fXj8yPgy2vJmfIywgESW6w== +"@types/async-retry@1.4.1": + version "1.4.1" + resolved "https://registry.yarnpkg.com/@types/async-retry/-/async-retry-1.4.1.tgz#3b136a707b7a850f4947a727eb0f7b473b601992" + integrity sha512-hDI5Ttk9SUmDLcD/Yl2VuWQRGYZjJ7aaJFeRlomUOz/iTKSE7yA55SwY87QwjiZgwhMlVAKoT1rl08UyQoheag== dependencies: - "@types/connect" "*" - "@types/node" "*" + "@types/retry" "*" -"@types/body-parser@1.17.1": - version "1.17.1" - resolved "https://registry.yarnpkg.com/@types/body-parser/-/body-parser-1.17.1.tgz#18fcf61768fb5c30ccc508c21d6fd2e8b3bf7897" - integrity sha512-RoX2EZjMiFMjZh9lmYrwgoP9RTpAjSHiJxdp4oidAQVO02T7HER3xj9UKue5534ULWeqVEkujhWcyvUce+d68w== +"@types/bluebird@*", "@types/bluebird@3.5.30": + version "3.5.30" + resolved "https://registry.yarnpkg.com/@types/bluebird/-/bluebird-3.5.30.tgz#ee034a0eeea8b84ed868b1aa60d690b08a6cfbc5" + integrity sha512-8LhzvcjIoqoi1TghEkRMkbbmM+jhHnBokPGkJWjclMK+Ks0MxEBow3/p2/iFTZ+OIbJHQDSfpgdZEb+af3gfVw== + +"@types/body-parser@*", "@types/body-parser@1.19.0": + version "1.19.0" + resolved "https://registry.yarnpkg.com/@types/body-parser/-/body-parser-1.19.0.tgz#0685b3c47eb3006ffed117cdd55164b61f80538f" + integrity sha512-W98JrE0j2K78swW4ukqMleo8R7h/pFETjM2DQ90MF6XK2i4LO4W3gQ71Lt4w3bfm2EvVSyWHplECvB5sK22yFQ== dependencies: "@types/connect" "*" "@types/node" "*" @@ -1085,10 +1235,15 @@ resolved "https://registry.yarnpkg.com/@types/caseless/-/caseless-0.12.2.tgz#f65d3d6389e01eeb458bd54dc8f52b95a9463bc8" integrity sha512-6ckxMjBBD8URvjB6J3NcnuAn5Pkl7t3TizAg+xdlzzQGSPSmBcXf8KoIH0ua/i+tio+ZRUHEXp0HEmvaR4kt0w== +"@types/color-name@^1.1.1": + version "1.1.1" + resolved "https://registry.yarnpkg.com/@types/color-name/-/color-name-1.1.1.tgz#1c1261bbeaa10a8055bbc5d8ab84b7b2afc846a0" + integrity sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ== + "@types/connect@*": - version "3.4.32" - resolved "https://registry.yarnpkg.com/@types/connect/-/connect-3.4.32.tgz#aa0e9616b9435ccad02bc52b5b454ffc2c70ba28" - integrity sha512-4r8qa0quOvh7lGD0pre62CAb1oni1OO6ecJLGCezTmhQ8Fz50Arx9RUszryR8KlgK6avuSXvviL6yWyViQABOg== + version "3.4.33" + resolved "https://registry.yarnpkg.com/@types/connect/-/connect-3.4.33.tgz#31610c901eca573b8713c3330abc6e6b9f588546" + integrity sha512-2+FrkXY4zllzTNfJth7jOqEHC+enpLeGslEhpnTAkg21GkRrWV4SsAtqchtT4YS9/nODBU2/ZfsBY2X4J/dX7A== dependencies: "@types/node" "*" @@ -1104,227 +1259,6 @@ dependencies: "@types/express" "*" -"@types/d3-array@*": - version "2.0.0" - resolved "https://registry.yarnpkg.com/@types/d3-array/-/d3-array-2.0.0.tgz#a0d63a296a2d8435a9ec59393dcac746c6174a96" - integrity sha512-rGqfPVowNDTszSFvwoZIXvrPG7s/qKzm9piCRIH6xwTTRu7pPZ3ootULFnPkTt74B6i5lN0FpLQL24qGOw1uZA== - -"@types/d3-array@^1": - version "1.2.7" - resolved "https://registry.yarnpkg.com/@types/d3-array/-/d3-array-1.2.7.tgz#34dc654d34fc058c41c31dbca1ed68071a8fcc17" - integrity sha512-51vHWuUyDOi+8XuwPrTw3cFqyh2Slg9y8COYkRfjCPG9TfYqY0hoNPzv/8BrcAy0FeQBzqEo/D/8Nk2caOQJnA== - -"@types/d3-axis@*": - version "1.0.12" - resolved "https://registry.yarnpkg.com/@types/d3-axis/-/d3-axis-1.0.12.tgz#8c124edfcc02f3b3a9cdaa2a28b8a20341401799" - integrity sha512-BZISgSD5M8TgURyNtcPAmUB9sk490CO1Thb6/gIn0WZTt3Y50IssX+2Z0vTccoqZksUDTep0b+o4ofXslvNbqg== - dependencies: - "@types/d3-selection" "*" - -"@types/d3-brush@*": - version "1.0.10" - resolved "https://registry.yarnpkg.com/@types/d3-brush/-/d3-brush-1.0.10.tgz#aa9b5545d816c29d19cff20118f236713af8e9fb" - integrity sha512-J8jREATIrfJaAfhJivqaEKPnJsRlwwrOPje+ABqZFgamADjll+q9zaDXnYyjiGPPsiJEU+Qq9jQi5rECxIOfhg== - dependencies: - "@types/d3-selection" "*" - -"@types/d3-chord@*": - version "1.0.9" - resolved "https://registry.yarnpkg.com/@types/d3-chord/-/d3-chord-1.0.9.tgz#ccc5de03ff079025491b7aa6b750670a140b45ae" - integrity sha512-UA6lI9CVW5cT5Ku/RV4hxoFn4mKySHm7HEgodtfRthAj1lt9rKZEPon58vyYfk+HIAm33DtJJgZwMXy2QgyPXw== - -"@types/d3-collection@*": - version "1.0.8" - resolved "https://registry.yarnpkg.com/@types/d3-collection/-/d3-collection-1.0.8.tgz#aa9552c570a96e33c132e0fd20e331f64baa9dd5" - integrity sha512-y5lGlazdc0HNO0F3UUX2DPE7OmYvd9Kcym4hXwrJcNUkDaypR5pX+apuMikl9LfTxKItJsY9KYvzBulpCKyvuQ== - -"@types/d3-color@*": - version "1.2.2" - resolved "https://registry.yarnpkg.com/@types/d3-color/-/d3-color-1.2.2.tgz#80cf7cfff7401587b8f89307ba36fe4a576bc7cf" - integrity sha512-6pBxzJ8ZP3dYEQ4YjQ+NVbQaOflfgXq/JbDiS99oLobM2o72uAST4q6yPxHv6FOTCRC/n35ktuo8pvw/S4M7sw== - -"@types/d3-contour@*": - version "1.3.0" - resolved "https://registry.yarnpkg.com/@types/d3-contour/-/d3-contour-1.3.0.tgz#1a408b121fa5e341f715e3055303ef3079fc7eb0" - integrity sha512-AUCUIjEnC5lCGBM9hS+MryRaFLIrPls4Rbv6ktqbd+TK/RXZPwOy9rtBWmGpbeXcSOYCJTUDwNJuEnmYPJRxHQ== - dependencies: - "@types/d3-array" "*" - "@types/geojson" "*" - -"@types/d3-dispatch@*": - version "1.0.7" - resolved "https://registry.yarnpkg.com/@types/d3-dispatch/-/d3-dispatch-1.0.7.tgz#6721aefbb9862ce78c20a87a1490c21f57c3ed7f" - integrity sha512-M+z84G7UKwK6hEPnGCSccOg8zJ3Nk2hgDQ9sCstHXgsFU0sMxlIZVKqKB5oxUDbALqQG6ucg0G9e8cmOSlishg== - -"@types/d3-drag@*": - version "1.2.3" - resolved "https://registry.yarnpkg.com/@types/d3-drag/-/d3-drag-1.2.3.tgz#d8ddccca28e939e9c689bea6f40a937e48c39051" - integrity sha512-rWB5SPvkYVxW3sqUxHOJUZwifD0KqvKwvt1bhNqcLpW6Azsd0BJgRNcyVW8GAferaAk5r8dzeZnf9zKlg9+xMQ== - dependencies: - "@types/d3-selection" "*" - -"@types/d3-dsv@*": - version "1.0.36" - resolved "https://registry.yarnpkg.com/@types/d3-dsv/-/d3-dsv-1.0.36.tgz#e91129d7c02b1b814838d001e921e8b9a67153d0" - integrity sha512-jbIWQ27QJcBNMZbQv0NSQMHnBDCmxghAxePxgyiPH1XPCRkOsTBei7jcdi3fDrUCGpCV3lKrSZFSlOkhUQVClA== - -"@types/d3-ease@*": - version "1.0.8" - resolved "https://registry.yarnpkg.com/@types/d3-ease/-/d3-ease-1.0.8.tgz#b440761fb85985d76259ec9c5bf01c4c56778ac2" - integrity sha512-VRf8czVWHSJPoUWxMunzpePK02//wHDAswknU8QWzcyrQn6pqe46bHRYi2smSpw5VjsT2CG8k/QeWIdWPS3Bmg== - -"@types/d3-fetch@*": - version "1.1.5" - resolved "https://registry.yarnpkg.com/@types/d3-fetch/-/d3-fetch-1.1.5.tgz#51601f79dd4653b5d84e6a3176d78145e065db5e" - integrity sha512-o9c0ItT5/Gl3wbNuVpzRnYX1t3RghzeWAjHUVLuyZJudiTxC4f/fC0ZPFWLQ2lVY8pAMmxpV8TJ6ETYCgPeI3A== - dependencies: - "@types/d3-dsv" "*" - -"@types/d3-force@*": - version "1.2.1" - resolved "https://registry.yarnpkg.com/@types/d3-force/-/d3-force-1.2.1.tgz#c28803ea36fe29788db69efa0ad6c2dc09544e83" - integrity sha512-jqK+I36uz4kTBjyk39meed5y31Ab+tXYN/x1dn3nZEus9yOHCLc+VrcIYLc/aSQ0Y7tMPRlIhLetulME76EiiA== - -"@types/d3-format@*": - version "1.3.1" - resolved "https://registry.yarnpkg.com/@types/d3-format/-/d3-format-1.3.1.tgz#35bf88264bd6bcda39251165bb827f67879c4384" - integrity sha512-KAWvReOKMDreaAwOjdfQMm0HjcUMlQG47GwqdVKgmm20vTd2pucj0a70c3gUSHrnsmo6H2AMrkBsZU2UhJLq8A== - -"@types/d3-geo@*": - version "1.11.1" - resolved "https://registry.yarnpkg.com/@types/d3-geo/-/d3-geo-1.11.1.tgz#e96ec91f16221d87507fec66b2cc889f52d2493e" - integrity sha512-Ox8WWOG3igDRoep/dNsGbOiSJYdUG3ew/6z0ETvHyAtXZVBjOE0S96zSSmzgl0gqQ3RdZjn2eeJOj9oRcMZPkQ== - dependencies: - "@types/geojson" "*" - -"@types/d3-hierarchy@*": - version "1.1.6" - resolved "https://registry.yarnpkg.com/@types/d3-hierarchy/-/d3-hierarchy-1.1.6.tgz#4c017521900813ea524c9ecb8d7985ec26a9ad9a" - integrity sha512-vvSaIDf/Ov0o3KwMT+1M8+WbnnlRiGjlGD5uvk83a1mPCTd/E5x12bUJ/oP55+wUY/4Kb5kc67rVpVGJ2KUHxg== - -"@types/d3-interpolate@*": - version "1.3.1" - resolved "https://registry.yarnpkg.com/@types/d3-interpolate/-/d3-interpolate-1.3.1.tgz#1c280511f622de9b0b47d463fa55f9a4fd6f5fc8" - integrity sha512-z8Zmi08XVwe8e62vP6wcA+CNuRhpuUU5XPEfqpG0hRypDE5BWNthQHB1UNWWDB7ojCbGaN4qBdsWp5kWxhT1IQ== - dependencies: - "@types/d3-color" "*" - -"@types/d3-path@*": - version "1.0.8" - resolved "https://registry.yarnpkg.com/@types/d3-path/-/d3-path-1.0.8.tgz#48e6945a8ff43ee0a1ce85c8cfa2337de85c7c79" - integrity sha512-AZGHWslq/oApTAHu9+yH/Bnk63y9oFOMROtqPAtxl5uB6qm1x2lueWdVEjsjjV3Qc2+QfuzKIwIR5MvVBakfzA== - -"@types/d3-polygon@*": - version "1.0.7" - resolved "https://registry.yarnpkg.com/@types/d3-polygon/-/d3-polygon-1.0.7.tgz#7b3947aa2d48287ff535230d3d396668ab17bfdf" - integrity sha512-Xuw0eSjQQKs8jTiNbntWH0S+Xp+JyhqxmQ0YAQ3rDu6c3kKMFfgsaGN7Jv5u3zG6yVX/AsLP/Xs/QRjmi9g43Q== - -"@types/d3-quadtree@*": - version "1.0.7" - resolved "https://registry.yarnpkg.com/@types/d3-quadtree/-/d3-quadtree-1.0.7.tgz#8e29464ff5b326f6612c1428d9362b4b35de2b70" - integrity sha512-0ajFawWicfjsaCLh6NzxOyVDYhQAmMFbsiI3MPGLInorauHFEh9/Cl6UHNf+kt/J1jfoxKY/ZJaKAoDpbvde5Q== - -"@types/d3-random@*": - version "1.1.2" - resolved "https://registry.yarnpkg.com/@types/d3-random/-/d3-random-1.1.2.tgz#6f77e8b7bb64ac393f92d33fe8f71038bc4f3cde" - integrity sha512-Jui+Zn28pQw/3EayPKaN4c/PqTvqNbIPjHkgIIFnxne1FdwNjfHtAIsZIBMKlquQNrrMjFzCrlF2gPs3xckqaA== - -"@types/d3-scale-chromatic@*": - version "1.3.1" - resolved "https://registry.yarnpkg.com/@types/d3-scale-chromatic/-/d3-scale-chromatic-1.3.1.tgz#a294ae688634027870f0307bf8802f863aa2ddb3" - integrity sha512-Ny3rLbV5tnmqgW7w/poCcef4kXP8mHPo/p8EjTS5d9OUk8MlqAeRaM8eF7Vyv7QMLiIXNE94Pa1cMLSPkXQBoQ== - -"@types/d3-scale@*": - version "2.1.1" - resolved "https://registry.yarnpkg.com/@types/d3-scale/-/d3-scale-2.1.1.tgz#405e58771ec6ae7b8f7b4178ee1887620759e8f7" - integrity sha512-kNTkbZQ+N/Ip8oX9PByXfDLoCSaZYm+VUOasbmsa6KD850/ziMdYepg/8kLg2plHzoLANdMqPoYQbvExevLUHg== - dependencies: - "@types/d3-time" "*" - -"@types/d3-selection@*": - version "1.4.1" - resolved "https://registry.yarnpkg.com/@types/d3-selection/-/d3-selection-1.4.1.tgz#fa1f8710a6b5d7cfe5c6caa61d161be7cae4a022" - integrity sha512-bv8IfFYo/xG6dxri9OwDnK3yCagYPeRIjTlrcdYJSx+FDWlCeBDepIHUpqROmhPtZ53jyna0aUajZRk0I3rXNA== - -"@types/d3-shape@*": - version "1.3.1" - resolved "https://registry.yarnpkg.com/@types/d3-shape/-/d3-shape-1.3.1.tgz#1b4f92b7efd7306fe2474dc6ee94c0f0ed2e6ab6" - integrity sha512-usqdvUvPJ7AJNwpd2drOzRKs1ELie53p2m2GnPKr076/ADM579jVTJ5dPsoZ5E/CMNWk8lvPWYQSvilpp6jjwg== - dependencies: - "@types/d3-path" "*" - -"@types/d3-time-format@*": - version "2.1.1" - resolved "https://registry.yarnpkg.com/@types/d3-time-format/-/d3-time-format-2.1.1.tgz#dd2c79ec4575f1355484ab6b10407824668eba42" - integrity sha512-tJSyXta8ZyJ52wDDHA96JEsvkbL6jl7wowGmuf45+fAkj5Y+SQOnz0N7/H68OWmPshPsAaWMQh+GAws44IzH3g== - -"@types/d3-time@*": - version "1.0.10" - resolved "https://registry.yarnpkg.com/@types/d3-time/-/d3-time-1.0.10.tgz#d338c7feac93a98a32aac875d1100f92c7b61f4f" - integrity sha512-aKf62rRQafDQmSiv1NylKhIMmznsjRN+MnXRXTqHoqm0U/UZzVpdrtRnSIfdiLS616OuC1soYeX1dBg2n1u8Xw== - -"@types/d3-timer@*": - version "1.0.9" - resolved "https://registry.yarnpkg.com/@types/d3-timer/-/d3-timer-1.0.9.tgz#aed1bde0cf18920d33f5d44839d73de393633fd3" - integrity sha512-WvfJ3LFxBbWjqRGz9n7GJt08RrTHPJDVsIwwoCMROlqF+iDacYiAFjf9oqnq0mXpb2juA2N/qjKP+MKdal3YNQ== - -"@types/d3-transition@*": - version "1.1.4" - resolved "https://registry.yarnpkg.com/@types/d3-transition/-/d3-transition-1.1.4.tgz#3c7a35ae9acfc59dfef1eb7308ebabf0fc0680de" - integrity sha512-/vsmKVUIXEyCcIXYAlw7bnYkIs9/J/nZbptRJFKUN3FdXq/dF6j9z9xXzerkyU6TDHLrMrwx9eGwdKyTIy/j9w== - dependencies: - "@types/d3-selection" "*" - -"@types/d3-voronoi@*": - version "1.1.9" - resolved "https://registry.yarnpkg.com/@types/d3-voronoi/-/d3-voronoi-1.1.9.tgz#7bbc210818a3a5c5e0bafb051420df206617c9e5" - integrity sha512-DExNQkaHd1F3dFPvGA/Aw2NGyjMln6E9QzsiqOcBgnE+VInYnFBHBBySbZQts6z6xD+5jTfKCP7M4OqMyVjdwQ== - -"@types/d3-zoom@*": - version "1.7.4" - resolved "https://registry.yarnpkg.com/@types/d3-zoom/-/d3-zoom-1.7.4.tgz#9226ffd2bd3846ec0e4a4e2bff211612d3aafad5" - integrity sha512-5jnFo/itYhJeB2khO/lKe730kW/h2EbKMOvY0uNp3+7NdPm4w63DwPEMxifQZ7n902xGYK5DdU67FmToSoy4VA== - dependencies: - "@types/d3-interpolate" "*" - "@types/d3-selection" "*" - -"@types/d3@5.7.2": - version "5.7.2" - resolved "https://registry.yarnpkg.com/@types/d3/-/d3-5.7.2.tgz#52235eb71a1d3ca171d6dca52a58f5ccbe0254cc" - integrity sha512-7/wClB8ycneWGy3jdvLfXKTd5SoTg9hji7IdJ0RuO9xTY54YpJ8zlcFADcXhY1J3kCBwxp+/1jeN6a5OMwgYOw== - dependencies: - "@types/d3-array" "^1" - "@types/d3-axis" "*" - "@types/d3-brush" "*" - "@types/d3-chord" "*" - "@types/d3-collection" "*" - "@types/d3-color" "*" - "@types/d3-contour" "*" - "@types/d3-dispatch" "*" - "@types/d3-drag" "*" - "@types/d3-dsv" "*" - "@types/d3-ease" "*" - "@types/d3-fetch" "*" - "@types/d3-force" "*" - "@types/d3-format" "*" - "@types/d3-geo" "*" - "@types/d3-hierarchy" "*" - "@types/d3-interpolate" "*" - "@types/d3-path" "*" - "@types/d3-polygon" "*" - "@types/d3-quadtree" "*" - "@types/d3-random" "*" - "@types/d3-scale" "*" - "@types/d3-scale-chromatic" "*" - "@types/d3-selection" "*" - "@types/d3-shape" "*" - "@types/d3-time" "*" - "@types/d3-time-format" "*" - "@types/d3-timer" "*" - "@types/d3-transition" "*" - "@types/d3-voronoi" "*" - "@types/d3-zoom" "*" - "@types/eslint-visitor-keys@^1.0.0": version "1.0.0" resolved "https://registry.yarnpkg.com/@types/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz#1ee30d79544ca84d68d4b3cdb0af4f205663dd2d" @@ -1335,42 +1269,57 @@ resolved "https://registry.yarnpkg.com/@types/events/-/events-3.0.0.tgz#2862f3f58a9a7f7c3e78d79f130dd4d71c25c2a7" integrity sha512-EaObqwIvayI5a8dCzhFrjKzVwKLxjoG9T6Ppd5CEo07LRKfQ8Yokw54r5+Wq7FaBQ+yXRvQAYPrHwya1/UFt9g== +"@types/express-jwt@0.0.42": + version "0.0.42" + resolved "https://registry.yarnpkg.com/@types/express-jwt/-/express-jwt-0.0.42.tgz#4f04e1fadf9d18725950dc041808a4a4adf7f5ae" + integrity sha512-WszgUddvM1t5dPpJ3LhWNH8kfNN8GPIBrAGxgIYXVCEGx6Bx4A036aAuf/r5WH9DIEdlmp7gHOYvSM6U87B0ag== + dependencies: + "@types/express" "*" + "@types/express-unless" "*" + "@types/express-serve-static-core@*": - version "4.16.7" - resolved "https://registry.yarnpkg.com/@types/express-serve-static-core/-/express-serve-static-core-4.16.7.tgz#50ba6f8a691c08a3dd9fa7fba25ef3133d298049" - integrity sha512-847KvL8Q1y3TtFLRTXcVakErLJQgdpFSaq+k043xefz9raEf0C7HalpSY7OW5PyjCnY8P7bPW5t/Co9qqp+USg== + version "4.17.7" + resolved "https://registry.yarnpkg.com/@types/express-serve-static-core/-/express-serve-static-core-4.17.7.tgz#dfe61f870eb549dc6d7e12050901847c7d7e915b" + integrity sha512-EMgTj/DF9qpgLXyc+Btimg+XoH7A2liE8uKul8qSmMTHCeNYzydDKFdsJskDvw42UsesCnhO63dO0Grbj8J4Dw== dependencies: "@types/node" "*" + "@types/qs" "*" "@types/range-parser" "*" -"@types/express@*": - version "4.17.0" - resolved "https://registry.yarnpkg.com/@types/express/-/express-4.17.0.tgz#49eaedb209582a86f12ed9b725160f12d04ef287" - integrity sha512-CjaMu57cjgjuZbh9DpkloeGxV45CnMGlVd+XpG7Gm9QgVrd7KFq+X4HY0vM+2v0bczS48Wg7bvnMY5TN+Xmcfw== +"@types/express-unless@*": + version "0.5.1" + resolved "https://registry.yarnpkg.com/@types/express-unless/-/express-unless-0.5.1.tgz#4f440b905e42bbf53382b8207bc337dc5ff9fd1f" + integrity sha512-5fuvg7C69lemNgl0+v+CUxDYWVPSfXHhJPst4yTLcqi4zKJpORCxnDrnnilk3k0DTq/WrAUdvXFs01+vUqUZHw== dependencies: - "@types/body-parser" "*" - "@types/express-serve-static-core" "*" - "@types/serve-static" "*" + "@types/express" "*" -"@types/express@4.17.1": - version "4.17.1" - resolved "https://registry.yarnpkg.com/@types/express/-/express-4.17.1.tgz#4cf7849ae3b47125a567dfee18bfca4254b88c5c" - integrity sha512-VfH/XCP0QbQk5B5puLqTLEeFgR8lfCJHZJKkInZ9mkYd+u8byX0kztXEQxEk4wZXJs8HI+7km2ALXjn4YKcX9w== +"@types/express@*", "@types/express@4.17.6": + version "4.17.6" + resolved "https://registry.yarnpkg.com/@types/express/-/express-4.17.6.tgz#6bce49e49570507b86ea1b07b806f04697fac45e" + integrity sha512-n/mr9tZI83kd4azlPG5y997C/M4DNABK9yErhFM6hKdym4kkmd9j0vtsJyjFIwfRBxtrxZtAfGZCNRIBMFLK5w== dependencies: "@types/body-parser" "*" "@types/express-serve-static-core" "*" + "@types/qs" "*" "@types/serve-static" "*" -"@types/flat@0.0.28": - version "0.0.28" - resolved "https://registry.yarnpkg.com/@types/flat/-/flat-0.0.28.tgz#5c788149d85a6cf8ff5f5f000acdd912cdea4274" - integrity sha1-XHiBSdhabPj/X18ACs3ZEs3qQnQ= +"@types/flat@5.0.0": + version "5.0.0" + resolved "https://registry.yarnpkg.com/@types/flat/-/flat-5.0.0.tgz#de18742aea57ce3dd2c093c7d2ba8fd3a4135b53" + integrity sha512-GcgAp7RXXGmA61spVEKZYpIy3/iV6GHbTW9f9kaKwHVQgnWitt6X026e+3N6j8ep1bkIWj83qPHQ3Y9Ft8FqiQ== -"@types/geojson@*", "@types/geojson@7946.0.7": +"@types/geojson@7946.0.7": version "7946.0.7" resolved "https://registry.yarnpkg.com/@types/geojson/-/geojson-7946.0.7.tgz#c8fa532b60a0042219cdf173ca21a975ef0666ad" integrity sha512-wE2v81i4C4Ol09RtsWFAqg3BUitWbHSpSlIo+bNdsCJijO9sjme+zm+73ZMCa/qMC8UEERxzGbvmr1cffo2SiQ== +"@types/git-revision-webpack-plugin@3.0.0": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@types/git-revision-webpack-plugin/-/git-revision-webpack-plugin-3.0.0.tgz#3eea6f68ff9dfa82fc1040bf70f67096181e30d4" + integrity sha512-WpeHN/O78FZpZaFbG8Yexaa8jI3ZibK1DAAZbPz3fWwbYj6d+FXbcQ/luXNBJIZZ6pQyhGJQaDh1K2kFZU3izA== + dependencies: + "@types/webpack" "*" + "@types/glob@^7.1.1": version "7.1.1" resolved "https://registry.yarnpkg.com/@types/glob/-/glob-7.1.1.tgz#aa59a1c6e3fbc421e07ccd31a944c30eba521575" @@ -1381,11 +1330,9 @@ "@types/node" "*" "@types/hapi__joi@*": - version "15.0.3" - resolved "https://registry.yarnpkg.com/@types/hapi__joi/-/hapi__joi-15.0.3.tgz#eeeca99e3829636975bf39532d6e8279409c78f2" - integrity sha512-kncvQstpAQSjQ+J+tL2oYerjOEepv+yJHKM/JD/NmFZyDcy3rYOBcMJhdHRDjBxuSZu3ipGECszhgtmug9VSuw== - dependencies: - "@types/hapi__joi" "*" + version "17.1.0" + resolved "https://registry.yarnpkg.com/@types/hapi__joi/-/hapi__joi-17.1.0.tgz#b5324c423a49697ea0926f19cc90c6f5cbb68391" + integrity sha512-zqPGKJ3FUiut0NZUstLBm3gAkSMwWwn/soNi/KvefJfhlaQYYoLnMiMCaMN97dPK4iKjFSIVwRbHANSgUCKm7g== "@types/hapi__joi@15.0.4": version "15.0.4" @@ -1394,27 +1341,22 @@ dependencies: "@types/hapi__joi" "*" -"@types/is-stream@^1.1.0": - version "1.1.0" - resolved "https://registry.yarnpkg.com/@types/is-stream/-/is-stream-1.1.0.tgz#b84d7bb207a210f2af9bed431dc0fbe9c4143be1" - integrity sha512-jkZatu4QVbR60mpIzjINmtS1ZF4a/FqdTUTBeQDVOQ2PYyidtwFKr0B5G6ERukKwliq+7mIXvxyppwzG5EgRYg== - dependencies: - "@types/node" "*" - "@types/json-schema@^7.0.3": - version "7.0.3" - resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.3.tgz#bdfd69d61e464dcc81b25159c270d75a73c1a636" - integrity sha512-Il2DtDVRGDcqjDtE+rF8iqg1CArehSK84HZJCT7AMITlyXRBpuPhqGLDQMowraqqu1coEaimg4ZOqggt6L6L+A== + version "7.0.4" + resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.4.tgz#38fd73ddfd9b55abb1e1b2ed578cb55bd7b7d339" + integrity sha512-8+KAKzEvSUdeo+kmqnKrqgeE+LcA0tjYWFY7RPProVYwnqDjukzO+3b6dLD56rYX5TdWejnEOLJYOIeh4CXKuA== "@types/json5@^0.0.29": version "0.0.29" resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee" integrity sha1-7ihweulOEdK4J7y+UnC86n8+ce4= -"@types/jwt-decode@2.2.1": - version "2.2.1" - resolved "https://registry.yarnpkg.com/@types/jwt-decode/-/jwt-decode-2.2.1.tgz#afdf5c527fcfccbd4009b5fd02d1e18241f2d2f2" - integrity sha512-aWw2YTtAdT7CskFyxEX2K21/zSDStuf/ikI3yBqmwpwJF0pS+/IX5DWv+1UFffZIbruP6cnT9/LAJV1gFwAT1A== +"@types/jsonwebtoken@8.5.0": + version "8.5.0" + resolved "https://registry.yarnpkg.com/@types/jsonwebtoken/-/jsonwebtoken-8.5.0.tgz#2531d5e300803aa63279b232c014acf780c981c5" + integrity sha512-9bVao7LvyorRGZCw0VmH/dr7Og+NdjYSsKAxB43OQoComFbBgsEpoR9JW6+qSq/ogwVBg8GI2MfAlk4SYI4OLg== + dependencies: + "@types/node" "*" "@types/mime@*": version "2.0.1" @@ -1426,61 +1368,73 @@ resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-3.0.3.tgz#3dca0e3f33b200fc7d1139c0cd96c1268cadfd9d" integrity sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA== -"@types/mocha@5.2.7": - version "5.2.7" - resolved "https://registry.yarnpkg.com/@types/mocha/-/mocha-5.2.7.tgz#315d570ccb56c53452ff8638738df60726d5b6ea" - integrity sha512-NYrtPht0wGzhwe9+/idPaBB+TqkY9AhTvOLMkThm0IoEfLaiVQZwBwyJ5puCkO3AUCWrmcoePjp2mbFocKy4SQ== +"@types/minimist@^1.2.0": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@types/minimist/-/minimist-1.2.0.tgz#69a23a3ad29caf0097f06eda59b361ee2f0639f6" + integrity sha1-aaI6OtKcrwCX8G7aWbNh7i8GOfY= + +"@types/mocha@7.0.2": + version "7.0.2" + resolved "https://registry.yarnpkg.com/@types/mocha/-/mocha-7.0.2.tgz#b17f16cf933597e10d6d78eae3251e692ce8b0ce" + integrity sha512-ZvO2tAcjmMi8V/5Z3JsyofMe3hasRcaw88cto5etSVMwVQfeivGAlEYmaQgceUSVYFofVjT+ioHsATjdWcFt1w== "@types/mockdate@2.0.0": version "2.0.0" resolved "https://registry.yarnpkg.com/@types/mockdate/-/mockdate-2.0.0.tgz#aaf388a1ead3b0f5ed6dc1611956ea7b40a57d3c" integrity sha1-qvOIoerTsPXtbcFhGVbqe0ClfTw= -"@types/moment-timezone@0.5.12": - version "0.5.12" - resolved "https://registry.yarnpkg.com/@types/moment-timezone/-/moment-timezone-0.5.12.tgz#0fb680c03db194fe8ff4551eaeb1eec8d3d80e9f" - integrity sha512-hnHH2+Efg2vExr/dSz+IX860nSiyk9Sk4pJF2EmS11lRpMcNXeB4KBW5xcgw2QPsb9amTXdsVNEe5IoJXiT0uw== +"@types/moment-timezone@0.5.13": + version "0.5.13" + resolved "https://registry.yarnpkg.com/@types/moment-timezone/-/moment-timezone-0.5.13.tgz#0317ccc91eb4c7f4901704166166395c39276528" + integrity sha512-SWk1qM8DRssS5YR9L4eEX7WUhK/wc96aIr4nMa6p0kTk9YhGGOJjECVhIdPEj13fvJw72Xun69gScXSZ/UmcPg== dependencies: moment ">=2.14.0" -"@types/morgan@1.7.37": - version "1.7.37" - resolved "https://registry.yarnpkg.com/@types/morgan/-/morgan-1.7.37.tgz#ebdd0b0f0276073f85283bf4f03c7c54284874df" - integrity sha512-tIdEA10BcHcOumMmUiiYdw8lhiVVq62r0ghih5Xpp4WETkfsMiTUZL4w9jCI502BBOrKhFrAOGml9IeELvVaBA== +"@types/morgan@1.9.0": + version "1.9.0" + resolved "https://registry.yarnpkg.com/@types/morgan/-/morgan-1.9.0.tgz#342119ae57fe67d36b91537143fc5aef16c2479f" + integrity sha512-warrzirh5dlTMaETytBTKR886pRXwr+SMZD87ZE13gLMR8Pzz69SiYFkvoDaii78qGP1iyBIUYz5GiXyryO//A== + dependencies: + "@types/express" "*" + +"@types/multer@1.4.3": + version "1.4.3" + resolved "https://registry.yarnpkg.com/@types/multer/-/multer-1.4.3.tgz#bdff74b334c38a8ee1de9fbedb5d1d3dbc377422" + integrity sha512-tWsKbF5LYtXrJ7eOfI0aLBgEv9B7fnJe1JRXTj5+Z6EMfX0yHVsRFsNGnKyN8Bs0gtDv+JR37xAqsPnALyVTqg== dependencies: "@types/express" "*" -"@types/node@*", "@types/node@>=8.9.0": - version "12.6.8" - resolved "https://registry.yarnpkg.com/@types/node/-/node-12.6.8.tgz#e469b4bf9d1c9832aee4907ba8a051494357c12c" - integrity sha512-aX+gFgA5GHcDi89KG5keey2zf0WfZk/HAQotEamsK2kbey+8yGKcson0hbK8E+v0NArlCJQCqMP161YhV6ZXLg== +"@types/node-rsa@1.0.0": + version "1.0.0" + resolved "https://registry.yarnpkg.com/@types/node-rsa/-/node-rsa-1.0.0.tgz#4432df6227c5de734f5f0fbea2420ffb51c51e44" + integrity sha512-9hTSXhGDKotpq5XCm+r7wMgt5gl6bGH6VS/sV9bsKda4JmJYyOCdTenSkTUh7zsOihm2oCm5o2ZdfpUIGYKLzQ== + dependencies: + "@types/node" "*" -"@types/node@10.14.21": - version "10.14.21" - resolved "https://registry.yarnpkg.com/@types/node/-/node-10.14.21.tgz#4a9db7ef1d1671c0015e632c5fa3d46c86c58c1e" - integrity sha512-nuFlRdBiqbF+PJIEVxm2jLFcQWN7q7iWEJGsBV4n7v1dbI9qXB8im2pMMKMCUZe092sQb5SQft2DHfuQGK5hqQ== +"@types/node@*", "@types/node@14.0.1", "@types/node@>= 8": + version "14.0.1" + resolved "https://registry.yarnpkg.com/@types/node/-/node-14.0.1.tgz#5d93e0a099cd0acd5ef3d5bde3c086e1f49ff68c" + integrity sha512-FAYBGwC+W6F9+huFIDtn43cpy7+SzG+atzRiTfdp3inUKL2hXnd4rG8hylJLIh4+hqrQy1P17kvJByE/z825hA== "@types/normalize-package-data@^2.4.0": version "2.4.0" resolved "https://registry.yarnpkg.com/@types/normalize-package-data/-/normalize-package-data-2.4.0.tgz#e486d0d97396d79beedd0a6e33f4534ff6b4973e" integrity sha512-f5j5b/Gf71L+dbqxIpQ4Z2WlmI/mPJ0fOkGGmFgtb6sAu97EPczzbS3/tJKxmcYDj55OX6ssqwDAWOHIYDRDGA== -"@types/p-queue@^2.3.2": - version "2.3.2" - resolved "https://registry.yarnpkg.com/@types/p-queue/-/p-queue-2.3.2.tgz#16bc5fece69ef85efaf2bce8b13f3ebe39c5a1c8" - integrity sha512-eKAv5Ql6k78dh3ULCsSBxX6bFNuGjTmof5Q/T6PiECDq0Yf8IIn46jCyp3RJvCi8owaEmm3DZH1PEImjBMd/vQ== +"@types/parse-json@^4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@types/parse-json/-/parse-json-4.0.0.tgz#2f8bb441434d163b35fb8ffdccd7138927ffb8c0" + integrity sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA== "@types/pg-types@*": - version "1.11.4" - resolved "https://registry.yarnpkg.com/@types/pg-types/-/pg-types-1.11.4.tgz#8d7c59fb509ce3dca3f8bae589252051c639a9a8" - integrity sha512-WdIiQmE347LGc1Vq3Ki8sk3iyCuLgnccqVzgxek6gEHp2H0p3MQ3jniIHt+bRODXKju4kNQ+mp53lmP5+/9moQ== - dependencies: - moment ">=2.14.0" + version "1.11.5" + resolved "https://registry.yarnpkg.com/@types/pg-types/-/pg-types-1.11.5.tgz#1eebbe62b6772fcc75c18957a90f933d155e005b" + integrity sha512-L8ogeT6vDzT1vxlW3KITTCt+BVXXVkLXfZ/XNm6UqbcJgxf+KPO7yjWx7dQQE8RW07KopL10x2gNMs41+IkMGQ== -"@types/pg@7.11.2": - version "7.11.2" - resolved "https://registry.yarnpkg.com/@types/pg/-/pg-7.11.2.tgz#199dec09426c9359574dedede37313805ba3fca2" - integrity sha512-4+rj7fnidA77jFURNanuPPc1HrQv+RkhI6s+K18G9zOKbOUUpChA/rbNMqFukNuZ89LoIt/I9dAlxf329TjCNw== +"@types/pg@7.14.3": + version "7.14.3" + resolved "https://registry.yarnpkg.com/@types/pg/-/pg-7.14.3.tgz#eb166e4f4287923890b10ed20371f937938cb995" + integrity sha512-go5zddQ1FrUQHeBvqPzQ1svKo4KKucSwvqLsvwc/EIuQ9sxDA21b68xc/RwhzAK5pPCnez8NrkYatFIGdJBVvA== dependencies: "@types/node" "*" "@types/pg-types" "*" @@ -1490,298 +1444,356 @@ resolved "https://registry.yarnpkg.com/@types/point-in-polygon/-/point-in-polygon-1.0.0.tgz#c5051ee2b69c97aab7a46d99db4f5268fc90d6cd" integrity sha512-4jwic3xvYymOk1Dl4L/y0scsC69Vie8f8QhXCAmuXlWY8QzxX9jlp0DXyyfc6Ib2ZfZVzsUz82tUDRwxEOG1lw== +"@types/qs@*": + version "6.9.2" + resolved "https://registry.yarnpkg.com/@types/qs/-/qs-6.9.2.tgz#faab98ec4f96ee72c829b7ec0983af4f4d343113" + integrity sha512-a9bDi4Z3zCZf4Lv1X/vwnvbbDYSNz59h3i3KdyuYYN+YrLjSeJD0dnphdULDfySvUv6Exy/O0K6wX/kQpnPQ+A== + "@types/range-parser@*": version "1.2.3" resolved "https://registry.yarnpkg.com/@types/range-parser/-/range-parser-1.2.3.tgz#7ee330ba7caafb98090bece86a5ee44115904c2c" integrity sha512-ewFXqrQHlFsgc09MK5jP5iR7vumV/BYayNC6PgJO2LPe8vrnNFyjQjSppfEngITi0qvfKtzFvgKymGheFM9UOA== -"@types/redis@2.8.14": - version "2.8.14" - resolved "https://registry.yarnpkg.com/@types/redis/-/redis-2.8.14.tgz#2ed46d0f923f7ccd63fbe73a46a1241e606cf716" - integrity sha512-255dzsOLJdXFHBio9/aMHGozNkoiBUgc+g2nlNjbTSp5qcAlmpm4Z6Xs3pKOBLNIKdZbA2BkUxWvYSIwKra0Yw== +"@types/redis@2.8.20": + version "2.8.20" + resolved "https://registry.yarnpkg.com/@types/redis/-/redis-2.8.20.tgz#7060fa9d65ccf7ef32f714f05f83a016a20211c8" + integrity sha512-IJOvkg+BnCRDlMrrBIasCs8bqddnjiplI26HwsPSeY/biEJaDzAQnjs2i5z5vJelTbi3TIiLUIrKXk7j9Tmi/Q== dependencies: "@types/node" "*" -"@types/request-promise@4.1.44": - version "4.1.44" - resolved "https://registry.yarnpkg.com/@types/request-promise/-/request-promise-4.1.44.tgz#05b59cd18445832fae16b68d5bb3d4621b549485" - integrity sha512-RId7eFsUKxfal1LirDDIcOp9u3MM3NXFDBcC3sqIMcmu7f4U6DsCEMD8RbLZtnPrQlN5Jc79di/WPsIEDO4keg== +"@types/request-promise@4.1.46": + version "4.1.46" + resolved "https://registry.yarnpkg.com/@types/request-promise/-/request-promise-4.1.46.tgz#37df6efae984316dfbfbbe8fcda37f3ba52822f2" + integrity sha512-3Thpj2Va5m0ji3spaCk8YKrjkZyZc6RqUVOphA0n/Xet66AW/AiOAs5vfXhQIL5NmkaO7Jnun7Nl9NEjJ2zBaw== dependencies: "@types/bluebird" "*" "@types/request" "*" "@types/request@*": - version "2.48.2" - resolved "https://registry.yarnpkg.com/@types/request/-/request-2.48.2.tgz#936374cbe1179d7ed529fc02543deb4597450fed" - integrity sha512-gP+PSFXAXMrd5PcD7SqHeUjdGshAI8vKQ3+AvpQr3ht9iQea+59LOKvKITcQI+Lg+1EIkDP6AFSBUJPWG8GDyA== + version "2.48.4" + resolved "https://registry.yarnpkg.com/@types/request/-/request-2.48.4.tgz#df3d43d7b9ed3550feaa1286c6eabf0738e6cf7e" + integrity sha512-W1t1MTKYR8PxICH+A4HgEIPuAC3sbljoEVfyZbeFJJDbr30guDspJri2XOaM2E+Un7ZjrihaDi7cf6fPa2tbgw== dependencies: "@types/caseless" "*" "@types/node" "*" "@types/tough-cookie" "*" form-data "^2.5.0" -"@types/retry@^0.12.0": +"@types/retry@*": version "0.12.0" resolved "https://registry.yarnpkg.com/@types/retry/-/retry-0.12.0.tgz#2b35eccfcee7d38cd72ad99232fbd58bffb3c84d" integrity sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA== "@types/serve-static@*": - version "1.13.2" - resolved "https://registry.yarnpkg.com/@types/serve-static/-/serve-static-1.13.2.tgz#f5ac4d7a6420a99a6a45af4719f4dcd8cd907a48" - integrity sha512-/BZ4QRLpH/bNYgZgwhKEh+5AsboDBcUdlBYgzoLX0fpj3Y2gp6EApyOlM3bK53wQS/OE1SrdSYBAbux2D1528Q== + version "1.13.3" + resolved "https://registry.yarnpkg.com/@types/serve-static/-/serve-static-1.13.3.tgz#eb7e1c41c4468272557e897e9171ded5e2ded9d1" + integrity sha512-oprSwp094zOglVrXdlo/4bAHtKTAxX6VT8FOZlBKrmyLbNvE1zxZyJ6yikMVtHIvwP45+ZQGJn+FdXGKTozq0g== dependencies: "@types/express-serve-static-core" "*" "@types/mime" "*" -"@types/should@13.0.0": - version "13.0.0" - resolved "https://registry.yarnpkg.com/@types/should/-/should-13.0.0.tgz#96c00117f1896177848fdecfa336313c230c879e" - integrity sha512-Mi6YZ2ABnnGGFMuiBDP0a8s1ZDCDNHqP97UH8TyDmCWuGGavpsFMfJnAMYaaqmDlSCOCNbVLHBrSDEOpx/oLhw== +"@types/sharp@0.25.0": + version "0.25.0" + resolved "https://registry.yarnpkg.com/@types/sharp/-/sharp-0.25.0.tgz#e21501779b110e26f33cec7fb1969e49e0cdc3c0" + integrity sha512-JIWCJN0OfHRjkiKyI0BXbym1ip+9KsZJgBsnwNVrJPzm400oO64WxHbyYQ3I5xAqkIrTw1KIqqBME57aOS6n6w== dependencies: - should "*" + "@types/node" "*" -"@types/sinon@7.5.0": - version "7.5.0" - resolved "https://registry.yarnpkg.com/@types/sinon/-/sinon-7.5.0.tgz#f5a10c27175465a0b001b68d8b9f761582967cc6" - integrity sha512-NyzhuSBy97B/zE58cDw4NyGvByQbAHNP9069KVSgnXt/sc0T6MFRh0InKAeBVHJWdSXG1S3+PxgVIgKo9mTHbw== +"@types/sinon-express-mock@1.3.8": + version "1.3.8" + resolved "https://registry.yarnpkg.com/@types/sinon-express-mock/-/sinon-express-mock-1.3.8.tgz#4db9fca024b5355ef5cbee48e1068ed3831e55ef" + integrity sha512-aYu0PMH/VLpl5GMqnhYRuosC4KY0d3gi/Jn7k8G+rJIniryPxCweYyUFvZSs12ee0zlyiWvWyGgv+CaKW5DusQ== + dependencies: + "@types/express" "*" + "@types/sinon" "*" -"@types/sizzle@2.3.2": - version "2.3.2" - resolved "https://registry.yarnpkg.com/@types/sizzle/-/sizzle-2.3.2.tgz#a811b8c18e2babab7d542b3365887ae2e4d9de47" - integrity sha512-7EJYyKTL7tFR8+gDbB6Wwz/arpGa0Mywk1TJbNzKzHtzbwVmY4HR9WqS5VV7dsBUKQmPNr192jHr/VpBluj/hg== +"@types/sinon@*", "@types/sinon@9.0.0": + version "9.0.0" + resolved "https://registry.yarnpkg.com/@types/sinon/-/sinon-9.0.0.tgz#5b70a360f55645dd64f205defd2a31b749a59799" + integrity sha512-v2TkYHkts4VXshMkcmot/H+ERZ2SevKa10saGaJPGCJ8vh3lKrC4u663zYEeRZxep+VbG6YRDtQ6gVqw9dYzPA== + dependencies: + "@types/sinonjs__fake-timers" "*" + +"@types/sinonjs__fake-timers@*": + version "6.0.1" + resolved "https://registry.yarnpkg.com/@types/sinonjs__fake-timers/-/sinonjs__fake-timers-6.0.1.tgz#681df970358c82836b42f989188d133e218c458e" + integrity sha512-yYezQwGWty8ziyYLdZjwxyMb0CZR49h8JALHGrxjQHWlqGgc8kLdHEgWrgL0uZ29DMvEVBDnHU2Wg36zKSIUtA== + +"@types/source-list-map@*": + version "0.1.2" + resolved "https://registry.yarnpkg.com/@types/source-list-map/-/source-list-map-0.1.2.tgz#0078836063ffaf17412349bba364087e0ac02ec9" + integrity sha512-K5K+yml8LTo9bWJI/rECfIPrGgxdpeNbj+d53lwN4QjW1MCwlkhUms+gtdzigTeUyBr09+u8BwOIY3MXvHdcsA== "@types/superagent@*": - version "4.1.3" - resolved "https://registry.yarnpkg.com/@types/superagent/-/superagent-4.1.3.tgz#52566ddd8957273b477c3e187c930fd791a95b2e" - integrity sha512-vy2licJQwOXrTAe+yz9SCyUVXAkMgCeDq9VHzS5CWJyDU1g6CI4xKb4d5sCEmyucjw5sG0y4k2/afS0iv/1D0Q== + version "4.1.7" + resolved "https://registry.yarnpkg.com/@types/superagent/-/superagent-4.1.7.tgz#a7d92d98c490ee0f802a127fdf149b9a114f77a5" + integrity sha512-JSwNPgRYjIC4pIeOqLwWwfGj6iP1n5NE6kNBEbGx2V8H78xCPwx7QpNp9plaI30+W3cFEzJO7BIIsXE+dbtaGg== dependencies: "@types/cookiejar" "*" "@types/node" "*" -"@types/supertest@2.0.8": - version "2.0.8" - resolved "https://registry.yarnpkg.com/@types/supertest/-/supertest-2.0.8.tgz#23801236e2b85204ed771a8e7c40febba7da2bda" - integrity sha512-wcax7/ip4XSSJRLbNzEIUVy2xjcBIZZAuSd2vtltQfRK7kxhx5WMHbLHkYdxN3wuQCrwpYrg86/9byDjPXoGMA== +"@types/supertest@2.0.9": + version "2.0.9" + resolved "https://registry.yarnpkg.com/@types/supertest/-/supertest-2.0.9.tgz#049bddbcb0ee0d60a9b836ccc977d813a1c32325" + integrity sha512-0BTpWWWAO1+uXaP/oA0KW1eOZv4hc0knhrWowV06Gwwz3kqQxNO98fUFM2e15T+PdPRmOouNFrYvaBgdojPJ3g== dependencies: "@types/superagent" "*" +"@types/tapable@*": + version "1.0.5" + resolved "https://registry.yarnpkg.com/@types/tapable/-/tapable-1.0.5.tgz#9adbc12950582aa65ead76bffdf39fe0c27a3c02" + integrity sha512-/gG2M/Imw7cQFp8PGvz/SwocNrmKFjFsm5Pb8HdbHkZ1K8pmuPzOX4VeVoiEecFCVf4CsN1r3/BRvx+6sNqwtQ== + "@types/tough-cookie@*": - version "2.3.5" - resolved "https://registry.yarnpkg.com/@types/tough-cookie/-/tough-cookie-2.3.5.tgz#9da44ed75571999b65c37b60c9b2b88db54c585d" - integrity sha512-SCcK7mvGi3+ZNz833RRjFIxrn4gI1PPR3NtuIS+6vMkvmsGjosqTJwRt5bAEFLRz+wtJMWv8+uOnZf2hi2QXTg== + version "4.0.0" + resolved "https://registry.yarnpkg.com/@types/tough-cookie/-/tough-cookie-4.0.0.tgz#fef1904e4668b6e5ecee60c52cc6a078ffa6697d" + integrity sha512-I99sngh224D0M7XgW1s120zxCt3VYQ3IQsuw3P3jbq5GG4yc79+ZjyKznyOGIQrflfylLgcfekeZW/vk0yng6A== -"@types/uuid@3.4.5": - version "3.4.5" - resolved "https://registry.yarnpkg.com/@types/uuid/-/uuid-3.4.5.tgz#d4dc10785b497a1474eae0ba7f0cb09c0ddfd6eb" - integrity sha512-MNL15wC3EKyw1VLF+RoVO4hJJdk9t/Hlv3rt1OL65Qvuadm4BYo6g9ZJQqoq7X8NBFSsQXgAujWciovh2lpVjA== +"@types/uglify-js@*": + version "3.9.1" + resolved "https://registry.yarnpkg.com/@types/uglify-js/-/uglify-js-3.9.1.tgz#0ad39d6a72979593f669acdfc7e980d590d3fb94" + integrity sha512-rdBIeMQyRBOXogop/EYBvSkYFn9D9yGxUa5hagBVG55KIdSUbp22EACJSHCs6kmmfunojAhf7zJH+Ds06/qLaQ== + dependencies: + source-map "^0.6.1" + +"@types/uuid@7.0.3": + version "7.0.3" + resolved "https://registry.yarnpkg.com/@types/uuid/-/uuid-7.0.3.tgz#45cd03e98e758f8581c79c535afbd4fc27ba7ac8" + integrity sha512-PUdqTZVrNYTNcIhLHkiaYzoOIaUi5LFg/XLerAdgvwQrUCx+oSbtoBze1AMyvYbcwzUSNC+Isl58SM4Sm/6COw== + +"@types/webpack-merge@4.1.5": + version "4.1.5" + resolved "https://registry.yarnpkg.com/@types/webpack-merge/-/webpack-merge-4.1.5.tgz#265fbee4810474860d0f4c17e0107032881eed47" + integrity sha512-cbDo592ljSHeaVe5Q39JKN6Z4vMhmo4+C3JbksOIg+kjhKQYN2keGN7dvr/i18+dughij98Qrsfn1mU9NgVoCA== + dependencies: + "@types/webpack" "*" + +"@types/webpack-sources@*": + version "0.1.7" + resolved "https://registry.yarnpkg.com/@types/webpack-sources/-/webpack-sources-0.1.7.tgz#0a330a9456113410c74a5d64180af0cbca007141" + integrity sha512-XyaHrJILjK1VHVC4aVlKsdNN5KBTwufMb43cQs+flGxtPAf/1Qwl8+Q0tp5BwEGaI8D6XT1L+9bSWXckgkjTLw== dependencies: "@types/node" "*" + "@types/source-list-map" "*" + source-map "^0.6.1" -"@types/ws@^5.1.1": - version "5.1.2" - resolved "https://registry.yarnpkg.com/@types/ws/-/ws-5.1.2.tgz#f02d3b1cd46db7686734f3ce83bdf46c49decd64" - integrity sha512-NkTXUKTYdXdnPE2aUUbGOXE1XfMK527SCvU/9bj86kyFF6kZ9ZnOQ3mK5jADn98Y2vEUD/7wKDgZa7Qst2wYOg== +"@types/webpack@*", "@types/webpack@4.41.13": + version "4.41.13" + resolved "https://registry.yarnpkg.com/@types/webpack/-/webpack-4.41.13.tgz#988d114c8913d039b8a0e0502a7fe4f1f84f3d5e" + integrity sha512-RYmIHOWSxnTTa765N6jJBVE45pd2SYNblEYshVDduLw6RhocazNmRzE5/ytvBD8IkDMH6DI+bcrqxh8NILimBA== + dependencies: + "@types/anymatch" "*" + "@types/node" "*" + "@types/tapable" "*" + "@types/uglify-js" "*" + "@types/webpack-sources" "*" + source-map "^0.6.0" + +"@types/ws@7.2.4": + version "7.2.4" + resolved "https://registry.yarnpkg.com/@types/ws/-/ws-7.2.4.tgz#b3859f7b9c243b220efac9716ec42c716a72969d" + integrity sha512-9S6Ask71vujkVyeEXKxjBSUV8ZUB0mjL5la4IncBoheu04bDaYyUKErh1BQcY9+WzOUOiKqz/OnpJHYckbMfNg== dependencies: - "@types/events" "*" "@types/node" "*" "@types/yargs-parser@*": - version "13.0.0" - resolved "https://registry.yarnpkg.com/@types/yargs-parser/-/yargs-parser-13.0.0.tgz#453743c5bbf9f1bed61d959baab5b06be029b2d0" - integrity sha512-wBlsw+8n21e6eTd4yVv8YD/E3xq0O6nNnJIquutAsFGE7EyMKz7W6RNT6BRu1SmdgmlCZ9tb0X+j+D6HGr8pZw== + version "15.0.0" + resolved "https://registry.yarnpkg.com/@types/yargs-parser/-/yargs-parser-15.0.0.tgz#cb3f9f741869e20cce330ffbeb9271590483882d" + integrity sha512-FA/BWv8t8ZWJ+gEOnLLd8ygxH/2UFbAvgEonyfN6yWGLKc7zVjbpl2Y4CTjid9h2RfgPP6SEt6uHwEOply00yw== -"@types/yargs@13.0.3": - version "13.0.3" - resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-13.0.3.tgz#76482af3981d4412d65371a318f992d33464a380" - integrity sha512-K8/LfZq2duW33XW/tFwEAfnZlqIfVsoyRB3kfXdPXYhl0nfM8mmh7GS0jg7WrX2Dgq/0Ha/pR1PaR+BvmWwjiQ== +"@types/yargs@15.0.5": + version "15.0.5" + resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-15.0.5.tgz#947e9a6561483bdee9adffc983e91a6902af8b79" + integrity sha512-Dk/IDOPtOgubt/IaevIUbTgV7doaKkoorvOyYM2CMwuDyP89bekI7H4xLIwunNYiK9jhCkmc6pUrJk3cj2AB9w== dependencies: "@types/yargs-parser" "*" -"@typescript-eslint/eslint-plugin@2.5.0": - version "2.5.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-2.5.0.tgz#101d96743ce3365b3223df73d641078c9b775903" - integrity sha512-ddrJZxp5ns1Lh5ofZQYk3P8RyvKfyz/VcRR4ZiJLHO/ljnQAO8YvTfj268+WJOOadn99mvDiqJA65+HAKoeSPA== +"@typescript-eslint/eslint-plugin@2.33.0": + version "2.33.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-2.33.0.tgz#d6c8319d5011b4783bb3d2dadf105d8bdd499bd5" + integrity sha512-QV6P32Btu1sCI/kTqjTNI/8OpCYyvlGjW5vD8MpTIg+HGE5S88HtT1G+880M4bXlvXj/NjsJJG0aGcVh0DdbeQ== dependencies: - "@typescript-eslint/experimental-utils" "2.5.0" - eslint-utils "^1.4.2" + "@typescript-eslint/experimental-utils" "2.33.0" functional-red-black-tree "^1.0.1" - regexpp "^2.0.1" + regexpp "^3.0.0" tsutils "^3.17.1" -"@typescript-eslint/experimental-utils@2.5.0": - version "2.5.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-2.5.0.tgz#383a97ded9a7940e5053449f6d73995e782b8fb1" - integrity sha512-UgcQGE0GKJVChyRuN1CWqDW8Pnu7+mVst0aWrhiyuUD1J9c+h8woBdT4XddCvhcXDodTDVIfE3DzGHVjp7tUeQ== +"@typescript-eslint/experimental-utils@2.33.0": + version "2.33.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-2.33.0.tgz#000f1e5f344fbea1323dc91cc174805d75f99a03" + integrity sha512-qzPM2AuxtMrRq78LwyZa8Qn6gcY8obkIrBs1ehqmQADwkYzTE1Pb4y2W+U3rE/iFkSWcWHG2LS6MJfj6SmHApg== dependencies: "@types/json-schema" "^7.0.3" - "@typescript-eslint/typescript-estree" "2.5.0" + "@typescript-eslint/typescript-estree" "2.33.0" eslint-scope "^5.0.0" + eslint-utils "^2.0.0" -"@typescript-eslint/parser@2.5.0": - version "2.5.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-2.5.0.tgz#858030ddd808fbbe88e03f42e5971efaccb8218a" - integrity sha512-9UBMiAwIDWSl79UyogaBdj3hidzv6exjKUx60OuZuFnJf56tq/UMpdPcX09YmGqE8f4AnAueYtBxV8IcAT3jdQ== +"@typescript-eslint/parser@2.33.0": + version "2.33.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-2.33.0.tgz#395c0ef229ebef883608f8632a34f0acf02b9bdd" + integrity sha512-AUtmwUUhJoH6yrtxZMHbRUEMsC2G6z5NSxg9KsROOGqNXasM71I8P2NihtumlWTUCRld70vqIZ6Pm4E5PAziEA== dependencies: "@types/eslint-visitor-keys" "^1.0.0" - "@typescript-eslint/experimental-utils" "2.5.0" - "@typescript-eslint/typescript-estree" "2.5.0" + "@typescript-eslint/experimental-utils" "2.33.0" + "@typescript-eslint/typescript-estree" "2.33.0" eslint-visitor-keys "^1.1.0" -"@typescript-eslint/typescript-estree@2.5.0": - version "2.5.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-2.5.0.tgz#40ada624d6217ef092a3a79ed30d947ad4f212ce" - integrity sha512-AXURyF8NcA3IsnbjNX1v9qbwa0dDoY9YPcKYR2utvMHoUcu3636zrz0gRWtVAyxbPCkhyKuGg6WZIyi2Fc79CA== +"@typescript-eslint/typescript-estree@2.33.0": + version "2.33.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-2.33.0.tgz#33504c050ccafd38f397a645d4e9534d2eccbb5c" + integrity sha512-d8rY6/yUxb0+mEwTShCQF2zYQdLlqihukNfG9IUlLYz5y1CH6G/9XYbrxQLq3Z14RNvkCC6oe+OcFlyUpwUbkg== dependencies: debug "^4.1.1" - glob "^7.1.4" + eslint-visitor-keys "^1.1.0" + glob "^7.1.6" is-glob "^4.0.1" - lodash.unescape "4.0.1" - semver "^6.3.0" + lodash "^4.17.15" + semver "^7.3.2" + tsutils "^3.17.1" + +"@webassemblyjs/ast@1.9.0": + version "1.9.0" + resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.9.0.tgz#bd850604b4042459a5a41cd7d338cbed695ed964" + integrity sha512-C6wW5L+b7ogSDVqymbkkvuW9kruN//YisMED04xzeBBqjHa2FYnmvOlS6Xj68xWQRgWvI9cIglsjFowH/RJyEA== + dependencies: + "@webassemblyjs/helper-module-context" "1.9.0" + "@webassemblyjs/helper-wasm-bytecode" "1.9.0" + "@webassemblyjs/wast-parser" "1.9.0" + +"@webassemblyjs/floating-point-hex-parser@1.9.0": + version "1.9.0" + resolved "https://registry.yarnpkg.com/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.9.0.tgz#3c3d3b271bddfc84deb00f71344438311d52ffb4" + integrity sha512-TG5qcFsS8QB4g4MhrxK5TqfdNe7Ey/7YL/xN+36rRjl/BlGE/NcBvJcqsRgCP6Z92mRE+7N50pRIi8SmKUbcQA== + +"@webassemblyjs/helper-api-error@1.9.0": + version "1.9.0" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-api-error/-/helper-api-error-1.9.0.tgz#203f676e333b96c9da2eeab3ccef33c45928b6a2" + integrity sha512-NcMLjoFMXpsASZFxJ5h2HZRcEhDkvnNFOAKneP5RbKRzaWJN36NC4jqQHKwStIhGXu5mUWlUUk7ygdtrO8lbmw== + +"@webassemblyjs/helper-buffer@1.9.0": + version "1.9.0" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-buffer/-/helper-buffer-1.9.0.tgz#a1442d269c5feb23fcbc9ef759dac3547f29de00" + integrity sha512-qZol43oqhq6yBPx7YM3m9Bv7WMV9Eevj6kMi6InKOuZxhw+q9hOkvq5e/PpKSiLfyetpaBnogSbNCfBwyB00CA== + +"@webassemblyjs/helper-code-frame@1.9.0": + version "1.9.0" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-code-frame/-/helper-code-frame-1.9.0.tgz#647f8892cd2043a82ac0c8c5e75c36f1d9159f27" + integrity sha512-ERCYdJBkD9Vu4vtjUYe8LZruWuNIToYq/ME22igL+2vj2dQ2OOujIZr3MEFvfEaqKoVqpsFKAGsRdBSBjrIvZA== + dependencies: + "@webassemblyjs/wast-printer" "1.9.0" + +"@webassemblyjs/helper-fsm@1.9.0": + version "1.9.0" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-fsm/-/helper-fsm-1.9.0.tgz#c05256b71244214671f4b08ec108ad63b70eddb8" + integrity sha512-OPRowhGbshCb5PxJ8LocpdX9Kl0uB4XsAjl6jH/dWKlk/mzsANvhwbiULsaiqT5GZGT9qinTICdj6PLuM5gslw== + +"@webassemblyjs/helper-module-context@1.9.0": + version "1.9.0" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-module-context/-/helper-module-context-1.9.0.tgz#25d8884b76839871a08a6c6f806c3979ef712f07" + integrity sha512-MJCW8iGC08tMk2enck1aPW+BE5Cw8/7ph/VGZxwyvGbJwjktKkDK7vy7gAmMDx88D7mhDTCNKAW5tED+gZ0W8g== + dependencies: + "@webassemblyjs/ast" "1.9.0" + +"@webassemblyjs/helper-wasm-bytecode@1.9.0": + version "1.9.0" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.9.0.tgz#4fed8beac9b8c14f8c58b70d124d549dd1fe5790" + integrity sha512-R7FStIzyNcd7xKxCZH5lE0Bqy+hGTwS3LJjuv1ZVxd9O7eHCedSdrId/hMOd20I+v8wDXEn+bjfKDLzTepoaUw== + +"@webassemblyjs/helper-wasm-section@1.9.0": + version "1.9.0" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.9.0.tgz#5a4138d5a6292ba18b04c5ae49717e4167965346" + integrity sha512-XnMB8l3ek4tvrKUUku+IVaXNHz2YsJyOOmz+MMkZvh8h1uSJpSen6vYnw3IoQ7WwEuAhL8Efjms1ZWjqh2agvw== + dependencies: + "@webassemblyjs/ast" "1.9.0" + "@webassemblyjs/helper-buffer" "1.9.0" + "@webassemblyjs/helper-wasm-bytecode" "1.9.0" + "@webassemblyjs/wasm-gen" "1.9.0" -"@webassemblyjs/ast@1.8.5": - version "1.8.5" - resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.8.5.tgz#51b1c5fe6576a34953bf4b253df9f0d490d9e359" - integrity sha512-aJMfngIZ65+t71C3y2nBBg5FFG0Okt9m0XEgWZ7Ywgn1oMAT8cNwx00Uv1cQyHtidq0Xn94R4TAywO+LCQ+ZAQ== - dependencies: - "@webassemblyjs/helper-module-context" "1.8.5" - "@webassemblyjs/helper-wasm-bytecode" "1.8.5" - "@webassemblyjs/wast-parser" "1.8.5" - -"@webassemblyjs/floating-point-hex-parser@1.8.5": - version "1.8.5" - resolved "https://registry.yarnpkg.com/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.8.5.tgz#1ba926a2923613edce496fd5b02e8ce8a5f49721" - integrity sha512-9p+79WHru1oqBh9ewP9zW95E3XAo+90oth7S5Re3eQnECGq59ly1Ri5tsIipKGpiStHsUYmY3zMLqtk3gTcOtQ== - -"@webassemblyjs/helper-api-error@1.8.5": - version "1.8.5" - resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-api-error/-/helper-api-error-1.8.5.tgz#c49dad22f645227c5edb610bdb9697f1aab721f7" - integrity sha512-Za/tnzsvnqdaSPOUXHyKJ2XI7PDX64kWtURyGiJJZKVEdFOsdKUCPTNEVFZq3zJ2R0G5wc2PZ5gvdTRFgm81zA== - -"@webassemblyjs/helper-buffer@1.8.5": - version "1.8.5" - resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-buffer/-/helper-buffer-1.8.5.tgz#fea93e429863dd5e4338555f42292385a653f204" - integrity sha512-Ri2R8nOS0U6G49Q86goFIPNgjyl6+oE1abW1pS84BuhP1Qcr5JqMwRFT3Ah3ADDDYGEgGs1iyb1DGX+kAi/c/Q== - -"@webassemblyjs/helper-code-frame@1.8.5": - version "1.8.5" - resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-code-frame/-/helper-code-frame-1.8.5.tgz#9a740ff48e3faa3022b1dff54423df9aa293c25e" - integrity sha512-VQAadSubZIhNpH46IR3yWO4kZZjMxN1opDrzePLdVKAZ+DFjkGD/rf4v1jap744uPVU6yjL/smZbRIIJTOUnKQ== - dependencies: - "@webassemblyjs/wast-printer" "1.8.5" - -"@webassemblyjs/helper-fsm@1.8.5": - version "1.8.5" - resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-fsm/-/helper-fsm-1.8.5.tgz#ba0b7d3b3f7e4733da6059c9332275d860702452" - integrity sha512-kRuX/saORcg8se/ft6Q2UbRpZwP4y7YrWsLXPbbmtepKr22i8Z4O3V5QE9DbZK908dh5Xya4Un57SDIKwB9eow== - -"@webassemblyjs/helper-module-context@1.8.5": - version "1.8.5" - resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-module-context/-/helper-module-context-1.8.5.tgz#def4b9927b0101dc8cbbd8d1edb5b7b9c82eb245" - integrity sha512-/O1B236mN7UNEU4t9X7Pj38i4VoU8CcMHyy3l2cV/kIF4U5KoHXDVqcDuOs1ltkac90IM4vZdHc52t1x8Yfs3g== - dependencies: - "@webassemblyjs/ast" "1.8.5" - mamacro "^0.0.3" - -"@webassemblyjs/helper-wasm-bytecode@1.8.5": - version "1.8.5" - resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.8.5.tgz#537a750eddf5c1e932f3744206551c91c1b93e61" - integrity sha512-Cu4YMYG3Ddl72CbmpjU/wbP6SACcOPVbHN1dI4VJNJVgFwaKf1ppeFJrwydOG3NDHxVGuCfPlLZNyEdIYlQ6QQ== - -"@webassemblyjs/helper-wasm-section@1.8.5": - version "1.8.5" - resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.8.5.tgz#74ca6a6bcbe19e50a3b6b462847e69503e6bfcbf" - integrity sha512-VV083zwR+VTrIWWtgIUpqfvVdK4ff38loRmrdDBgBT8ADXYsEZ5mPQ4Nde90N3UYatHdYoDIFb7oHzMncI02tA== - dependencies: - "@webassemblyjs/ast" "1.8.5" - "@webassemblyjs/helper-buffer" "1.8.5" - "@webassemblyjs/helper-wasm-bytecode" "1.8.5" - "@webassemblyjs/wasm-gen" "1.8.5" - -"@webassemblyjs/ieee754@1.8.5": - version "1.8.5" - resolved "https://registry.yarnpkg.com/@webassemblyjs/ieee754/-/ieee754-1.8.5.tgz#712329dbef240f36bf57bd2f7b8fb9bf4154421e" - integrity sha512-aaCvQYrvKbY/n6wKHb/ylAJr27GglahUO89CcGXMItrOBqRarUMxWLJgxm9PJNuKULwN5n1csT9bYoMeZOGF3g== +"@webassemblyjs/ieee754@1.9.0": + version "1.9.0" + resolved "https://registry.yarnpkg.com/@webassemblyjs/ieee754/-/ieee754-1.9.0.tgz#15c7a0fbaae83fb26143bbacf6d6df1702ad39e4" + integrity sha512-dcX8JuYU/gvymzIHc9DgxTzUUTLexWwt8uCTWP3otys596io0L5aW02Gb1RjYpx2+0Jus1h4ZFqjla7umFniTg== dependencies: "@xtuc/ieee754" "^1.2.0" -"@webassemblyjs/leb128@1.8.5": - version "1.8.5" - resolved "https://registry.yarnpkg.com/@webassemblyjs/leb128/-/leb128-1.8.5.tgz#044edeb34ea679f3e04cd4fd9824d5e35767ae10" - integrity sha512-plYUuUwleLIziknvlP8VpTgO4kqNaH57Y3JnNa6DLpu/sGcP6hbVdfdX5aHAV716pQBKrfuU26BJK29qY37J7A== +"@webassemblyjs/leb128@1.9.0": + version "1.9.0" + resolved "https://registry.yarnpkg.com/@webassemblyjs/leb128/-/leb128-1.9.0.tgz#f19ca0b76a6dc55623a09cffa769e838fa1e1c95" + integrity sha512-ENVzM5VwV1ojs9jam6vPys97B/S65YQtv/aanqnU7D8aSoHFX8GyhGg0CMfyKNIHBuAVjy3tlzd5QMMINa7wpw== dependencies: "@xtuc/long" "4.2.2" -"@webassemblyjs/utf8@1.8.5": - version "1.8.5" - resolved "https://registry.yarnpkg.com/@webassemblyjs/utf8/-/utf8-1.8.5.tgz#a8bf3b5d8ffe986c7c1e373ccbdc2a0915f0cedc" - integrity sha512-U7zgftmQriw37tfD934UNInokz6yTmn29inT2cAetAsaU9YeVCveWEwhKL1Mg4yS7q//NGdzy79nlXh3bT8Kjw== - -"@webassemblyjs/wasm-edit@1.8.5": - version "1.8.5" - resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-edit/-/wasm-edit-1.8.5.tgz#962da12aa5acc1c131c81c4232991c82ce56e01a" - integrity sha512-A41EMy8MWw5yvqj7MQzkDjU29K7UJq1VrX2vWLzfpRHt3ISftOXqrtojn7nlPsZ9Ijhp5NwuODuycSvfAO/26Q== - dependencies: - "@webassemblyjs/ast" "1.8.5" - "@webassemblyjs/helper-buffer" "1.8.5" - "@webassemblyjs/helper-wasm-bytecode" "1.8.5" - "@webassemblyjs/helper-wasm-section" "1.8.5" - "@webassemblyjs/wasm-gen" "1.8.5" - "@webassemblyjs/wasm-opt" "1.8.5" - "@webassemblyjs/wasm-parser" "1.8.5" - "@webassemblyjs/wast-printer" "1.8.5" - -"@webassemblyjs/wasm-gen@1.8.5": - version "1.8.5" - resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-gen/-/wasm-gen-1.8.5.tgz#54840766c2c1002eb64ed1abe720aded714f98bc" - integrity sha512-BCZBT0LURC0CXDzj5FXSc2FPTsxwp3nWcqXQdOZE4U7h7i8FqtFK5Egia6f9raQLpEKT1VL7zr4r3+QX6zArWg== - dependencies: - "@webassemblyjs/ast" "1.8.5" - "@webassemblyjs/helper-wasm-bytecode" "1.8.5" - "@webassemblyjs/ieee754" "1.8.5" - "@webassemblyjs/leb128" "1.8.5" - "@webassemblyjs/utf8" "1.8.5" - -"@webassemblyjs/wasm-opt@1.8.5": - version "1.8.5" - resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-opt/-/wasm-opt-1.8.5.tgz#b24d9f6ba50394af1349f510afa8ffcb8a63d264" - integrity sha512-HKo2mO/Uh9A6ojzu7cjslGaHaUU14LdLbGEKqTR7PBKwT6LdPtLLh9fPY33rmr5wcOMrsWDbbdCHq4hQUdd37Q== - dependencies: - "@webassemblyjs/ast" "1.8.5" - "@webassemblyjs/helper-buffer" "1.8.5" - "@webassemblyjs/wasm-gen" "1.8.5" - "@webassemblyjs/wasm-parser" "1.8.5" - -"@webassemblyjs/wasm-parser@1.8.5": - version "1.8.5" - resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-parser/-/wasm-parser-1.8.5.tgz#21576f0ec88b91427357b8536383668ef7c66b8d" - integrity sha512-pi0SYE9T6tfcMkthwcgCpL0cM9nRYr6/6fjgDtL6q/ZqKHdMWvxitRi5JcZ7RI4SNJJYnYNaWy5UUrHQy998lw== - dependencies: - "@webassemblyjs/ast" "1.8.5" - "@webassemblyjs/helper-api-error" "1.8.5" - "@webassemblyjs/helper-wasm-bytecode" "1.8.5" - "@webassemblyjs/ieee754" "1.8.5" - "@webassemblyjs/leb128" "1.8.5" - "@webassemblyjs/utf8" "1.8.5" - -"@webassemblyjs/wast-parser@1.8.5": - version "1.8.5" - resolved "https://registry.yarnpkg.com/@webassemblyjs/wast-parser/-/wast-parser-1.8.5.tgz#e10eecd542d0e7bd394f6827c49f3df6d4eefb8c" - integrity sha512-daXC1FyKWHF1i11obK086QRlsMsY4+tIOKgBqI1lxAnkp9xe9YMcgOxm9kLe+ttjs5aWV2KKE1TWJCN57/Btsg== - dependencies: - "@webassemblyjs/ast" "1.8.5" - "@webassemblyjs/floating-point-hex-parser" "1.8.5" - "@webassemblyjs/helper-api-error" "1.8.5" - "@webassemblyjs/helper-code-frame" "1.8.5" - "@webassemblyjs/helper-fsm" "1.8.5" +"@webassemblyjs/utf8@1.9.0": + version "1.9.0" + resolved "https://registry.yarnpkg.com/@webassemblyjs/utf8/-/utf8-1.9.0.tgz#04d33b636f78e6a6813227e82402f7637b6229ab" + integrity sha512-GZbQlWtopBTP0u7cHrEx+73yZKrQoBMpwkGEIqlacljhXCkVM1kMQge/Mf+csMJAjEdSwhOyLAS0AoR3AG5P8w== + +"@webassemblyjs/wasm-edit@1.9.0": + version "1.9.0" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-edit/-/wasm-edit-1.9.0.tgz#3fe6d79d3f0f922183aa86002c42dd256cfee9cf" + integrity sha512-FgHzBm80uwz5M8WKnMTn6j/sVbqilPdQXTWraSjBwFXSYGirpkSWE2R9Qvz9tNiTKQvoKILpCuTjBKzOIm0nxw== + dependencies: + "@webassemblyjs/ast" "1.9.0" + "@webassemblyjs/helper-buffer" "1.9.0" + "@webassemblyjs/helper-wasm-bytecode" "1.9.0" + "@webassemblyjs/helper-wasm-section" "1.9.0" + "@webassemblyjs/wasm-gen" "1.9.0" + "@webassemblyjs/wasm-opt" "1.9.0" + "@webassemblyjs/wasm-parser" "1.9.0" + "@webassemblyjs/wast-printer" "1.9.0" + +"@webassemblyjs/wasm-gen@1.9.0": + version "1.9.0" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-gen/-/wasm-gen-1.9.0.tgz#50bc70ec68ded8e2763b01a1418bf43491a7a49c" + integrity sha512-cPE3o44YzOOHvlsb4+E9qSqjc9Qf9Na1OO/BHFy4OI91XDE14MjFN4lTMezzaIWdPqHnsTodGGNP+iRSYfGkjA== + dependencies: + "@webassemblyjs/ast" "1.9.0" + "@webassemblyjs/helper-wasm-bytecode" "1.9.0" + "@webassemblyjs/ieee754" "1.9.0" + "@webassemblyjs/leb128" "1.9.0" + "@webassemblyjs/utf8" "1.9.0" + +"@webassemblyjs/wasm-opt@1.9.0": + version "1.9.0" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-opt/-/wasm-opt-1.9.0.tgz#2211181e5b31326443cc8112eb9f0b9028721a61" + integrity sha512-Qkjgm6Anhm+OMbIL0iokO7meajkzQD71ioelnfPEj6r4eOFuqm4YC3VBPqXjFyyNwowzbMD+hizmprP/Fwkl2A== + dependencies: + "@webassemblyjs/ast" "1.9.0" + "@webassemblyjs/helper-buffer" "1.9.0" + "@webassemblyjs/wasm-gen" "1.9.0" + "@webassemblyjs/wasm-parser" "1.9.0" + +"@webassemblyjs/wasm-parser@1.9.0": + version "1.9.0" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-parser/-/wasm-parser-1.9.0.tgz#9d48e44826df4a6598294aa6c87469d642fff65e" + integrity sha512-9+wkMowR2AmdSWQzsPEjFU7njh8HTO5MqO8vjwEHuM+AMHioNqSBONRdr0NQQ3dVQrzp0s8lTcYqzUdb7YgELA== + dependencies: + "@webassemblyjs/ast" "1.9.0" + "@webassemblyjs/helper-api-error" "1.9.0" + "@webassemblyjs/helper-wasm-bytecode" "1.9.0" + "@webassemblyjs/ieee754" "1.9.0" + "@webassemblyjs/leb128" "1.9.0" + "@webassemblyjs/utf8" "1.9.0" + +"@webassemblyjs/wast-parser@1.9.0": + version "1.9.0" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wast-parser/-/wast-parser-1.9.0.tgz#3031115d79ac5bd261556cecc3fa90a3ef451914" + integrity sha512-qsqSAP3QQ3LyZjNC/0jBJ/ToSxfYJ8kYyuiGvtn/8MK89VrNEfwj7BPQzJVHi0jGTRK2dGdJ5PRqhtjzoww+bw== + dependencies: + "@webassemblyjs/ast" "1.9.0" + "@webassemblyjs/floating-point-hex-parser" "1.9.0" + "@webassemblyjs/helper-api-error" "1.9.0" + "@webassemblyjs/helper-code-frame" "1.9.0" + "@webassemblyjs/helper-fsm" "1.9.0" "@xtuc/long" "4.2.2" -"@webassemblyjs/wast-printer@1.8.5": - version "1.8.5" - resolved "https://registry.yarnpkg.com/@webassemblyjs/wast-printer/-/wast-printer-1.8.5.tgz#114bbc481fd10ca0e23b3560fa812748b0bae5bc" - integrity sha512-w0U0pD4EhlnvRyeJzBqaVSJAo9w/ce7/WPogeXLzGkO6hzhr4GnQIZ4W4uUt5b9ooAaXPtnXlj0gzsXEOUNYMg== +"@webassemblyjs/wast-printer@1.9.0": + version "1.9.0" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wast-printer/-/wast-printer-1.9.0.tgz#4935d54c85fef637b00ce9f52377451d00d47899" + integrity sha512-2J0nE95rHXHyQ24cWjMKJ1tqB/ds8z/cyeOZxJhcb+rW+SQASVjuznUSmdz5GpVJTzU8JkhYut0D3siFDD6wsA== dependencies: - "@webassemblyjs/ast" "1.8.5" - "@webassemblyjs/wast-parser" "1.8.5" + "@webassemblyjs/ast" "1.9.0" + "@webassemblyjs/wast-parser" "1.9.0" "@xtuc/long" "4.2.2" "@xtuc/ieee754@^1.2.0": @@ -1816,6 +1828,13 @@ abbrev@1: resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8" integrity sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q== +abort-controller@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/abort-controller/-/abort-controller-3.0.0.tgz#eaf54d53b62bae4138e809ca225c8439a6efb392" + integrity sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg== + dependencies: + event-target-shim "^5.0.0" + accepts@~1.3.7: version "1.3.7" resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.7.tgz#531bc726517a3b2b41f850021c6cc15eaab507cd" @@ -1824,35 +1843,20 @@ accepts@~1.3.7: mime-types "~2.1.24" negotiator "0.6.2" -acorn-jsx@^5.0.0: - version "5.0.1" - resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.0.1.tgz#32a064fd925429216a09b141102bfdd185fae40e" - integrity sha512-HJ7CfNHrfJLlNTzIEUTj43LNWGkqpRLxm3YjAlcD0ACydk9XynzYsCBHxut+iqt+1aBXkx9UP/w/ZqMr13XIzg== - -acorn-jsx@^5.0.2: - version "5.0.2" - resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.0.2.tgz#84b68ea44b373c4f8686023a551f61a21b7c4a4f" - integrity sha512-tiNTrP1MP0QrChmD2DdupCr6HWSFeKVw5d/dHTu4Y7rkAkRhU/Dt7dphAfIUyxtHpl/eBVip5uTNSpQJHylpAw== +acorn-jsx@^5.0.0, acorn-jsx@^5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.2.0.tgz#4c66069173d6fdd68ed85239fc256226182b2ebe" + integrity sha512-HiUX/+K2YpkpJ+SzBffkM/AQ2YE03S0U1kjTLVpoJdhZMOWy8qvXVN9JdLqv2QsaQ6MPYQIuNmwD8zOiYUofLQ== -acorn-walk@^6.1.1: - version "6.2.0" - resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-6.2.0.tgz#123cb8f3b84c2171f1f7fb252615b1c78a6b1a8c" - integrity sha512-7evsyfH1cLOCdAzZAd43Cic04yKydNx0cF+7tiA19p1XnLLPU4dpCQOqpjqwokFe//vS0QqfqqjCS2JkiIs0cA== +acorn@^6.0.7, acorn@^6.4.1: + version "6.4.1" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-6.4.1.tgz#531e58ba3f51b9dacb9a6646ca4debf5b14ca474" + integrity sha512-ZVA9k326Nwrj3Cj9jlh3wGFutC2ZornPNARZwsNYqQYgN0EsV2d53w5RN/co65Ohn4sUAUtb1rSUAOD6XN9idA== -acorn@^6.0.7: - version "6.2.1" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-6.2.1.tgz#3ed8422d6dec09e6121cc7a843ca86a330a86b51" - integrity sha512-JD0xT5FCRDNyjDda3Lrg/IxFscp9q4tiYtxE1/nOzlKCk7hIRuYjhq1kCNkbPjMRMZuFq20HNQn1I9k8Oj0E+Q== - -acorn@^6.2.1: - version "6.3.0" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-6.3.0.tgz#0087509119ffa4fc0a0041d1e93a417e68cb856e" - integrity sha512-/czfa8BwS88b9gWQVhc8eknunSA2DoJpJyTQkhheIf5E48u1N0R4q/YxxsAeqRrmK9TQ/uYfgLDfZo91UlANIA== - -acorn@^7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.0.0.tgz#26b8d1cd9a9b700350b71c0905546f64d1284e7a" - integrity sha512-PaF/MduxijYYt7unVGRuds1vBC9bFxbNf+VWqhOClfdgy7RlVkQqt610ig1/yxTgsDIfW1cWDel5EBbOy3jdtQ== +acorn@^7.1.1: + version "7.2.0" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.2.0.tgz#17ea7e40d7c8640ff54a694c889c26f31704effe" + integrity sha512-apwXVmYVpQ34m/i71vrApRrRKCWQnZZF1+npOD0WV5xZFfwWOmKGQ2RWlfdy9vWITsenisM8M0Qeq8agcFHNiQ== agent-base@4, agent-base@^4.3.0: version "4.3.0" @@ -1861,6 +1865,13 @@ agent-base@4, agent-base@^4.3.0: dependencies: es6-promisify "^5.0.0" +agent-base@6: + version "6.0.0" + resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-6.0.0.tgz#5d0101f19bbfaed39980b22ae866de153b93f09a" + integrity sha512-j1Q7cSCqN+AwrmDd+pzgqc0/NpC655x2bUf5ZjRIO77DcNBFmh+OgRNzF6OKdCC9RSCb19fGd99+bhXFdkRNqw== + dependencies: + debug "4" + agent-base@~4.2.1: version "4.2.1" resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-4.2.1.tgz#d89e5999f797875674c07d87f260fc41e83e8ca9" @@ -1875,6 +1886,14 @@ agentkeepalive@^3.4.1: dependencies: humanize-ms "^1.2.1" +aggregate-error@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/aggregate-error/-/aggregate-error-3.0.1.tgz#db2fe7246e536f40d9b5442a39e117d7dd6a24e0" + integrity sha512-quoaXsZ9/BLNae5yiNoUz+Nhkwz83GhWwtYFglcjEQB2NDHCIpApbqXxIFnm4Pq/Nvhrsq5sYJFyohrrxnTGAA== + dependencies: + clean-stack "^2.0.0" + indent-string "^4.0.0" + ajv-errors@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/ajv-errors/-/ajv-errors-1.0.1.tgz#f35986aceb91afadec4102fbd85014950cefa64d" @@ -1885,38 +1904,40 @@ ajv-keywords@^3.1.0, ajv-keywords@^3.4.1: resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-3.4.1.tgz#ef916e271c64ac12171fd8384eaae6b2345854da" integrity sha512-RO1ibKvd27e6FEShVFfPALuHI3WjSVNeK5FIsmme/LYRNxjKuNj+Dt7bucLa6NdSv3JcVTyMlm9kGR84z1XpaQ== -ajv@^6.1.0, ajv@^6.10.0, ajv@^6.10.2, ajv@^6.5.5, ajv@^6.7.0, ajv@^6.9.1: - version "6.10.2" - resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.10.2.tgz#d3cea04d6b017b2894ad69040fec8b623eb4bd52" - integrity sha512-TXtUUEYHuaTEbLZWIKUr5pmBuhDLy+8KYtPYdcV8qC+pOZL+NKqYwvWSRrVXHn+ZmRRAu8vJTAznH7Oag6RVRw== +ajv@^6.1.0, ajv@^6.10.0, ajv@^6.10.2, ajv@^6.5.5, ajv@^6.9.1: + version "6.12.2" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.2.tgz#c629c5eced17baf314437918d2da88c99d5958cd" + integrity sha512-k+V+hzjm5q/Mr8ef/1Y9goCmlsK4I6Sm74teeyGvFk1XrOsbsKLjEdrvny42CZ+a8sXbk8KWpY/bDwS+FLL2UQ== dependencies: - fast-deep-equal "^2.0.1" + 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-align@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/ansi-align/-/ansi-align-2.0.0.tgz#c36aeccba563b89ceb556f3690f0b1d9e3547f7f" - integrity sha1-w2rsy6VjuJzrVW82kPCx2eNUf38= +ansi-align@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/ansi-align/-/ansi-align-3.0.0.tgz#b536b371cf687caaef236c18d3e21fe3797467cb" + integrity sha512-ZpClVKqXN3RGBmKibdfWzqCY4lnjEuoNzU5T0oEFpfd/z5qJHVarukridD4juLO2FXMiwUQxr9WqQtaYa8XRYw== dependencies: - string-width "^2.0.0" + string-width "^3.0.0" ansi-colors@3.2.3: version "3.2.3" resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-3.2.3.tgz#57d35b8686e851e2cc04c403f1c00203976a1813" integrity sha512-LEHHyuhlPY3TmuUYMh2oz89lTShfvgbmzaBcxve9t/9Wuy7Dwf4yoAKcND7KFT1HAQfqZ12qtc+DUrBMeKF9nw== -ansi-escapes@^1.0.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-1.4.0.tgz#d3a8a83b319aa67793662b13e761c7911422306e" - integrity sha1-06ioOzGapneTZisT52HHkRQiMG4= - ansi-escapes@^3.2.0: version "3.2.0" resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-3.2.0.tgz#8780b98ff9dbf5638152d1f1fe5c1d7b4442976b" integrity sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ== +ansi-escapes@^4.2.1: + version "4.3.1" + resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-4.3.1.tgz#a5c47cc43181f1f38ffd7076837700d395522a61" + integrity sha512-JWF7ocqNrp8u9oqpgV+wH5ftbt+cfvv+PTjOvKLT3AdYly/LmORARfEVT1iyjwN+4MqE5UmVKoAdIBqeoCHgLA== + dependencies: + type-fest "^0.11.0" + ansi-regex@^2.0.0: version "2.1.1" resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df" @@ -1932,6 +1953,11 @@ ansi-regex@^4.1.0: resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-4.1.0.tgz#8b9f8f08cf1acb843756a839ca8c7e3168c51997" integrity sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg== +ansi-regex@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.0.tgz#388539f55179bf39339c81af30a654d69f87cb75" + integrity sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg== + ansi-styles@^2.2.1: version "2.2.1" resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-2.2.1.tgz#b432dd3358b634cf75e1e4664368240533c1ddbe" @@ -1944,6 +1970,14 @@ ansi-styles@^3.2.0, ansi-styles@^3.2.1: dependencies: color-convert "^1.9.0" +ansi-styles@^4.0.0, ansi-styles@^4.1.0: + version "4.2.1" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.2.1.tgz#90ae75c424d008d2624c5bf29ead3177ebfcf359" + integrity sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA== + dependencies: + "@types/color-name" "^1.1.1" + color-convert "^2.0.1" + any-promise@^1.0.0: version "1.3.0" resolved "https://registry.yarnpkg.com/any-promise/-/any-promise-1.3.0.tgz#abc6afeedcea52e809cdc0376aed3ce39635d17f" @@ -1957,12 +1991,30 @@ anymatch@^2.0.0: micromatch "^3.1.4" normalize-path "^2.1.1" -append-transform@^1.0.0: +anymatch@~3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.1.tgz#c55ecf02185e2469259399310c173ce31233b142" + integrity sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg== + dependencies: + normalize-path "^3.0.0" + picomatch "^2.0.4" + +app-root-path@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/app-root-path/-/app-root-path-3.0.0.tgz#210b6f43873227e18a4b810a032283311555d5ad" + integrity sha512-qMcx+Gy2UZynHjOHOIXPNvpf+9cjvk3cWrBBK7zg4gH9+clobJRb9NGzcT7mQTcV/6Gm/1WelUtqxVXnNlrwcw== + +append-field@^1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/append-transform/-/append-transform-1.0.0.tgz#046a52ae582a228bd72f58acfbe2967c678759ab" - integrity sha512-P009oYkeHyU742iSZJzZZywj4QRJdnTWffaKuJQLablCZ1uz6/cW4yaRgcDaoQ+uwOxxnt0gRUcwfsNP2ri0gw== + resolved "https://registry.yarnpkg.com/append-field/-/append-field-1.0.0.tgz#1e3440e915f0b1203d23748e78edd7b9b5b43e56" + integrity sha1-HjRA6RXwsSA9I3SOeO3XubW0PlY= + +append-transform@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/append-transform/-/append-transform-2.0.0.tgz#99d9d29c7b38391e6f428d28ce136551f0b77e12" + integrity sha512-7yeyCEurROLQJFv5Xj4lEGTy0borxepjFv1g22oAdqFu//SrAlDl1O1Nxx15SH1RoliUml6p8dwJW9jvZughhg== dependencies: - default-require-extensions "^2.0.0" + default-require-extensions "^3.0.0" aproba@^1.0.3, aproba@^1.1.1: version "1.2.0" @@ -1974,11 +2026,6 @@ aproba@^2.0.0: resolved "https://registry.yarnpkg.com/aproba/-/aproba-2.0.0.tgz#52520b8ae5b569215b354efc0caa3fe1e45a8adc" integrity sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ== -arch@2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/arch/-/arch-2.1.1.tgz#8f5c2731aa35a30929221bb0640eed65175ec84e" - integrity sha512-BLM56aPo9vLLFVa8+/+pJLnrZ7QGGTVHWsCwieAWT9o9K8UeGaQbzZbGoabWLOo2ksBCztoXdqBZBplqLDDCSg== - archy@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/archy/-/archy-1.0.0.tgz#f9c8c13757cc1dd7bc379ac77b2c62a5c2868c40" @@ -1993,9 +2040,9 @@ are-we-there-yet@~1.1.2: readable-stream "^2.0.6" arg@^4.1.0: - version "4.1.1" - resolved "https://registry.yarnpkg.com/arg/-/arg-4.1.1.tgz#485f8e7c390ce4c5f78257dbea80d4be11feda4c" - integrity sha512-SlmP3fEA88MBv0PypnXZ8ZfJhwmDeIE3SP71j37AiXQBXYosPV0x6uISAaHYSlSVhmHOVkomen0tbGk6Anlebw== + version "4.1.3" + resolved "https://registry.yarnpkg.com/arg/-/arg-4.1.3.tgz#269fc7ad5b8e42cb63c896d5666017261c144089" + integrity sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA== argparse@^1.0.7: version "1.0.10" @@ -2044,13 +2091,14 @@ array-ify@^1.0.0: resolved "https://registry.yarnpkg.com/array-ify/-/array-ify-1.0.0.tgz#9e528762b4a9066ad163a6962a364418e9626ece" integrity sha1-nlKHYrSpBmrRY6aWKjZEGOlibs4= -array-includes@^3.0.3: - version "3.0.3" - resolved "https://registry.yarnpkg.com/array-includes/-/array-includes-3.0.3.tgz#184b48f62d92d7452bb31b323165c7f8bd02266d" - integrity sha1-GEtI9i2S10UrsxsyMWXH+L0CJm0= +array-includes@^3.0.3, array-includes@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/array-includes/-/array-includes-3.1.1.tgz#cdd67e6852bdf9c1215460786732255ed2459348" + integrity sha512-c2VXaCHl7zPsvpkFsw4nxvFie4fh1ur9bpcgsVkIjqn0H/Xwdg+7fv3n2r/isyS8EBj5b06M9kHyZuIr4El6WQ== dependencies: - define-properties "^1.1.2" - es-abstract "^1.7.0" + define-properties "^1.1.3" + es-abstract "^1.17.0" + is-string "^1.0.5" array-union@^1.0.2: version "1.0.2" @@ -2069,11 +2117,24 @@ array-unique@^0.3.2: resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.3.2.tgz#a894b75d4bc4f6cd679ef3244a9fd8f46ae2d428" integrity sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg= +array.prototype.flat@^1.2.1: + version "1.2.3" + resolved "https://registry.yarnpkg.com/array.prototype.flat/-/array.prototype.flat-1.2.3.tgz#0de82b426b0318dbfdb940089e38b043d37f6c7b" + integrity sha512-gBlRZV0VSmfPIeWfuuy56XZMvbVfbEUnOXUvt3F/eUUUSyzlgLxhEX4YAEpxNAogRGehPSnfXyPtYyKAhkzQhQ== + dependencies: + define-properties "^1.1.3" + es-abstract "^1.17.0-next.1" + arrify@^1.0.0, arrify@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/arrify/-/arrify-1.0.1.tgz#898508da2226f380df904728456849c1501a4b0d" integrity sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0= +arrify@^2.0.0, arrify@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/arrify/-/arrify-2.0.1.tgz#c9655e9331e0abcd588d2a7cad7e9956f66701fa" + integrity sha512-3duEwti880xqi4eAMN8AyR4a0ByT90zoYdLlevfrvU43vb0YZwZVfxOgxWrLXXXpyugL0hNZc9G6BiB5B3nUug== + asap@^2.0.0: version "2.0.6" resolved "https://registry.yarnpkg.com/asap/-/asap-2.0.6.tgz#e50347611d7e690943208bbdafebcbc2fb866d46" @@ -2088,7 +2149,7 @@ asn1.js@^4.0.0: inherits "^2.0.1" minimalistic-assert "^1.0.0" -asn1@~0.2.3: +asn1@^0.2.4, asn1@~0.2.3: version "0.2.4" resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.4.tgz#8d2475dfab553bb33e77b54e59e880bb8ce23136" integrity sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg== @@ -2108,11 +2169,6 @@ assert@^1.1.1: object-assign "^4.1.1" util "0.10.3" -assertion-error@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/assertion-error/-/assertion-error-1.1.0.tgz#e60b6b0e8f301bd97e5375215bda406c85118c0b" - integrity sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw== - assign-symbols@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/assign-symbols/-/assign-symbols-1.0.0.tgz#59667f41fadd4f20ccbc2bb96b8d4f7f78ec0367" @@ -2129,21 +2185,16 @@ async-each@^1.0.1: integrity sha512-z/WhQ5FPySLdvREByI2vZiTWwCnF0moMJ1hK9YQwDTHKh6I7/uSckMetoRGb5UBZPC1z0jlw+n/XCgjeH7y1AQ== async-limiter@~1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/async-limiter/-/async-limiter-1.0.0.tgz#78faed8c3d074ab81f22b4e985d79e8738f720f8" - integrity sha512-jp/uFnooOiO+L211eZOoSyzpOITMXx1rBITauYykG3BRYPu8h0UcxsPNB04RR5vo4Tyz3+ay17tR6JVf9qzYWg== + version "1.0.1" + resolved "https://registry.yarnpkg.com/async-limiter/-/async-limiter-1.0.1.tgz#dd379e94f0db8310b08291f9d64c3209766617fd" + integrity sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ== -async@2.6.1: - version "2.6.1" - resolved "https://registry.yarnpkg.com/async/-/async-2.6.1.tgz#b245a23ca71930044ec53fa46aa00a3e87c6a610" - integrity sha512-fNEiL2+AZt6AlAw/29Cr0UDe4sRAHCpEHh54WMz+Bb7QfNcFw4h3loofyJpLeQs4Yx7yuqu/2dLgM5hKOs6HlQ== +async-retry@1.3.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/async-retry/-/async-retry-1.3.1.tgz#139f31f8ddce50c0870b0ba558a6079684aaed55" + integrity sha512-aiieFW/7h3hY0Bq5d+ktDBejxuwR78vRu9hDUdR8rNhSaQ29VzPL4AoIRG7D/c7tdenwOcKvgPM6tIxB3cB6HA== dependencies: - lodash "^4.17.10" - -async@^1.3.0: - version "1.5.2" - resolved "https://registry.yarnpkg.com/async/-/async-1.5.2.tgz#ec6a61ae56480c0c3cb241c95618e20892f9672a" - integrity sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo= + retry "0.12.0" asynckit@^0.4.0: version "0.4.0" @@ -2155,50 +2206,69 @@ atob-lite@^2.0.0: resolved "https://registry.yarnpkg.com/atob-lite/-/atob-lite-2.0.0.tgz#0fef5ad46f1bd7a8502c65727f0367d5ee43d696" integrity sha1-D+9a1G8b16hQLGVyfwNn1e5D1pY= -atob@^2.1.1: +atob@^2.1.2: version "2.1.2" resolved "https://registry.yarnpkg.com/atob/-/atob-2.1.2.tgz#6d9517eb9e030d2436666651e86bd9f6f13533c9" integrity sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg== +await-lock@2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/await-lock/-/await-lock-2.0.1.tgz#b3f65fdf66e08f7538260f79b46c15bcfc18cadd" + integrity sha512-ntLi9fzlMT/vWjC1wwVI11/cSRJ3nTS35qVekNc9WnaoMOP2eWH0RvIqwLQkDjX4a4YynsKEv+Ere2VONp9wxg== + +aws-sdk@2.676.0: + version "2.676.0" + resolved "https://registry.yarnpkg.com/aws-sdk/-/aws-sdk-2.676.0.tgz#add497d57438f450a95efe570a4fd55654d7c16e" + integrity sha512-+ftolB59dL+7M6hRihhlKSDKNPfm0AJe48gtkwVJYljYubz1Y7vAwveRgBc18L9aoRXH2obFC5Ivf3r0dcfF2A== + dependencies: + buffer "4.9.1" + events "1.1.1" + ieee754 "1.1.13" + jmespath "0.15.0" + querystring "0.2.0" + sax "1.2.1" + url "0.10.3" + uuid "3.3.2" + xml2js "0.4.19" + aws-sign2@~0.7.0: version "0.7.0" resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.7.0.tgz#b46e890934a9591f2d2f6f86d7e6a9f1b3fe76a8" integrity sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg= aws4@^1.8.0: - version "1.8.0" - resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.8.0.tgz#f0e003d9ca9e7f59c7a508945d7b2ef9a04a542f" - integrity sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ== + version "1.9.1" + resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.9.1.tgz#7e33d8f7d449b3f673cd72deb9abdc552dbe528e" + integrity sha512-wMHVg2EOHaMRxbzgFJ9gtjOOCrI80OHLG14rxi28XwOW8ux6IiEbRCGGGqCtdAIg4FQCbW20k9RsT4y3gJlFug== -axios@0.18.1, axios@^0.18.0: - version "0.18.1" - resolved "https://registry.yarnpkg.com/axios/-/axios-0.18.1.tgz#ff3f0de2e7b5d180e757ad98000f1081b87bcea3" - integrity sha512-0BfJq4NSfQXd+SkFdrvFbG7addhYSBA2mQwISr46pD6E5iqkWg02RAs8vyTT/j0RTnoYmeXauBuSv1qKwR179g== +axios@^0.19.1, axios@^0.19.2: + version "0.19.2" + resolved "https://registry.yarnpkg.com/axios/-/axios-0.19.2.tgz#3ea36c5d8818d0d5f8a8a97a6d36b86cdc00cb27" + integrity sha512-fjgm5MvRHLhx+osE2xoekY70AhARk3a6hkN+3Io1jc00jtquGvxYlKlsFUhmUET0V5te6CcZI7lcv2Ym61mjHA== dependencies: follow-redirects "1.5.10" - is-buffer "^2.0.2" babel-eslint@^10.0.1: - version "10.0.2" - resolved "https://registry.yarnpkg.com/babel-eslint/-/babel-eslint-10.0.2.tgz#182d5ac204579ff0881684b040560fdcc1558456" - integrity sha512-UdsurWPtgiPgpJ06ryUnuaSXC2s0WoSZnQmEpbAH65XZSdwowgN5MvyP7e88nW07FYXv72erVtpBkxyDVKhH1Q== + version "10.1.0" + resolved "https://registry.yarnpkg.com/babel-eslint/-/babel-eslint-10.1.0.tgz#6968e568a910b78fb3779cdd8b6ac2f479943232" + integrity sha512-ifWaTHQ0ce+448CYop8AdrQiBsGrnC+bMgfyKFdi6EsPLTAWG+QfyDeM6OH+FmWnKvEq5NnBMLvlBUPKQZoDSg== dependencies: "@babel/code-frame" "^7.0.0" - "@babel/parser" "^7.0.0" - "@babel/traverse" "^7.0.0" - "@babel/types" "^7.0.0" - eslint-scope "3.7.1" + "@babel/parser" "^7.7.0" + "@babel/traverse" "^7.7.0" + "@babel/types" "^7.7.0" eslint-visitor-keys "^1.0.0" + resolve "^1.12.0" balanced-match@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767" integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c= -base64-js@^1.0.2: - version "1.3.0" - resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.3.0.tgz#cab1e6118f051095e58b5281aea8c1cd22bfc0e3" - integrity sha512-ccav/yGvoa80BQDljCxsmmQ3Xvx60/UpBIij5QN21W3wBi/hhIC9OoO+KLpu9IJTS9j4DRVJ3aDDF9cMSoa2lw== +base64-js@^1.0.2, base64-js@^1.3.0: + version "1.3.1" + resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.3.1.tgz#58ece8cb75dd07e71ed08c736abc5fac4dbf8df1" + integrity sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g== base@^0.11.1: version "0.11.2" @@ -2213,7 +2283,7 @@ base@^0.11.1: mixin-deep "^1.2.0" pascalcase "^0.1.1" -basic-auth@~2.0.0: +basic-auth@~2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/basic-auth/-/basic-auth-2.0.1.tgz#b998279bf47ce38344b4f3cf916d4679bbf51e3a" integrity sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg== @@ -2232,51 +2302,57 @@ before-after-hook@^2.0.0: resolved "https://registry.yarnpkg.com/before-after-hook/-/before-after-hook-2.1.0.tgz#b6c03487f44e24200dd30ca5e6a1979c5d2fb635" integrity sha512-IWIbu7pMqyw3EAJHzzHbWa85b6oud/yfKYg5rqB5hNE8CeMi3nX+2C2sj0HswfblST86hpVEOAb9x34NZd6P7A== -bfj@^6.1.1: - version "6.1.2" - resolved "https://registry.yarnpkg.com/bfj/-/bfj-6.1.2.tgz#325c861a822bcb358a41c78a33b8e6e2086dde7f" - integrity sha512-BmBJa4Lip6BPRINSZ0BPEIfB1wUY/9rwbwvIHQA1KjX9om29B6id0wnWXq7m3bn5JrUVjeOTnVuhPT1FiHwPGw== - dependencies: - bluebird "^3.5.5" - check-types "^8.0.3" - hoopy "^0.1.4" - tryer "^1.0.1" - big.js@^5.2.2: version "5.2.2" resolved "https://registry.yarnpkg.com/big.js/-/big.js-5.2.2.tgz#65f0af382f578bcdc742bd9c281e9cb2d7768328" integrity sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ== +bignumber.js@^7.0.0: + version "7.2.1" + resolved "https://registry.yarnpkg.com/bignumber.js/-/bignumber.js-7.2.1.tgz#80c048759d826800807c4bfd521e50edbba57a5f" + integrity sha512-S4XzBk5sMB+Rcb/LNcpzXr57VRTxgAvaAEDAl1AwRx27j00hT84O6OkteE7u8UB3NuaaygCRrEpqox4uDOrbdQ== + binary-extensions@^1.0.0: version "1.13.1" resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-1.13.1.tgz#598afe54755b2868a5330d2aff9d4ebb53209b65" integrity sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw== -bluebird@3.5.0: - version "3.5.0" - resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.5.0.tgz#791420d7f551eea2897453a8a77653f96606d67c" - integrity sha1-eRQg1/VR7qKJdFOop3ZT+WYG1nw= +binary-extensions@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.0.0.tgz#23c0df14f6a88077f5f986c0d167ec03c3d5537c" + integrity sha512-Phlt0plgpIIBOGTT/ehfFnbNlfsDEiqmzE2KRXoX1bLIlir4X/MR+zSyBEkL05ffWgnRSf/DXv+WrUAVr93/ow== -bluebird@3.7.1, bluebird@^3.7.1: - version "3.7.1" - resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.7.1.tgz#df70e302b471d7473489acf26a93d63b53f874de" - integrity sha512-DdmyoGCleJnkbp3nkbxTLJ18rjDsE4yCggEwKNXkeV123sPNfOCYeDoeuOY+F2FrSjO1YXcTU+dsy96KMy+gcg== +bindings@^1.5.0: + version "1.5.0" + resolved "https://registry.yarnpkg.com/bindings/-/bindings-1.5.0.tgz#10353c9e945334bc0511a6d90b38fbc7c9c504df" + integrity sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ== + dependencies: + file-uri-to-path "1.0.0" -bluebird@^3.5.0, bluebird@^3.5.3, bluebird@^3.5.5: - version "3.5.5" - resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.5.5.tgz#a8d0afd73251effbbd5fe384a77d73003c17a71f" - integrity sha512-5am6HnnfN+urzt4yfg7IgTbotDjIT/u8AJpEt0sIU9FtXfVeezXAPKswrG+xKUCOYAINpSdgZVDU6QFh+cuH3w== +bl@^4.0.1: + version "4.0.2" + resolved "https://registry.yarnpkg.com/bl/-/bl-4.0.2.tgz#52b71e9088515d0606d9dd9cc7aa48dc1f98e73a" + integrity sha512-j4OH8f6Qg2bGuWfRiltT2HYGx0e1QcBTrK9KAHNMwMZdQnDZFk0ZSYIpADjYCB3U12nicC5tVJwSIhwOWjb4RQ== + dependencies: + buffer "^5.5.0" + inherits "^2.0.4" + readable-stream "^3.4.0" -bluebird@^3.5.1: - version "3.7.0" - resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.7.0.tgz#56a6a886e03f6ae577cffedeb524f8f2450293cf" - integrity sha512-aBQ1FxIa7kSWCcmKHlcHFlT2jt6J/l4FzC7KcPELkOJOsPOb/bccdhmIrKDfXhwFrmc7vDoDrrepFvGqjyXGJg== +bluebird@3.7.2, bluebird@^3.5.0, bluebird@^3.5.1, bluebird@^3.5.3, bluebird@^3.5.5, bluebird@^3.7.1: + version "3.7.2" + resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.7.2.tgz#9f229c15be272454ffa973ace0dbee79a1b0c36f" + integrity sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg== -bn.js@^4.0.0, bn.js@^4.1.0, bn.js@^4.1.1, bn.js@^4.4.0: +bn.js@^4.0.0, bn.js@^4.1.0, bn.js@^4.4.0: version "4.11.8" resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.11.8.tgz#2cde09eb5ee341f484746bb0309b3253b1b1442f" integrity sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA== +bn.js@^5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-5.1.1.tgz#48efc4031a9c4041b9c99c6941d903463ab62eb5" + integrity sha512-IUTD/REb78Z2eodka1QZyyEk66pciRcP6Sroka0aI3tG/iwIdYLrBD62RsubR7vqdt3WyX8p4jxeatzmRSphtA== + body-parser@1.19.0: version "1.19.0" resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.19.0.tgz#96b2709e57c9c4e09a6fd66a8fd979844f69f08a" @@ -2293,18 +2369,19 @@ body-parser@1.19.0: raw-body "2.4.0" type-is "~1.6.17" -boxen@^1.2.1: - version "1.3.0" - resolved "https://registry.yarnpkg.com/boxen/-/boxen-1.3.0.tgz#55c6c39a8ba58d9c61ad22cd877532deb665a20b" - integrity sha512-TNPjfTr432qx7yOjQyaXm3dSR0MH9vXp7eT1BFSl/C51g+EFnOR9hTg1IreahGBmDNCehscshe45f+C1TBZbLw== - dependencies: - ansi-align "^2.0.0" - camelcase "^4.0.0" - chalk "^2.0.1" - cli-boxes "^1.0.0" - string-width "^2.0.0" - term-size "^1.2.0" - widest-line "^2.0.0" +boxen@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/boxen/-/boxen-4.2.0.tgz#e411b62357d6d6d36587c8ac3d5d974daa070e64" + integrity sha512-eB4uT9RGzg2odpER62bBwSLvUeGC+WbRjjyyFhGsKnc8wp/m0+hQsMUvUe3H2V0D5vw0nBdO1hCJoZo5mKeuIQ== + dependencies: + ansi-align "^3.0.0" + camelcase "^5.3.1" + chalk "^3.0.0" + cli-boxes "^2.2.0" + string-width "^4.1.0" + term-size "^2.1.0" + type-fest "^0.8.1" + widest-line "^3.1.0" brace-expansion@^1.1.7: version "1.1.11" @@ -2330,7 +2407,7 @@ braces@^2.3.1, braces@^2.3.2: split-string "^3.0.2" to-regex "^3.0.1" -braces@^3.0.1: +braces@^3.0.1, braces@~3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107" integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A== @@ -2378,7 +2455,7 @@ browserify-des@^1.0.0: inherits "^2.0.1" safe-buffer "^5.1.2" -browserify-rsa@^4.0.0: +browserify-rsa@^4.0.0, browserify-rsa@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/browserify-rsa/-/browserify-rsa-4.0.1.tgz#21e0abfaf6f2029cf2fafb133567a701d4135524" integrity sha1-IeCr+vbyApzy+vsTNWenAdQTVSQ= @@ -2387,17 +2464,18 @@ browserify-rsa@^4.0.0: randombytes "^2.0.1" browserify-sign@^4.0.0: - version "4.0.4" - resolved "https://registry.yarnpkg.com/browserify-sign/-/browserify-sign-4.0.4.tgz#aa4eb68e5d7b658baa6bf6a57e630cbd7a93d298" - integrity sha1-qk62jl17ZYuqa/alfmMMvXqT0pg= - dependencies: - bn.js "^4.1.1" - browserify-rsa "^4.0.0" - create-hash "^1.1.0" - create-hmac "^1.1.2" - elliptic "^6.0.0" - inherits "^2.0.1" - parse-asn1 "^5.0.0" + version "4.1.0" + resolved "https://registry.yarnpkg.com/browserify-sign/-/browserify-sign-4.1.0.tgz#4fe971b379a5aeb4925e06779f9fa1f41d249d70" + integrity sha512-VYxo7cDCeYUoBZ0ZCy4UyEUCP3smyBd4DRQM5nrFS1jJjPJjX7rP3oLRpPoWfkhQfyJ0I9ZbHbKafrFD/SGlrg== + dependencies: + bn.js "^5.1.1" + browserify-rsa "^4.0.1" + create-hash "^1.2.0" + create-hmac "^1.1.7" + elliptic "^6.5.2" + inherits "^2.0.4" + parse-asn1 "^5.1.5" + readable-stream "^3.6.0" browserify-zlib@^0.2.0: version "0.2.0" @@ -2411,11 +2489,6 @@ btoa-lite@^1.0.0: resolved "https://registry.yarnpkg.com/btoa-lite/-/btoa-lite-1.0.0.tgz#337766da15801210fdd956c22e9c6891ab9d0337" integrity sha1-M3dm2hWAEhD92VbCLpxokaudAzc= -buffer-crc32@~0.2.3: - version "0.2.13" - resolved "https://registry.yarnpkg.com/buffer-crc32/-/buffer-crc32-0.2.13.tgz#0d333e3f00eac50aa1454abd30ef8c2a5d9a7242" - integrity sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI= - buffer-equal-constant-time@1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz#f8e71132f7ffe6e01a5c9697a4c6f3e48d5cc819" @@ -2436,7 +2509,7 @@ buffer-xor@^1.0.3: resolved "https://registry.yarnpkg.com/buffer-xor/-/buffer-xor-1.0.3.tgz#26e61ed1422fb70dd42e6e36729ed51d855fe8d9" integrity sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk= -buffer@^4.3.0: +buffer@4.9.1: version "4.9.1" resolved "https://registry.yarnpkg.com/buffer/-/buffer-4.9.1.tgz#6d1bb601b07a4efced97094132093027c95bc298" integrity sha1-bRu2AbB6TvztlwlBMgkwJ8lbwpg= @@ -2445,6 +2518,23 @@ buffer@^4.3.0: ieee754 "^1.1.4" isarray "^1.0.0" +buffer@^4.3.0: + version "4.9.2" + resolved "https://registry.yarnpkg.com/buffer/-/buffer-4.9.2.tgz#230ead344002988644841ab0244af8c44bbe3ef8" + integrity sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg== + dependencies: + base64-js "^1.0.2" + ieee754 "^1.1.4" + isarray "^1.0.0" + +buffer@^5.1.0, buffer@^5.5.0: + version "5.6.0" + resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.6.0.tgz#a31749dc7d81d84db08abf937b6b8c4033f62786" + integrity sha512-/gDYp/UtU0eA1ys8bOs9J6a+E/KWIY+DZ+Q2WESNUA0jFRsJOc0SNUO6xJ5SGA1xueg3NL65W6s+NY5l9cunuw== + dependencies: + base64-js "^1.0.2" + ieee754 "^1.1.4" + builtin-status-codes@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz#85982878e21b98e1c66425e03d0174788f569ee8" @@ -2455,6 +2545,14 @@ builtins@^1.0.3: resolved "https://registry.yarnpkg.com/builtins/-/builtins-1.0.3.tgz#cb94faeb61c8696451db36534e1422f94f0aee88" integrity sha1-y5T662HIaWRR2zZTThQi+U8K7og= +busboy@^0.2.11: + version "0.2.14" + resolved "https://registry.yarnpkg.com/busboy/-/busboy-0.2.14.tgz#6c2a622efcf47c57bbbe1e2a9c37ad36c7925453" + integrity sha1-bCpiLvz0fFe7vh4qnDetNseSVFM= + dependencies: + dicer "0.2.5" + readable-stream "1.1.x" + byline@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/byline/-/byline-5.0.0.tgz#741c5216468eadc457b03410118ad77de8c1ddb1" @@ -2471,9 +2569,9 @@ bytes@3.1.0: integrity sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg== cacache@^12.0.0, cacache@^12.0.2, cacache@^12.0.3: - version "12.0.3" - resolved "https://registry.yarnpkg.com/cacache/-/cacache-12.0.3.tgz#be99abba4e1bf5df461cd5a2c1071fc432573390" - integrity sha512-kqdmfXEGFepesTuROHMs3MpFLWrPkSSpRqOw80RCflZXy/khxaArvFrQ7uJxSUduzAufc6G0g1VUCOZXxWavPw== + version "12.0.4" + resolved "https://registry.yarnpkg.com/cacache/-/cacache-12.0.4.tgz#668bcbd105aeb5f1d92fe25570ec9525c8faa40c" + integrity sha512-a0tMB40oefvuInr4Cwb3GerbL9xTj1D5yg0T5xrjGCGyfvbxseIXX7BAO/u/hIXdafzOI5JC3wDwHyf24buOAQ== dependencies: bluebird "^3.5.5" chownr "^1.1.1" @@ -2506,22 +2604,28 @@ cache-base@^1.0.1: union-value "^1.0.0" unset-value "^1.0.0" -cachedir@1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/cachedir/-/cachedir-1.3.0.tgz#5e01928bf2d95b5edd94b0942188246740e0dbc4" - integrity sha512-O1ji32oyON9laVPJL1IZ5bmwd2cB46VfpxkDequezH+15FDzzVddEyrGEeX4WusDSqKxdyFdDQDEG1yo1GoWkg== - dependencies: - os-homedir "^1.0.1" - -caching-transform@^3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/caching-transform/-/caching-transform-3.0.2.tgz#601d46b91eca87687a281e71cef99791b0efca70" - integrity sha512-Mtgcv3lh3U0zRii/6qVgQODdPA4G3zhG+jtbCWj39RXuUFTMzH0vcdMtaJS1jPowd+It2Pqr6y3NJMQqOqCE2w== +cacheable-request@^6.0.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/cacheable-request/-/cacheable-request-6.1.0.tgz#20ffb8bd162ba4be11e9567d823db651052ca912" + integrity sha512-Oj3cAGPCqOZX7Rz64Uny2GYAZNliQSqfbePrgAQ1wKAihYmCUnraBtJtKcGR4xz7wF+LoJC+ssFZvv5BgF9Igg== + dependencies: + clone-response "^1.0.2" + get-stream "^5.1.0" + http-cache-semantics "^4.0.0" + keyv "^3.0.0" + lowercase-keys "^2.0.0" + normalize-url "^4.1.0" + responselike "^1.0.2" + +caching-transform@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/caching-transform/-/caching-transform-4.0.0.tgz#00d297a4206d71e2163c39eaffa8157ac0651f0f" + integrity sha512-kpqOvwXnjjN44D89K5ccQC+RUrsy7jB/XLlRrx0D7/2HNcTPqzsb6XgYoErwko6QsV184CA2YgS1fxDiiDZMWA== dependencies: - hasha "^3.0.0" - make-dir "^2.0.0" - package-hash "^3.0.0" - write-file-atomic "^2.4.2" + hasha "^5.0.0" + make-dir "^3.0.0" + package-hash "^4.0.0" + write-file-atomic "^3.0.0" call-me-maybe@^1.0.1: version "1.0.1" @@ -2569,44 +2673,41 @@ camelcase-keys@^4.0.0: map-obj "^2.0.0" quick-lru "^1.0.0" +camelcase-keys@^6.2.2: + version "6.2.2" + resolved "https://registry.yarnpkg.com/camelcase-keys/-/camelcase-keys-6.2.2.tgz#5e755d6ba51aa223ec7d3d52f25778210f9dc3c0" + integrity sha512-YrwaA0vEKazPBkn0ipTiMpSajYDSe+KjQfrjhcBMxJt/znbvlHd8Pw/Vamaz5EB4Wfhs3SUR3Z9mwRu/P3s3Yg== + dependencies: + camelcase "^5.3.1" + map-obj "^4.0.0" + quick-lru "^4.0.1" + camelcase@^2.0.0: version "2.1.1" resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-2.1.1.tgz#7c1d16d679a1bbe59ca02cacecfb011e201f5a1f" integrity sha1-fB0W1nmhu+WcoCys7PsBHiAfWh8= -camelcase@^4.0.0, camelcase@^4.1.0: +camelcase@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-4.1.0.tgz#d545635be1e33c542649c69173e5de6acfae34dd" integrity sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0= -camelcase@^5.0.0: +camelcase@^5.0.0, camelcase@^5.3.1: version "5.3.1" resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320" integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg== -capture-stack-trace@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/capture-stack-trace/-/capture-stack-trace-1.0.1.tgz#a6c0bbe1f38f3aa0b92238ecb6ff42c344d4135d" - integrity sha512-mYQLZnx5Qt1JgB1WEiMCf2647plpGeQ2NMR/5L0HNZzGQo4fuSPnK+wjfPnKZV0aiJDgzmWqqkV/g7JD+DW0qw== +camelcase@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.0.0.tgz#5259f7c30e35e278f1bdc2a4d91230b37cad981e" + integrity sha512-8KMDF1Vz2gzOq54ONPJS65IvTUaB1cHJ2DMM7MbPmLZljDH1qpzzLsWdiN9pHh6qvkRVDTi/07+eNGch/oLU4w== caseless@~0.12.0: version "0.12.0" resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc" integrity sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw= -chai@4.2.0: - version "4.2.0" - resolved "https://registry.yarnpkg.com/chai/-/chai-4.2.0.tgz#760aa72cf20e3795e84b12877ce0e83737aa29e5" - integrity sha512-XQU3bhBukrOsQCuwZndwGcCVQHyZi53fQ6Ys1Fym7E4olpIqqZZhhoFJoaKVvV17lWQoXYwgWN2nF5crA8J2jw== - dependencies: - assertion-error "^1.1.0" - check-error "^1.0.2" - deep-eql "^3.0.1" - get-func-name "^2.0.0" - pathval "^1.1.0" - type-detect "^4.0.5" - -chalk@2.4.2, chalk@^2.0.0, chalk@^2.0.1, chalk@^2.1.0, chalk@^2.3.0, chalk@^2.3.1, chalk@^2.4.1, chalk@^2.4.2: +chalk@2.4.2, chalk@^2.0.0, chalk@^2.1.0, chalk@^2.3.0, chalk@^2.3.1, chalk@^2.4.2: version "2.4.2" resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== @@ -2615,7 +2716,7 @@ chalk@2.4.2, chalk@^2.0.0, chalk@^2.0.1, chalk@^2.1.0, chalk@^2.3.0, chalk@^2.3. escape-string-regexp "^1.0.5" supports-color "^5.3.0" -chalk@^1.0.0, chalk@^1.1.1, chalk@^1.1.3: +chalk@^1.1.1: version "1.1.3" resolved "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98" integrity sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg= @@ -2626,56 +2727,53 @@ chalk@^1.0.0, chalk@^1.1.1, chalk@^1.1.3: strip-ansi "^3.0.0" supports-color "^2.0.0" +chalk@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-3.0.0.tgz#3f73c2bf526591f574cc492c51e2456349f844e4" + integrity sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg== + dependencies: + ansi-styles "^4.1.0" + supports-color "^7.1.0" + +chalk@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.0.0.tgz#6e98081ed2d17faab615eb52ac66ec1fe6209e72" + integrity sha512-N9oWFcegS0sFr9oh1oz2d7Npos6vNoWW9HvtCg5N1KRFpUhaAhvTv5Y58g880fZaEYSNm3qDz8SU1UrGvp+n7A== + dependencies: + ansi-styles "^4.1.0" + supports-color "^7.1.0" + chardet@^0.7.0: version "0.7.0" resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e" integrity sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA== -check-error@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/check-error/-/check-error-1.0.2.tgz#574d312edd88bb5dd8912e9286dd6c0aed4aac82" - integrity sha1-V00xLt2Iu13YkS6Sht1sCu1KrII= - -check-more-types@2.24.0: - version "2.24.0" - resolved "https://registry.yarnpkg.com/check-more-types/-/check-more-types-2.24.0.tgz#1420ffb10fd444dcfc79b43891bbfffd32a84600" - integrity sha1-FCD/sQ/URNz8ebQ4kbv//TKoRgA= - -check-node-version@4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/check-node-version/-/check-node-version-4.0.1.tgz#ae35200e5434c1aae3712c8d9f63b9a2144a10bd" - integrity sha512-pWV+uuJJoOGbODDC6+DPUYeprv1CUg/jr1SGKpgkANstGN22f9T0Vn40mdv6hvRZ25KMH/IjkVc0LZH5ms+qEg== +check-node-version@4.0.3: + version "4.0.3" + resolved "https://registry.yarnpkg.com/check-node-version/-/check-node-version-4.0.3.tgz#b783dc7267b2db0c2d0f0f51d75c9b6a5ee292f8" + integrity sha512-kbnOaF7SE1uasWx3hqxzsU2yz7I+rkxSMuTW0eKoF39eUtSR/a0F4Sm35LwJNx4itOIARf9eLhWHOOh7rlZ5/g== dependencies: - chalk "^2.3.0" + chalk "^3.0.0" map-values "^1.0.1" minimist "^1.2.0" object-filter "^1.0.2" run-parallel "^1.1.4" - semver "^5.7.0" - -check-types@^8.0.3: - version "8.0.3" - resolved "https://registry.yarnpkg.com/check-types/-/check-types-8.0.3.tgz#3356cca19c889544f2d7a95ed49ce508a0ecf552" - integrity sha512-YpeKZngUmG65rLudJ4taU7VLkOCTMhNl/u4ctNC56LQS/zJTyNH0Lrtwm1tfTsbLlwvlfsA2d1c8vCf/Kh2KwQ== + semver "^6.3.0" -chokidar@^2.0.2: - version "2.1.6" - resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-2.1.6.tgz#b6cad653a929e244ce8a834244164d241fa954c5" - integrity sha512-V2jUo67OKkc6ySiRpJrjlpJKl9kDuG+Xb8VgsGzb+aEouhgS1D0weyPU4lEzdAcsCAvrih2J2BqyXqHWvVLw5g== - dependencies: - anymatch "^2.0.0" - async-each "^1.0.1" - braces "^2.3.2" - glob-parent "^3.1.0" - inherits "^2.0.3" - is-binary-path "^1.0.0" - is-glob "^4.0.0" - normalize-path "^3.0.0" - path-is-absolute "^1.0.0" - readdirp "^2.2.1" - upath "^1.1.1" +chokidar@3.3.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.3.0.tgz#12c0714668c55800f659e262d4962a97faf554a6" + integrity sha512-dGmKLDdT3Gdl7fBUe8XK+gAtGmzy5Fn0XkkWQuYxGIgWVPPse2CxFA5mtrlD0TOHaHjEUqkWNyP1XdHoJES/4A== + dependencies: + anymatch "~3.1.1" + braces "~3.0.2" + glob-parent "~5.1.0" + is-binary-path "~2.1.0" + is-glob "~4.0.1" + normalize-path "~3.0.0" + readdirp "~3.2.0" optionalDependencies: - fsevents "^1.2.7" + fsevents "~2.1.1" chokidar@^2.1.8: version "2.1.8" @@ -2696,15 +2794,30 @@ chokidar@^2.1.8: optionalDependencies: fsevents "^1.2.7" -chownr@^1.1.1: - version "1.1.2" - resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.1.2.tgz#a18f1e0b269c8a6a5d3c86eb298beb14c3dd7bf6" - integrity sha512-GkfeAQh+QNy3wquu9oIZr6SS5x7wGdSgNQvD10X3r+AZr1Oys22HW8kAmDMvNg2+Dm0TeGaEuO8gFwdBXxwO8A== +chokidar@^3.2.2: + version "3.4.0" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.4.0.tgz#b30611423ce376357c765b9b8f904b9fba3c0be8" + integrity sha512-aXAaho2VJtisB/1fg1+3nlLJqGOuewTzQpd/Tz0yTg2R0e4IGtshYvtjowyEumcBv2z+y4+kc75Mz7j5xJskcQ== + dependencies: + anymatch "~3.1.1" + braces "~3.0.2" + glob-parent "~5.1.0" + is-binary-path "~2.1.0" + is-glob "~4.0.1" + normalize-path "~3.0.0" + readdirp "~3.4.0" + optionalDependencies: + fsevents "~2.1.2" -chownr@^1.1.2: - version "1.1.3" - resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.1.3.tgz#42d837d5239688d55f303003a508230fa6727142" - integrity sha512-i70fVHhmV3DtTl6nqvZOnIjbY0Pe4kAUjwHj8z0zAdgBtYrJyYwLKCCuRBQ5ppkyL0AkN7HKRnETdmdp1zqNXw== +chownr@^1.1.1, chownr@^1.1.2: + version "1.1.4" + resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.1.4.tgz#6fc9d7b42d32a583596337666e7d08084da2cc6b" + integrity sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg== + +chownr@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/chownr/-/chownr-2.0.0.tgz#15bfbe53d2eab4cf70f18a8cd68ebe5b3cb1dece" + integrity sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ== chrome-trace-event@^1.0.2: version "1.0.2" @@ -2713,11 +2826,6 @@ chrome-trace-event@^1.0.2: dependencies: tslib "^1.9.0" -ci-info@^1.5.0: - version "1.6.0" - resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-1.6.0.tgz#2ca20dbb9ceb32d4524a683303313f0304b1e497" - integrity sha512-vsGdkwSCDpWmP80ncATX7iea5DWQemg1UgCW5J8tqjU3lYw4FBYuj89J0CTVomA7BEfvSZd84GmHko+MxFQU2A== - ci-info@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-2.0.0.tgz#67a9e964be31a51e15e5010d58e6f12834002f46" @@ -2731,10 +2839,10 @@ cipher-base@^1.0.0, cipher-base@^1.0.1, cipher-base@^1.0.3: inherits "^2.0.1" safe-buffer "^5.0.1" -circle-to-polygon@1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/circle-to-polygon/-/circle-to-polygon-1.0.2.tgz#48e9079e6e96cf34d183ff314039de6a59f60a60" - integrity sha512-JuGRbis6++Emc4IAKzQjhq/ltRQ2F1mn1YsQaabYhHeWx8nSRBOevMc/Tf4W7+WFOLl8GbTvcX6+k2p9swxWVg== +circle-to-polygon@2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/circle-to-polygon/-/circle-to-polygon-2.0.2.tgz#e4a8fa50ad89227e6bc1cee5ead8851369290540" + integrity sha512-k0DBIkH5a5EUgcEjWtJm9INOiTioQrW4uec5UCeFwlofD/+lvaN8rMk/67UJ6K4VcLbkBFw8DZGV0dzsH2CElQ== class-utils@^0.3.5: version "0.3.6" @@ -2746,17 +2854,15 @@ class-utils@^0.3.5: isobject "^3.0.0" static-extend "^0.1.1" -cli-boxes@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/cli-boxes/-/cli-boxes-1.0.0.tgz#4fa917c3e59c94a004cd61f8ee509da651687143" - integrity sha1-T6kXw+WclKAEzWH47lCdplFocUM= +clean-stack@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/clean-stack/-/clean-stack-2.2.0.tgz#ee8472dbb129e727b31e8a10a427dee9dfe4008b" + integrity sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A== -cli-cursor@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-1.0.2.tgz#64da3f7d56a54412e59794bd62dc35295e8f2987" - integrity sha1-ZNo/fValRBLll5S9Ytw1KV6PKYc= - dependencies: - restore-cursor "^1.0.1" +cli-boxes@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/cli-boxes/-/cli-boxes-2.2.0.tgz#538ecae8f9c6ca508e3c3c95b453fe93cb4c168d" + integrity sha512-gpaBrMAizVEANOpfZp/EEUixTXDyGt7DFzdK5hU+UbWt/J0lB0w20ncZj59Z9a93xHb9u12zF5BS6i9RKbtg4w== cli-cursor@^2.1.0: version "2.1.0" @@ -2765,23 +2871,29 @@ cli-cursor@^2.1.0: dependencies: restore-cursor "^2.0.0" -cli-spinners@^0.1.2: - version "0.1.2" - resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-0.1.2.tgz#bb764d88e185fb9e1e6a2a1f19772318f605e31c" - integrity sha1-u3ZNiOGF+54eaiofGXcjGPYF4xw= +cli-cursor@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-3.1.0.tgz#264305a7ae490d1d03bf0c9ba7c925d1753af307" + integrity sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw== + dependencies: + restore-cursor "^3.1.0" -cli-truncate@^0.2.1: - version "0.2.1" - resolved "https://registry.yarnpkg.com/cli-truncate/-/cli-truncate-0.2.1.tgz#9f15cfbb0705005369216c626ac7d05ab90dd574" - integrity sha1-nxXPuwcFAFNpIWxiasfQWrkN1XQ= +cli-highlight@^2.0.0: + version "2.1.4" + resolved "https://registry.yarnpkg.com/cli-highlight/-/cli-highlight-2.1.4.tgz#098cb642cf17f42adc1c1145e07f960ec4d7522b" + integrity sha512-s7Zofobm20qriqDoU9sXptQx0t2R9PEgac92mENNm7xaEe1hn71IIMsXMK+6encA6WRCWWxIGQbipr3q998tlQ== dependencies: - slice-ansi "0.0.4" - string-width "^1.0.1" + chalk "^3.0.0" + highlight.js "^9.6.0" + mz "^2.4.0" + parse5 "^5.1.1" + parse5-htmlparser2-tree-adapter "^5.1.1" + yargs "^15.0.0" cli-width@^2.0.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-2.2.0.tgz#ff19ede8a9a5e579324147b0c11f0fbcbabed639" - integrity sha1-/xnt6Kml5XkyQUewwR8PvLq+1jk= + version "2.2.1" + resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-2.2.1.tgz#b0433d0b4e9c847ef18868a4ef16fd5fc8271c48" + integrity sha512-GRMWDxpOB6Dgk2E5Uo+3eEBvtOOlimMmpbFiKuLFnQzYDavtLFY3K5ona41jgN/WdRZtG7utuVSVTL4HbZHGkw== cliui@^5.0.0: version "5.0.0" @@ -2792,22 +2904,36 @@ cliui@^5.0.0: strip-ansi "^5.2.0" wrap-ansi "^5.1.0" +cliui@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/cliui/-/cliui-6.0.0.tgz#511d702c0c4e41ca156d7d0e96021f23e13225b1" + integrity sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ== + dependencies: + string-width "^4.2.0" + strip-ansi "^6.0.0" + wrap-ansi "^6.2.0" + +clone-deep@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/clone-deep/-/clone-deep-4.0.1.tgz#c19fd9bdbbf85942b4fd979c84dcf7d5f07c2387" + integrity sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ== + dependencies: + is-plain-object "^2.0.4" + kind-of "^6.0.2" + shallow-clone "^3.0.0" + +clone-response@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/clone-response/-/clone-response-1.0.2.tgz#d1dc973920314df67fbeb94223b4ee350239e96b" + integrity sha1-0dyXOSAxTfZ/vrlCI7TuNQI56Ws= + dependencies: + mimic-response "^1.0.0" + clone@^1.0.2: version "1.0.4" resolved "https://registry.yarnpkg.com/clone/-/clone-1.0.4.tgz#da309cc263df15994c688ca902179ca3c7cd7c7e" integrity sha1-2jCcwmPfFZlMaIypAheco8fNfH4= -cloudevents-sdk@0.3.2: - version "0.3.2" - resolved "https://registry.yarnpkg.com/cloudevents-sdk/-/cloudevents-sdk-0.3.2.tgz#6c518d5b4c78732b1a8ecb16cb0b55fedb72b252" - integrity sha512-w5IWOGAeP6ixcgv+SpgQ7OiKiaQTnb6obuz4L8W38Bo/Qf8XBCtzcCaGiw8QAXGeAVlkPoGXQ3shyRdn0OgKmg== - dependencies: - ajv "^6.7.0" - axios "0.18.1" - is-empty "1.2.0" - uri-js "4.2.2" - uuid "3.3.2" - code-point-at@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77" @@ -2821,18 +2947,46 @@ collection-visit@^1.0.0: map-visit "^1.0.0" object-visit "^1.0.0" -color-convert@^1.9.0: +color-convert@^1.9.0, color-convert@^1.9.1: version "1.9.3" resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg== dependencies: color-name "1.1.3" +color-convert@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" + integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== + dependencies: + color-name "~1.1.4" + color-name@1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" integrity sha1-p9BVi9icQveV3UIyj3QIMcpTvCU= +color-name@^1.0.0, color-name@~1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" + integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== + +color-string@^1.5.2: + version "1.5.3" + resolved "https://registry.yarnpkg.com/color-string/-/color-string-1.5.3.tgz#c9bbc5f01b58b5492f3d6857459cb6590ce204cc" + integrity sha512-dC2C5qeWoYkxki5UAXapdjqO672AM4vZuPGRQfO8b5HKuKGBbKWpITyDYN7TOFKvRW7kOgAn3746clDBMDJyQw== + dependencies: + color-name "^1.0.0" + simple-swizzle "^0.2.2" + +color@^3.1.2: + version "3.1.2" + resolved "https://registry.yarnpkg.com/color/-/color-3.1.2.tgz#68148e7f85d41ad7649c5fa8c8106f098d229e10" + integrity sha512-vXTJhHebByxZn3lDvDJYw4lR5+uB3vuoHsuYA5AKuxRVn5wzzIfQKGLBmgdVRHKTJYeK5rvJcHnrd0Li49CFpg== + dependencies: + color-convert "^1.9.1" + color-string "^1.5.2" + columnify@^1.5.4: version "1.5.4" resolved "https://registry.yarnpkg.com/columnify/-/columnify-1.5.4.tgz#4737ddf1c7b69a8a7c340570782e947eec8e78bb" @@ -2848,20 +3002,10 @@ combined-stream@^1.0.6, combined-stream@~1.0.6: dependencies: delayed-stream "~1.0.0" -commander@2.15.1: - version "2.15.1" - resolved "https://registry.yarnpkg.com/commander/-/commander-2.15.1.tgz#df46e867d0fc2aec66a34662b406a9ccafff5b0f" - integrity sha512-VlfT9F3V0v+jr4yxPc5gg9s62/fIVWsd2Bk2iD435um1NlGMYdVCq+MjcXnhYq2icNOizHr1kK+5TI6H0Hy0ag== - -commander@^2.18.0, commander@^2.20.0, commander@~2.20.0: - version "2.20.0" - resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.0.tgz#d58bb2b5c1ee8f87b0d340027e9e94e222c5a422" - integrity sha512-7j2y+40w61zy6YC2iRNpUe/NwhNyoXrYpHMrSunaMG64nRnaf96zO/KMQR4OyN/UnE5KLyEBnKHd4aG3rskjpQ== - -common-tags@1.8.0: - version "1.8.0" - resolved "https://registry.yarnpkg.com/common-tags/-/common-tags-1.8.0.tgz#8e3153e542d4a39e9b10554434afaaf98956a937" - integrity sha512-6P6g0uetGpW/sdyUy/iQQCbFF0kWVMSIVSyYz7Zgjcgh8mgw8PQzDNZeyZ5DQ2gM7LBoZPHmnjz8rUthkBG5tw== +commander@^2.20.0, commander@~2.20.3: + version "2.20.3" + resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" + integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== commondir@^1.0.1: version "1.0.1" @@ -2876,6 +3020,11 @@ compare-func@^1.3.1: array-ify "^1.0.0" dot-prop "^3.0.0" +compare-versions@^3.6.0: + version "3.6.0" + resolved "https://registry.yarnpkg.com/compare-versions/-/compare-versions-3.6.0.tgz#1a5689913685e5a87637b8d3ffca75514ec41d62" + integrity sha512-W6Af2Iw1z4CB7q4uU4hv646dW9GQuBM+YpC0UvUCWSD8w90SJjp+ujJuXaEMtAXBtSqGfMPuFOVn4/+FlaqfBA== + component-emitter@^1.2.0, component-emitter@^1.2.1: version "1.3.0" resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.3.0.tgz#16e4070fba8ae29b679f2215853ee181ab2eabc0" @@ -2886,7 +3035,7 @@ concat-map@0.0.1: resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= -concat-stream@1.6.2, concat-stream@^1.5.0: +concat-stream@^1.5.0, concat-stream@^1.5.2: version "1.6.2" resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.2.tgz#904bdf194cd3122fc675c77fc4ac3d4ff0fd1a34" integrity sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw== @@ -2914,29 +3063,27 @@ config-chain@^1.1.11: ini "^1.3.4" proto-list "~1.2.1" -configstore@^3.0.0: - version "3.1.2" - resolved "https://registry.yarnpkg.com/configstore/-/configstore-3.1.2.tgz#c6f25defaeef26df12dd33414b001fe81a543f8f" - integrity sha512-vtv5HtGjcYUgFrXc6Kx747B83MRRVS5R1VTEQoXvuP+kMI+if6uywV0nDGoiydJRy4yk7h9od5Og0kxx4zUXmw== +configstore@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/configstore/-/configstore-5.0.1.tgz#d365021b5df4b98cdd187d6a3b0e3f6a7cc5ed96" + integrity sha512-aMKprgk5YhBNyH25hj8wGt2+D52Sw1DRRIzqBwLp2Ya9mFmY8KPvvtvmna8SxVR9JMZ4kzMD68N22vlaRpkeFA== dependencies: - dot-prop "^4.1.0" + dot-prop "^5.2.0" graceful-fs "^4.1.2" - make-dir "^1.0.0" - unique-string "^1.0.0" - write-file-atomic "^2.0.0" - xdg-basedir "^3.0.0" + make-dir "^3.0.0" + unique-string "^2.0.0" + write-file-atomic "^3.0.0" + xdg-basedir "^4.0.0" -confusing-browser-globals@^1.0.7: - version "1.0.8" - resolved "https://registry.yarnpkg.com/confusing-browser-globals/-/confusing-browser-globals-1.0.8.tgz#93ffec1f82a6e2bf2bc36769cc3a92fa20e502f3" - integrity sha512-lI7asCibVJ6Qd3FGU7mu4sfG4try4LX3+GVS+Gv8UlrEf2AeW57piecapnog2UHZSbcX/P/1UDWVaTsblowlZg== +confusing-browser-globals@^1.0.9: + version "1.0.9" + resolved "https://registry.yarnpkg.com/confusing-browser-globals/-/confusing-browser-globals-1.0.9.tgz#72bc13b483c0276801681871d4898516f8f54fdd" + integrity sha512-KbS1Y0jMtyPgIxjO7ZzMAuUpAKMt1SzCL9fsrKsX6b0zJPTaT0SiSPmewwVZg9UAO83HVIlEhZF84LIjZ0lmAw== console-browserify@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/console-browserify/-/console-browserify-1.1.0.tgz#f0241c45730a9fc6323b206dbf38edc741d0bb10" - integrity sha1-8CQcRXMKn8YyOyBtvzjtx0HQuxA= - dependencies: - date-now "^0.1.4" + version "1.2.0" + resolved "https://registry.yarnpkg.com/console-browserify/-/console-browserify-1.2.0.tgz#67063cef57ceb6cf4993a2ab3a55840ae8c49336" + integrity sha512-ZMkYO/LkF17QvCPqM0gxw8yUzigAOZOSWSHg91FH6orS7vcEj5dVZTidN2fQ14yBSdg97RqhSNwLUXInd52OTA== console-control-strings@^1.0.0, console-control-strings@~1.1.0: version "1.1.0" @@ -2966,9 +3113,9 @@ content-type@~1.0.4: integrity sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA== conventional-changelog-angular@^5.0.3: - version "5.0.3" - resolved "https://registry.yarnpkg.com/conventional-changelog-angular/-/conventional-changelog-angular-5.0.3.tgz#299fdd43df5a1f095283ac16aeedfb0a682ecab0" - integrity sha512-YD1xzH7r9yXQte/HF9JBuEDfvjxxwDGGwZU1+ndanbY0oFgA+Po1T9JDSpPLdP0pZT6MhCAsdvFKC4TJ4MTJTA== + version "5.0.10" + resolved "https://registry.yarnpkg.com/conventional-changelog-angular/-/conventional-changelog-angular-5.0.10.tgz#5cf7b00dd315b6a6a558223c80d5ef24ddb34205" + integrity sha512-k7RPPRs0vp8+BtPsM9uDxRl6KcgqtCJmzRD1wRtgqmhQ96g8ifBGo9O/TZBG23jqlXS/rg8BKRDELxfnQQGiaA== dependencies: compare-func "^1.3.1" q "^1.5.1" @@ -2993,43 +3140,43 @@ conventional-changelog-core@^3.1.6: through2 "^3.0.0" conventional-changelog-preset-loader@^2.1.1: - version "2.2.0" - resolved "https://registry.yarnpkg.com/conventional-changelog-preset-loader/-/conventional-changelog-preset-loader-2.2.0.tgz#571e2b3d7b53d65587bea9eedf6e37faa5db4fcc" - integrity sha512-zXB+5vF7D5Y3Cb/rJfSyCCvFphCVmF8mFqOdncX3BmjZwAtGAPfYrBcT225udilCKvBbHgyzgxqz2GWDB5xShQ== + version "2.3.4" + resolved "https://registry.yarnpkg.com/conventional-changelog-preset-loader/-/conventional-changelog-preset-loader-2.3.4.tgz#14a855abbffd59027fd602581f1f34d9862ea44c" + integrity sha512-GEKRWkrSAZeTq5+YjUZOYxdHq+ci4dNwHvpaBC3+ENalzFWuCWa9EZXSuZBpkr72sMdKB+1fyDV4takK1Lf58g== conventional-changelog-writer@^4.0.6: - version "4.0.7" - resolved "https://registry.yarnpkg.com/conventional-changelog-writer/-/conventional-changelog-writer-4.0.7.tgz#e4b7d9cbea902394ad671f67108a71fa90c7095f" - integrity sha512-p/wzs9eYaxhFbrmX/mCJNwJuvvHR+j4Fd0SQa2xyAhYed6KBiZ780LvoqUUvsayP4R1DtC27czalGUhKV2oabw== + version "4.0.16" + resolved "https://registry.yarnpkg.com/conventional-changelog-writer/-/conventional-changelog-writer-4.0.16.tgz#ca10f2691a8ea6d3c2eb74bd35bcf40aa052dda5" + integrity sha512-jmU1sDJDZpm/dkuFxBeRXvyNcJQeKhGtVcFFkwTphUAzyYWcwz2j36Wcv+Mv2hU3tpvLMkysOPXJTLO55AUrYQ== dependencies: compare-func "^1.3.1" - conventional-commits-filter "^2.0.2" + conventional-commits-filter "^2.0.6" dateformat "^3.0.0" - handlebars "^4.1.2" + handlebars "^4.7.6" json-stringify-safe "^5.0.1" - lodash "^4.2.1" - meow "^4.0.0" + lodash "^4.17.15" + meow "^7.0.0" semver "^6.0.0" split "^1.0.0" through2 "^3.0.0" -conventional-commits-filter@^2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/conventional-commits-filter/-/conventional-commits-filter-2.0.2.tgz#f122f89fbcd5bb81e2af2fcac0254d062d1039c1" - integrity sha512-WpGKsMeXfs21m1zIw4s9H5sys2+9JccTzpN6toXtxhpw2VNF2JUXwIakthKBy+LN4DvJm+TzWhxOMWOs1OFCFQ== +conventional-commits-filter@^2.0.2, conventional-commits-filter@^2.0.6: + version "2.0.6" + resolved "https://registry.yarnpkg.com/conventional-commits-filter/-/conventional-commits-filter-2.0.6.tgz#0935e1240c5ca7698329affee1b6a46d33324c4c" + integrity sha512-4g+sw8+KA50/Qwzfr0hL5k5NWxqtrOVw4DDk3/h6L85a9Gz0/Eqp3oP+CWCNfesBvZZZEFHF7OTEbRe+yYSyKw== dependencies: lodash.ismatch "^4.4.0" modify-values "^1.0.0" conventional-commits-parser@^3.0.3: - version "3.0.5" - resolved "https://registry.yarnpkg.com/conventional-commits-parser/-/conventional-commits-parser-3.0.5.tgz#df471d6cb3f6fecfd1356ac72e0b577dbdae0a9c" - integrity sha512-qVz9+5JwdJzsbt7JbJ6P7NOXBGt8CyLFJYSjKAuPSgO+5UGfcsbk9EMR+lI8Unlvx6qwIc2YDJlrGIfay2ehNA== + version "3.1.0" + resolved "https://registry.yarnpkg.com/conventional-commits-parser/-/conventional-commits-parser-3.1.0.tgz#10140673d5e7ef5572633791456c5d03b69e8be4" + integrity sha512-RSo5S0WIwXZiRxUGTPuYFbqvrR4vpJ1BDdTlthFgvHt5kEdnd1+pdvwWphWn57/oIl4V72NMmOocFqqJ8mFFhA== dependencies: JSONStream "^1.0.4" - is-text-path "^2.0.0" - lodash "^4.2.1" - meow "^4.0.0" + is-text-path "^1.0.1" + lodash "^4.17.15" + meow "^7.0.0" split2 "^2.0.0" through2 "^3.0.0" trim-off-newlines "^1.0.0" @@ -3048,10 +3195,10 @@ conventional-recommended-bump@^5.0.0: meow "^4.0.0" q "^1.5.1" -convert-source-map@^1.6.0: - version "1.6.0" - resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.6.0.tgz#51b537a8c43e0f04dec1993bffcdd504e758ac20" - integrity sha512-eFu7XigvxdZ1ETfbgPBohgyQ/Z++C0eEhTor0qRwBw9unw+L0/6V8wkSuGgzdThkiS5lSpdptOQPD8Ak40a+7A== +convert-source-map@^1.7.0: + version "1.7.0" + resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.7.0.tgz#17a2cb882d7f77d3490585e2ce6c524424a3a442" + integrity sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA== dependencies: safe-buffer "~5.1.1" @@ -3087,6 +3234,11 @@ copy-descriptor@^0.1.0: resolved "https://registry.yarnpkg.com/copy-descriptor/-/copy-descriptor-0.1.1.tgz#676f6eb3c39997c2ee1ac3a924fd6124748f578d" integrity sha1-Z29us8OZl8LuGsOpJP1hJHSPV40= +core-js-pure@^3.0.0: + version "3.6.5" + resolved "https://registry.yarnpkg.com/core-js-pure/-/core-js-pure-3.6.5.tgz#c79e75f5e38dbc85a662d91eea52b8256d53b813" + integrity sha512-lacdXOimsiD0QyNf9BC/mxivNJ/ybBGJXQFKzRekp1WTHoVUWsUHEn+2T8GJAzzIhyOuXA+gOxCVN3l+5PLPUA== + core-util-is@1.0.2, core-util-is@~1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" @@ -3100,7 +3252,7 @@ cors@2.8.5: object-assign "^4" vary "^1" -cosmiconfig@^5.1.0, cosmiconfig@^5.2.1: +cosmiconfig@^5.1.0: version "5.2.1" resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-5.2.1.tgz#040f726809c591e77a17c0a3626ca45b4f168b1a" integrity sha512-H65gsXo1SKjf8zmrJ67eJk8aIRKV5ff2D4uKZIBZShbhGSpEmsQOPW/SKMKYhSTrqR7ufy6RP69rPogdaPh/kA== @@ -3110,16 +3262,16 @@ cosmiconfig@^5.1.0, cosmiconfig@^5.2.1: js-yaml "^3.13.1" parse-json "^4.0.0" -cp-file@^6.2.0: - version "6.2.0" - resolved "https://registry.yarnpkg.com/cp-file/-/cp-file-6.2.0.tgz#40d5ea4a1def2a9acdd07ba5c0b0246ef73dc10d" - integrity sha512-fmvV4caBnofhPe8kOcitBwSn2f39QLjnAnGq3gO9dfd75mUytzKNZB1hde6QHunW2Rt+OwuBOMc3i1tNElbszA== +cosmiconfig@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-6.0.0.tgz#da4fee853c52f6b1e6935f41c1a2fc50bd4a9982" + integrity sha512-xb3ZL6+L8b9JLLCx3ZdoZy4+2ECphCMo2PwqgP1tlfVq6M6YReyzBJtvWWtbDSpNr9hn96pkCiZqUcFEc+54Qg== dependencies: - graceful-fs "^4.1.2" - make-dir "^2.0.0" - nested-error-stacks "^2.0.0" - pify "^4.0.1" - safe-buffer "^5.0.1" + "@types/parse-json" "^4.0.0" + import-fresh "^3.1.0" + parse-json "^5.0.0" + path-type "^4.0.0" + yaml "^1.7.2" create-ecdh@^4.0.0: version "4.0.3" @@ -3129,14 +3281,7 @@ create-ecdh@^4.0.0: bn.js "^4.1.0" elliptic "^6.0.0" -create-error-class@^3.0.0: - version "3.0.2" - resolved "https://registry.yarnpkg.com/create-error-class/-/create-error-class-3.0.2.tgz#06be7abef947a3f14a30fd610671d401bca8b7b6" - integrity sha1-Br56vvlHo/FKMP1hBnHUAbyot7Y= - dependencies: - capture-stack-trace "^1.0.0" - -create-hash@^1.1.0, create-hash@^1.1.2: +create-hash@^1.1.0, create-hash@^1.1.2, create-hash@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/create-hash/-/create-hash-1.2.0.tgz#889078af11a63756bcfb59bd221996be3a9ef196" integrity sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg== @@ -3147,7 +3292,7 @@ create-hash@^1.1.0, create-hash@^1.1.2: ripemd160 "^2.0.1" sha.js "^2.4.0" -create-hmac@^1.1.0, create-hmac@^1.1.2, create-hmac@^1.1.4: +create-hmac@^1.1.0, create-hmac@^1.1.4, create-hmac@^1.1.7: version "1.1.7" resolved "https://registry.yarnpkg.com/create-hmac/-/create-hmac-1.1.7.tgz#69170c78b3ab957147b2b8b04572e47ead2243ff" integrity sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg== @@ -3170,22 +3315,14 @@ cross-spawn@6.0.5, cross-spawn@^6.0.0, cross-spawn@^6.0.5: shebang-command "^1.2.0" which "^1.2.9" -cross-spawn@^4: - version "4.0.2" - resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-4.0.2.tgz#7b9247621c23adfdd3856004a823cbe397424d41" - integrity sha1-e5JHYhwjrf3ThWAEqCPL45dCTUE= - dependencies: - lru-cache "^4.0.1" - which "^1.2.9" - -cross-spawn@^5.0.1: - version "5.1.0" - resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-5.1.0.tgz#e8bd0efee58fcff6f8f94510a0a554bbfa235449" - integrity sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk= +cross-spawn@^7.0.0, cross-spawn@^7.0.2: + version "7.0.2" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.2.tgz#d0d7dcfa74e89115c7619f4f721a94e1fdb716d6" + integrity sha512-PD6G8QG3S4FK/XCGFbEQrDqO2AnMMsy0meR7lerlIOHAAbkuavGU/pOqprrlvfTNjvowivTeBsjebAL0NSoMxw== dependencies: - lru-cache "^4.0.1" - shebang-command "^1.2.0" - which "^1.2.9" + path-key "^3.1.0" + shebang-command "^2.0.0" + which "^2.0.1" crypto-browserify@^3.11.0: version "3.12.0" @@ -3204,10 +3341,10 @@ crypto-browserify@^3.11.0: randombytes "^2.0.0" randomfill "^1.0.3" -crypto-random-string@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/crypto-random-string/-/crypto-random-string-1.0.0.tgz#a230f64f568310e1498009940790ec99545bca7e" - integrity sha1-ojD2T1aDEOFJgAmUB5DsmVRbyn4= +crypto-random-string@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/crypto-random-string/-/crypto-random-string-2.0.0.tgz#ef2a7a966ec11083388369baa02ebead229b30d5" + integrity sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA== currently-unhandled@^0.4.1: version "0.4.1" @@ -3216,48 +3353,10 @@ currently-unhandled@^0.4.1: dependencies: array-find-index "^1.0.1" -cyclist@~0.2.2: - version "0.2.2" - resolved "https://registry.yarnpkg.com/cyclist/-/cyclist-0.2.2.tgz#1b33792e11e914a2fd6d6ed6447464444e5fa640" - integrity sha1-GzN5LhHpFKL9bW7WRHRkRE5fpkA= - -cypress@3.5.0: - version "3.5.0" - resolved "https://registry.yarnpkg.com/cypress/-/cypress-3.5.0.tgz#e188bc8f48782953f6865d8830a4dc342334b81c" - integrity sha512-I1iSReD2C8CTP6s4BvQky4gEqHBnKLmhBIqFyCUZdj6BQ6ZDxGnmIbQPM5g79E2iP60KTIbTK99ZPSDVtsNUUg== - dependencies: - "@cypress/listr-verbose-renderer" "0.4.1" - "@cypress/xvfb" "1.2.4" - "@types/sizzle" "2.3.2" - arch "2.1.1" - bluebird "3.5.0" - cachedir "1.3.0" - chalk "2.4.2" - check-more-types "2.24.0" - commander "2.15.1" - common-tags "1.8.0" - debug "3.2.6" - execa "0.10.0" - executable "4.1.1" - extract-zip "1.6.7" - fs-extra "5.0.0" - getos "3.1.1" - is-ci "1.2.1" - is-installed-globally "0.1.0" - lazy-ass "1.6.0" - listr "0.12.0" - lodash "4.17.15" - log-symbols "2.2.0" - minimist "1.2.0" - moment "2.24.0" - ramda "0.24.1" - request "2.88.0" - request-progress "3.0.0" - supports-color "5.5.0" - tmp "0.1.0" - untildify "3.0.3" - url "0.11.0" - yauzl "2.10.0" +cyclist@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/cyclist/-/cyclist-1.0.1.tgz#596e9698fd0c80e12038c2b82d6eb1b35b6224d9" + integrity sha1-WW6WmP0MgOEgOMK4LW6xs1tiJNk= dargs@^4.0.1: version "4.1.0" @@ -3273,22 +3372,12 @@ dashdash@^1.12.0: dependencies: assert-plus "^1.0.0" -date-fns@^1.27.2: - version "1.30.1" - resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-1.30.1.tgz#2e71bf0b119153dbb4cc4e88d9ea5acfb50dc05c" - integrity sha512-hBSVCvSmWC+QypYObzwGOd9wqdDpOt+0wl0KbU+R+uuZBS1jN8VsD1ss3irQDknRj5NvxiTF6oj/nDRnN/UQNw== - -date-now@^0.1.4: - version "0.1.4" - resolved "https://registry.yarnpkg.com/date-now/-/date-now-0.1.4.tgz#eaf439fd4d4848ad74e5cc7dbef200672b9e345b" - integrity sha1-6vQ5/U1ISK105cx9vvIAZyueNFs= - dateformat@^3.0.0, dateformat@^3.0.2: version "3.0.3" resolved "https://registry.yarnpkg.com/dateformat/-/dateformat-3.0.3.tgz#a6e37499a4d9a9cf85ef5872044d62901c9889ae" integrity sha512-jyCETtSl3VMZMWeRo7iY1FL19ges1t55hMo5yaam4Jrsm5EPL89UQkoQRyiI+Yf4k8r2ZpdngkV8hr1lIdjb3Q== -debug@2.6.9, debug@^2.2.0, debug@^2.3.3, debug@^2.6.8, debug@^2.6.9: +debug@2.6.9, debug@^2.2.0, debug@^2.3.3, debug@^2.6.9: version "2.6.9" resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== @@ -3309,7 +3398,7 @@ debug@3.2.6, debug@^3.1.0, debug@^3.2.6: dependencies: ms "^2.1.1" -debug@^4.0.1, debug@^4.1.0, debug@^4.1.1: +debug@4, debug@^4.0.1, debug@^4.1.0, debug@^4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/debug/-/debug-4.1.1.tgz#3b72260255109c6b589cee050f1d516139664791" integrity sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw== @@ -3321,7 +3410,7 @@ debuglog@^1.0.1: resolved "https://registry.yarnpkg.com/debuglog/-/debuglog-1.0.1.tgz#aa24ffb9ac3df9a2351837cfb2d279360cd78492" integrity sha1-qiT/uaw9+aI1GDfPstJ5NgzXhJI= -decamelize-keys@^1.0.0: +decamelize-keys@^1.0.0, decamelize-keys@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/decamelize-keys/-/decamelize-keys-1.1.0.tgz#d171a87933252807eb3cb61dc1c1445d078df2d9" integrity sha1-0XGoeTMlKAfrPLYdwcFEXQeN8tk= @@ -3339,39 +3428,41 @@ decode-uri-component@^0.2.0: resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.0.tgz#eb3913333458775cb84cd1a1fae062106bb87545" integrity sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU= +decompress-response@^3.3.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/decompress-response/-/decompress-response-3.3.0.tgz#80a4dd323748384bfa248083622aedec982adff3" + integrity sha1-gKTdMjdIOEv6JICDYirt7Jgq3/M= + dependencies: + mimic-response "^1.0.0" + +decompress-response@^4.2.0: + version "4.2.1" + resolved "https://registry.yarnpkg.com/decompress-response/-/decompress-response-4.2.1.tgz#414023cc7a302da25ce2ec82d0d5238ccafd8986" + integrity sha512-jOSne2qbyE+/r8G1VU+G/82LBs2Fs4LAsTiLSHOCOMZQl2OKZ6i8i4IyHemTe+/yIXOtTcRQMzPcgyhoFlqPkw== + dependencies: + mimic-response "^2.0.0" + dedent@^0.7.0: version "0.7.0" resolved "https://registry.yarnpkg.com/dedent/-/dedent-0.7.0.tgz#2495ddbaf6eb874abb0e1be9df22d2e5a544326c" integrity sha1-JJXduvbrh0q7Dhvp3yLS5aVEMmw= -deep-eql@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/deep-eql/-/deep-eql-3.0.1.tgz#dfc9404400ad1c8fe023e7da1df1c147c4b444df" - integrity sha512-+QeIQyN5ZuO+3Uk5DYh6/1eKO0m0YmJFGNmFHGACpf1ClL1nmlV/p4gNgbl2pJGxgXb4faqo6UE+M5ACEMyVcw== - dependencies: - type-detect "^4.0.0" - deep-extend@^0.6.0: version "0.6.0" resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.6.0.tgz#c4fa7c95404a17a9c3e8ca7e1537312b736330ac" integrity sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA== -deep-is@~0.1.3: +deep-is@^0.1.3, deep-is@~0.1.3: version "0.1.3" resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34" integrity sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ= -deepmerge@^2.0.1: - version "2.2.1" - resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-2.2.1.tgz#5d3ff22a01c00f645405a2fbc17d0778a1801170" - integrity sha512-R9hc1Xa/NOBi9WRVUWg19rl1UB7Tt4kuPd+thNJgFZoxXsTz7ncaPaeIm+40oSGuP33DfMb4sZt1QIGiJzC4EA== - -default-require-extensions@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/default-require-extensions/-/default-require-extensions-2.0.0.tgz#f5f8fbb18a7d6d50b21f641f649ebb522cfe24f7" - integrity sha1-9fj7sYp9bVCyH2QfZJ67Uiz+JPc= +default-require-extensions@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/default-require-extensions/-/default-require-extensions-3.0.0.tgz#e03f93aac9b2b6443fc52e5e4a37b3ad9ad8df96" + integrity sha512-ek6DpXq/SCpvjhpFsLFRVtIxJCRw6fUR42lYMVZuUMK7n8eMz4Uh5clckdBjEpLhn/gEBZo7hDJnJcwdKLKQjg== dependencies: - strip-bom "^3.0.0" + strip-bom "^4.0.0" defaults@^1.0.3: version "1.0.3" @@ -3380,6 +3471,11 @@ defaults@^1.0.3: dependencies: clone "^1.0.2" +defer-to-connect@^1.0.1: + version "1.1.3" + resolved "https://registry.yarnpkg.com/defer-to-connect/-/defer-to-connect-1.1.3.tgz#331ae050c08dcf789f8c83a7b81f0ed94f4ac591" + integrity sha512-0ISdNousHvZT2EiFlZeZAHBUvSxmKswVCEf8hW7KWgG4a8MVEu/3Vb6uWYozkjylyCxe0JBIiRB1jV45S70WVQ== + define-properties@^1.1.2, define-properties@^1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.3.tgz#cf88da6cbee26fe6db7094f61d870cbd84cee9f1" @@ -3419,20 +3515,30 @@ delegates@^1.0.0: resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a" integrity sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o= +denque@^1.4.1: + version "1.4.1" + resolved "https://registry.yarnpkg.com/denque/-/denque-1.4.1.tgz#6744ff7641c148c3f8a69c307e51235c1f4a37cf" + integrity sha512-OfzPuSZKGcgr96rf1oODnfjqBFmr1DVoc/TrItj3Ohe0Ah1C5WX5Baquw/9U9KovnQ88EqmJbD66rKYUQYN1tQ== + depd@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9" integrity sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak= -deprecation@^2.0.0: +depd@~2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/depd/-/depd-2.0.0.tgz#b696163cc757560d09cf22cc8fad1571b79e76df" + integrity sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw== + +deprecation@^2.0.0, deprecation@^2.3.1: version "2.3.1" resolved "https://registry.yarnpkg.com/deprecation/-/deprecation-2.3.1.tgz#6368cbdb40abf3373b525ac87e4a260c3a700919" integrity sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ== des.js@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/des.js/-/des.js-1.0.0.tgz#c074d2e2aa6a8a9a07dbd61f9a15c2cd83ec8ecc" - integrity sha1-wHTS4qpqipoH29YfmhXCzYPsjsw= + version "1.0.1" + resolved "https://registry.yarnpkg.com/des.js/-/des.js-1.0.1.tgz#5382142e1bdc53f85d86d53e5f4aa7deb91e0843" + integrity sha512-Q0I4pfFrv2VPd34/vfLrFOoRmlYj3OV50i7fskps1jZWK1kApMWWT9G6RRUeYedLcBDIhnSDaUvJMb3AhUlaEA== dependencies: inherits "^2.0.1" minimalistic-assert "^1.0.0" @@ -3452,7 +3558,7 @@ detect-indent@^5.0.0: resolved "https://registry.yarnpkg.com/detect-indent/-/detect-indent-5.0.0.tgz#3871cc0a6a002e8c3e5b3cf7f336264675f06b9d" integrity sha1-OHHMCmoALow+Wzz38zYmRnXwa50= -detect-libc@^1.0.2: +detect-libc@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-1.0.3.tgz#fa137c4bd698edf55cd5cd02ac559f91a4c4ba9b" integrity sha1-+hN8S9aY7fVc1c0CrFWfkaTEups= @@ -3465,15 +3571,23 @@ dezalgo@^1.0.0: asap "^2.0.0" wrappy "1" +dicer@0.2.5: + version "0.2.5" + resolved "https://registry.yarnpkg.com/dicer/-/dicer-0.2.5.tgz#5996c086bb33218c812c090bddc09cd12facb70f" + integrity sha1-WZbAhrszIYyBLAkL3cCc0S+stw8= + dependencies: + readable-stream "1.1.x" + streamsearch "0.1.2" + diff@3.5.0, diff@^3.1.0, diff@^3.5.0: version "3.5.0" resolved "https://registry.yarnpkg.com/diff/-/diff-3.5.0.tgz#800c0dd1e0a8bfbc95835c202ad220fe317e5a12" integrity sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA== -diff@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.1.tgz#0c667cb467ebbb5cea7f14f135cc2dba7780a8ff" - integrity sha512-s2+XdvhPCOF01LRQBC8hf4vhbVmI2CGS5aZnxLJlT5FtdhPCDFq80q++zK2KlrVorVDdL5BOGZ/VfLrVtYNF+Q== +diff@^4.0.1, diff@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d" + integrity sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A== diffie-hellman@^5.0.0: version "5.0.3" @@ -3525,22 +3639,29 @@ dot-prop@^3.0.0: dependencies: is-obj "^1.0.0" -dot-prop@^4.1.0, dot-prop@^4.2.0: +dot-prop@^4.2.0: version "4.2.0" resolved "https://registry.yarnpkg.com/dot-prop/-/dot-prop-4.2.0.tgz#1f19e0c2e1aa0e32797c49799f2837ac6af69c57" integrity sha512-tUMXrxlExSW6U2EXiiKGSBVdYgtV8qlHL+C10TsW4PURY/ic+eaysnSkwB4kA/mBlCyy/IKDJ+Lc3wbWeaXtuQ== dependencies: is-obj "^1.0.0" +dot-prop@^5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/dot-prop/-/dot-prop-5.2.0.tgz#c34ecc29556dc45f1f4c22697b6f4904e0cc4fcb" + integrity sha512-uEUyaDKoSQ1M4Oq8l45hSE26SnTxL6snNnqvK/VWx5wJhmff5z0FUVJDKDanor/6w3kzE3i7XZOk+7wC0EXr1A== + dependencies: + is-obj "^2.0.0" + dotenv@8.2.0: version "8.2.0" resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-8.2.0.tgz#97e619259ada750eea3e4ea3e26bceea5424b16a" integrity sha512-8sJ78ElpbDJBHNeBzUbUVLsqKdccaa/BXF1uPTw3GrvQTBgrQrtObr2mUrE38vzYd8cEv+m/JBfDLioYcfXoaw== -double-ended-queue@^2.1.0-0: - version "2.1.0-0" - resolved "https://registry.yarnpkg.com/double-ended-queue/-/double-ended-queue-2.1.0-0.tgz#103d3527fd31528f40188130c841efdd78264e5c" - integrity sha1-ED01J/0xUo9AGIEwyEHv3XgmTlw= +dotenv@^6.2.0: + version "6.2.0" + resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-6.2.0.tgz#941c0410535d942c8becf28d3f357dbd9d476064" + integrity sha512-HygQCKUBSFl8wKQZBSemMywRWcEDNidvNbjGVyZu3nbZ8qq9ubiPoGLMdRDpfSrpkkm9BXYFkpKxxFX38o/76w== duplexer3@^0.1.4: version "0.1.4" @@ -3570,7 +3691,7 @@ ecc-jsbn@~0.1.1: jsbn "~0.1.0" safer-buffer "^2.1.0" -ecdsa-sig-formatter@1.0.11: +ecdsa-sig-formatter@1.0.11, ecdsa-sig-formatter@^1.0.11: version "1.0.11" resolved "https://registry.yarnpkg.com/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz#ae0f0fa2d85045ef14a817daa3ce9acd0489e5bf" integrity sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ== @@ -3589,20 +3710,10 @@ ee-first@1.1.1: dependencies: kindof ">= 2.0.0 < 3" -ejs@^2.6.1: - version "2.6.2" - resolved "https://registry.yarnpkg.com/ejs/-/ejs-2.6.2.tgz#3a32c63d1cd16d11266cd4703b14fec4e74ab4f6" - integrity sha512-PcW2a0tyTuPHz3tWyYqtK6r1fZ3gp+3Sop8Ph+ZYN81Ob5rwmbHEzaqs10N3BEsaGTkh/ooniXK+WwszGlc2+Q== - -elegant-spinner@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/elegant-spinner/-/elegant-spinner-1.0.1.tgz#db043521c95d7e303fd8f345bedc3349cfb0729e" - integrity sha1-2wQ1IcldfjA/2PNFvtwzSc+wcp4= - -elliptic@^6.0.0: - version "6.5.0" - resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.5.0.tgz#2b8ed4c891b7de3200e14412a5b8248c7af505ca" - integrity sha512-eFOJTMyCYb7xtE/caJ6JJu+bhi67WCYNbkGSknu20pmM8Ke/bqOfdnZWxyoGN26JgfxTbXrsCkEw4KheCT/KGg== +elliptic@^6.0.0, elliptic@^6.5.2: + version "6.5.2" + resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.5.2.tgz#05c5678d7173c049d8ca433552224a495d0e3762" + integrity sha512-f4x70okzZbIQl/NSRLkI/+tteV/9WqL98zx+SQ69KbXxmVrmjwsNUPn/gYJJ0sHvEak24cZgHIPegRePAtA/xw== dependencies: bn.js "^4.4.0" brorand "^1.0.1" @@ -3617,11 +3728,21 @@ emoji-regex@^7.0.1: resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-7.0.3.tgz#933a04052860c85e83c122479c4748a8e4c72156" integrity sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA== +emoji-regex@^8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" + integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== + emojis-list@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/emojis-list/-/emojis-list-2.1.0.tgz#4daa4d9db00f9819880c79fa457ae5b09a1fd389" integrity sha1-TapNnbAPmBmIDHn6RXrlsJof04k= +emojis-list@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/emojis-list/-/emojis-list-3.0.0.tgz#5570662046ad29e2e916e71aae260abdff4f6a78" + integrity sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q== + encodeurl@~1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59" @@ -3634,14 +3755,14 @@ encoding@^0.1.11: dependencies: iconv-lite "~0.4.13" -end-of-stream@^1.0.0, end-of-stream@^1.1.0: - version "1.4.1" - resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.1.tgz#ed29634d19baba463b6ce6b80a37213eab71ec43" - integrity sha512-1MkrZNvWTKCaigbn+W15elq2BB/L22nqrSY5DKlo3X6+vclJm8Bb5djXJBmEX6fS3+zCh/F4VBK5Z2KxJt4s2Q== +end-of-stream@^1.0.0, end-of-stream@^1.1.0, end-of-stream@^1.4.1: + version "1.4.4" + resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0" + integrity sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q== dependencies: once "^1.4.0" -enhanced-resolve@4.1.0, enhanced-resolve@^4.0.0, enhanced-resolve@^4.1.0: +enhanced-resolve@4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-4.1.0.tgz#41c7e0bfdfe74ac1ffe1e57ad6a5c6c9f3742a7f" integrity sha512-F/7vkyTtyc/llOIn8oWclcB25KdRaiPBpZYDgJHgh/UHtpgT2p2eldQgtQnLtUvfMKPKxbRaQM/hHkvLHt1Vng== @@ -3650,10 +3771,24 @@ enhanced-resolve@4.1.0, enhanced-resolve@^4.0.0, enhanced-resolve@^4.1.0: memory-fs "^0.4.0" tapable "^1.0.0" -env-paths@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/env-paths/-/env-paths-1.0.0.tgz#4168133b42bb05c38a35b1ae4397c8298ab369e0" - integrity sha1-QWgTO0K7BcOKNbGuQ5fIKYqzaeA= +enhanced-resolve@^4.0.0, enhanced-resolve@^4.1.0: + version "4.1.1" + resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-4.1.1.tgz#2937e2b8066cd0fe7ce0990a98f0d71a35189f66" + integrity sha512-98p2zE+rL7/g/DzMHMTF4zZlCgeVdJ7yr6xzEpJRYwFYrGi9ANdn5DnJURg6RpBkyk60XYDnWIv51VfIhfNGuA== + dependencies: + graceful-fs "^4.1.2" + memory-fs "^0.5.0" + tapable "^1.0.0" + +env-paths@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/env-paths/-/env-paths-2.2.0.tgz#cdca557dc009152917d6166e2febe1f039685e43" + integrity sha512-6u0VYSCo/OW6IoD5WCLLy9JUGARbamfSavcNXry/eu8aHVFei6CD3Sw+VGX5alea1i9pgPHW0mbu6Xj0uBh7gA== + +envinfo@^7.3.1: + version "7.5.1" + resolved "https://registry.yarnpkg.com/envinfo/-/envinfo-7.5.1.tgz#93c26897225a00457c75e734d354ea9106a72236" + integrity sha512-hQBkDf2iO4Nv0CNHpCuSBeaSrveU6nThVxFGTrq/eDlV716UQk09zChaJae4mZRsos1x4YLY2TaH3LHUae3ZmQ== err-code@^1.0.0: version "1.1.2" @@ -3674,22 +3809,27 @@ error-ex@^1.2.0, error-ex@^1.3.1: dependencies: is-arrayish "^0.2.1" -es-abstract@^1.11.0, es-abstract@^1.12.0, es-abstract@^1.5.1, es-abstract@^1.7.0: - version "1.13.0" - resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.13.0.tgz#ac86145fdd5099d8dd49558ccba2eaf9b88e24e9" - integrity sha512-vDZfg/ykNxQVwup/8E1BZhVzFfBxs9NqMzGcvIJrqg5k2/5Za2bWo40dK2J1pgLngZ7c+Shh8lwYtLGyrwPutg== +es-abstract@^1.17.0, es-abstract@^1.17.0-next.1, es-abstract@^1.17.5: + version "1.17.5" + resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.17.5.tgz#d8c9d1d66c8981fb9200e2251d799eee92774ae9" + integrity sha512-BR9auzDbySxOcfog0tLECW8l28eRGpDpU3Dm3Hp4q/N+VtLTmyj4EUN088XZWQDW/hzj6sYRDXeOFsaAODKvpg== dependencies: - es-to-primitive "^1.2.0" + es-to-primitive "^1.2.1" function-bind "^1.1.1" has "^1.0.3" - is-callable "^1.1.4" - is-regex "^1.0.4" - object-keys "^1.0.12" + has-symbols "^1.0.1" + is-callable "^1.1.5" + is-regex "^1.0.5" + object-inspect "^1.7.0" + object-keys "^1.1.1" + object.assign "^4.1.0" + string.prototype.trimleft "^2.1.1" + string.prototype.trimright "^2.1.1" -es-to-primitive@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.2.0.tgz#edf72478033456e8dda8ef09e00ad9650707f377" - integrity sha512-qZryBOJjV//LaxLTV6UC//WewneB3LcXOL9NP++ozKVXsIIIpm/2c13UDiD9Jp2eThsecw9m3jPqDwTyobcdbg== +es-to-primitive@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.2.1.tgz#e55cd4c9cdc188bcefb03b366c736323fc5c898a" + integrity sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA== dependencies: is-callable "^1.1.4" is-date-object "^1.0.1" @@ -3712,7 +3852,12 @@ es6-promisify@^5.0.0: dependencies: es6-promise "^4.0.3" -escape-html@~1.0.3: +escape-goat@^2.0.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/escape-goat/-/escape-goat-2.1.1.tgz#1b2dc77003676c457ec760b2dc68edb648188675" + integrity sha512-8/uIhbG12Csjy2JEW7D9pHbreaVaS/OpN3ycnyvElTdwM5n6GY6W6e2IPemfvGZeUMqZ9A/3GqIZMgKnBhAw/Q== + +escape-html@^1.0.3, escape-html@~1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" integrity sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg= @@ -3722,14 +3867,14 @@ escape-string-regexp@1.0.5, escape-string-regexp@^1.0.2, escape-string-regexp@^1 resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ= -eslint-config-airbnb-base@14.0.0: - version "14.0.0" - resolved "https://registry.yarnpkg.com/eslint-config-airbnb-base/-/eslint-config-airbnb-base-14.0.0.tgz#8a7bcb9643d13c55df4dd7444f138bf4efa61e17" - integrity sha512-2IDHobw97upExLmsebhtfoD3NAKhV4H0CJWP3Uprd/uk+cHuWYOczPVxQ8PxLFUAw7o3Th1RAU8u1DoUpr+cMA== +eslint-config-airbnb-base@14.1.0: + version "14.1.0" + resolved "https://registry.yarnpkg.com/eslint-config-airbnb-base/-/eslint-config-airbnb-base-14.1.0.tgz#2ba4592dd6843258221d9bff2b6831bd77c874e4" + integrity sha512-+XCcfGyCnbzOnktDVhwsCAx+9DmrzEmuwxyHUJpw+kqBVT744OUBrB09khgFKlK1lshVww6qXGsYPZpavoNjJw== dependencies: - confusing-browser-globals "^1.0.7" + confusing-browser-globals "^1.0.9" object.assign "^4.1.0" - object.entries "^1.1.0" + object.entries "^1.1.1" eslint-config-esnext@^4.0.0: version "4.0.0" @@ -3749,10 +3894,10 @@ eslint-config-node@^4.0.0: eslint "^5.6.0" eslint-config-esnext "^4.0.0" -eslint-config-prettier@6.4.0: - version "6.4.0" - resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-6.4.0.tgz#0a04f147e31d33c6c161b2dd0971418ac52d0477" - integrity sha512-YrKucoFdc7SEko5Sxe4r6ixqXPDP1tunGw91POeZTTRKItf/AMFYt/YLEQtZMkR2LVpAVhcAcZgcWpm1oGPW7w== +eslint-config-prettier@6.11.0: + version "6.11.0" + resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-6.11.0.tgz#f6d2238c1290d01c859a8b5c1f7d352a0b0da8b1" + integrity sha512-oB8cpLWSAjOVFEJhhyMZh6NOEOtBVziaqdDQ86+qhDHFbZXoRTM7pNSvFRfW/W/L/LrQ38C99J5CGuRBBzBsdA== dependencies: get-stdin "^6.0.0" @@ -3777,19 +3922,19 @@ eslint-config-recommended@4.0.0: eslint-config-react-native "^4.0.0" eslint-import-resolver-node@^0.3.2: - version "0.3.2" - resolved "https://registry.yarnpkg.com/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.2.tgz#58f15fb839b8d0576ca980413476aab2472db66a" - integrity sha512-sfmTqJfPSizWu4aymbPr4Iidp5yKm8yDkHp+Ir3YiTHiiDfxh69mOUsmiqW6RZ9zRXFaF64GtYmN7e+8GHBv6Q== + version "0.3.3" + resolved "https://registry.yarnpkg.com/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.3.tgz#dbaa52b6b2816b50bc6711af75422de808e98404" + integrity sha512-b8crLDo0M5RSe5YG8Pu2DYBj71tSB6OvXkfzwbJU2w7y8P4/yo0MyF8jU26IEuEuHF2K5/gcAJE3LhQGqBBbVg== dependencies: debug "^2.6.9" - resolve "^1.5.0" + resolve "^1.13.1" -eslint-module-utils@^2.4.0: - version "2.4.1" - resolved "https://registry.yarnpkg.com/eslint-module-utils/-/eslint-module-utils-2.4.1.tgz#7b4675875bf96b0dbf1b21977456e5bb1f5e018c" - integrity sha512-H6DOj+ejw7Tesdgbfs4jeS4YMFrT8uI8xwd1gtQqXssaR0EQ26L+2O/w6wkYFy2MymON0fTwHmXBvvfLNZVZEw== +eslint-module-utils@^2.4.1: + version "2.6.0" + resolved "https://registry.yarnpkg.com/eslint-module-utils/-/eslint-module-utils-2.6.0.tgz#579ebd094f56af7797d19c9866c9c9486629bfa6" + integrity sha512-6j9xxegbqe8/kZY8cYpcp0xhbK0EgJlg3g9mib3/miLaExuuwc3n5UEfSnU6hWMbT0FAYVvDbL9RrRgpUeQIvA== dependencies: - debug "^2.6.8" + debug "^2.6.9" pkg-dir "^2.0.0" eslint-plugin-babel@^5.2.1: @@ -3799,27 +3944,28 @@ eslint-plugin-babel@^5.2.1: dependencies: eslint-rule-composer "^0.3.0" -eslint-plugin-import@2.18.2, eslint-plugin-import@^2.14.0: - version "2.18.2" - resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.18.2.tgz#02f1180b90b077b33d447a17a2326ceb400aceb6" - integrity sha512-5ohpsHAiUBRNaBWAF08izwUGlbrJoJJ+W9/TBwsGoR1MnlgfwMIKrFeSjWbt6moabiXW9xNvtFz+97KHRfI4HQ== +eslint-plugin-import@2.20.2, eslint-plugin-import@^2.14.0: + version "2.20.2" + resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.20.2.tgz#91fc3807ce08be4837141272c8b99073906e588d" + integrity sha512-FObidqpXrR8OnCh4iNsxy+WACztJLXAHBO5hK79T1Hc77PgQZkyDGA5Ag9xAvRpglvLNxhH/zSmZ70/pZ31dHg== dependencies: array-includes "^3.0.3" + array.prototype.flat "^1.2.1" contains-path "^0.1.0" debug "^2.6.9" doctrine "1.5.0" eslint-import-resolver-node "^0.3.2" - eslint-module-utils "^2.4.0" + eslint-module-utils "^2.4.1" has "^1.0.3" minimatch "^3.0.4" object.values "^1.1.0" read-pkg-up "^2.0.0" - resolve "^1.11.0" + resolve "^1.12.0" -eslint-plugin-prettier@3.1.1: - version "3.1.1" - resolved "https://registry.yarnpkg.com/eslint-plugin-prettier/-/eslint-plugin-prettier-3.1.1.tgz#507b8562410d02a03f0ddc949c616f877852f2ba" - integrity sha512-A+TZuHZ0KU0cnn56/9mfR7/KjUJ9QNVXUhwvRFSR7PGPe0zQR6PTkmyqg1AtUUEOzTqeRsUwyKFh0oVZKVCrtA== +eslint-plugin-prettier@3.1.3: + version "3.1.3" + resolved "https://registry.yarnpkg.com/eslint-plugin-prettier/-/eslint-plugin-prettier-3.1.3.tgz#ae116a0fc0e598fdae48743a4430903de5b4e6ca" + integrity sha512-+HG5jmu/dN3ZV3T6eCD7a4BlAySdN7mLIbJYo0z1cFQuI+r2DiTJEFeF68ots93PsnrMxbzIZ2S/ieX+mkrBeQ== dependencies: prettier-linter-helpers "^1.0.0" @@ -3834,40 +3980,34 @@ eslint-plugin-react-native-globals@^0.1.1: integrity sha512-9aEPf1JEpiTjcFAmmyw8eiIXmcNZOqaZyHO77wgm0/dWfT/oxC1SrIq8ET38pMxHYrcB6Uew+TzUVsBeczF88g== eslint-plugin-react-native@^3.3.0: - version "3.7.0" - resolved "https://registry.yarnpkg.com/eslint-plugin-react-native/-/eslint-plugin-react-native-3.7.0.tgz#7e2cc1f3cf24919c4c0ea7fac13301e7444e105f" - integrity sha512-krLtQmGih/uJDPxF8DBpnU8J3kRUsDm/Dey5yEhOO8LN1I3Wesbk4PGCg8Zah57azKFU+9YtGooFjJcDJWUs+g== + version "3.8.1" + resolved "https://registry.yarnpkg.com/eslint-plugin-react-native/-/eslint-plugin-react-native-3.8.1.tgz#92811e37191ecb0d29c0f0a0c9e5c943ee573821" + integrity sha512-6Z4s4nvgFRdda/1s1+uu4a6EMZwEjjJ9Bk/1yBImv0fd9U2CsGu2cUakAtV83cZKhizbWhSouXoaK4JtlScdFg== dependencies: eslint-plugin-react-native-globals "^0.1.1" eslint-plugin-react@^7.11.1: - version "7.14.3" - resolved "https://registry.yarnpkg.com/eslint-plugin-react/-/eslint-plugin-react-7.14.3.tgz#911030dd7e98ba49e1b2208599571846a66bdf13" - integrity sha512-EzdyyBWC4Uz2hPYBiEJrKCUi2Fn+BJ9B/pJQcjw5X+x/H2Nm59S4MJIvL4O5NEE0+WbnQwEBxWY03oUk+Bc3FA== + version "7.20.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-react/-/eslint-plugin-react-7.20.0.tgz#f98712f0a5e57dfd3e5542ef0604b8739cd47be3" + integrity sha512-rqe1abd0vxMjmbPngo4NaYxTcR3Y4Hrmc/jg4T+sYz63yqlmJRknpEQfmWY+eDWPuMmix6iUIK+mv0zExjeLgA== dependencies: - array-includes "^3.0.3" + array-includes "^3.1.1" doctrine "^2.1.0" has "^1.0.3" - jsx-ast-utils "^2.1.0" - object.entries "^1.1.0" - object.fromentries "^2.0.0" - object.values "^1.1.0" + jsx-ast-utils "^2.2.3" + object.entries "^1.1.1" + object.fromentries "^2.0.2" + object.values "^1.1.1" prop-types "^15.7.2" - resolve "^1.10.1" + resolve "^1.15.1" + string.prototype.matchall "^4.0.2" + xregexp "^4.3.0" eslint-rule-composer@^0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/eslint-rule-composer/-/eslint-rule-composer-0.3.0.tgz#79320c927b0c5c0d3d3d2b76c8b4a488f25bbaf9" integrity sha512-bt+Sh8CtDmn2OajxvNO+BX7Wn4CIWMpTRm3MaiKPCQcnnlm0CS2mhui6QaoeQugs+3Kj2ESKEEGJUdVafwhiCg== -eslint-scope@3.7.1: - version "3.7.1" - resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-3.7.1.tgz#3d63c3edfda02e06e01a452ad88caacc7cdcb6e8" - integrity sha1-PWPD7f2gLgbgGkUq2IyqzHzctug= - dependencies: - esrecurse "^4.1.0" - estraverse "^4.1.1" - eslint-scope@^4.0.3: version "4.0.3" resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-4.0.3.tgz#ca03833310f6889a3264781aa82e63eb9cfe7848" @@ -3884,57 +4024,63 @@ eslint-scope@^5.0.0: esrecurse "^4.1.0" estraverse "^4.1.1" -eslint-utils@^1.3.1, eslint-utils@^1.4.2: - version "1.4.2" - resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-1.4.2.tgz#166a5180ef6ab7eb462f162fd0e6f2463d7309ab" - integrity sha512-eAZS2sEUMlIeCjBeubdj45dmBHQwPHWyBcT1VSYB7o9x9WRRqKxyUoiXlRjyAwzN7YEzHJlYg0NmzDRWx6GP4Q== +eslint-utils@^1.3.1: + version "1.4.3" + resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-1.4.3.tgz#74fec7c54d0776b6f67e0251040b5806564e981f" + integrity sha512-fbBN5W2xdY45KulGXmLHZ3c3FHfVYmKg0IrAKGOkT/464PQsx2UeIzfz1RmEci+KLm1bBaAzZAh8+/E+XAeZ8Q== dependencies: - eslint-visitor-keys "^1.0.0" + eslint-visitor-keys "^1.1.0" + +eslint-utils@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-2.0.0.tgz#7be1cc70f27a72a76cd14aa698bcabed6890e1cd" + integrity sha512-0HCPuJv+7Wv1bACm8y5/ECVfYdfsAm9xmVb7saeFlxjPYALefjhbYoCkBjPdPzGH8wWyTpAez82Fh3VKYEZ8OA== + dependencies: + eslint-visitor-keys "^1.1.0" eslint-visitor-keys@^1.0.0, eslint-visitor-keys@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-1.1.0.tgz#e2a82cea84ff246ad6fb57f9bde5b46621459ec2" integrity sha512-8y9YjtM1JBJU/A9Kc+SbaOV4y29sSWckBwMHa+FGtVj5gN/sbnKDf6xJUl+8g7FAij9LVaP8C24DUiH/f/2Z9A== -eslint@6.5.1: - version "6.5.1" - resolved "https://registry.yarnpkg.com/eslint/-/eslint-6.5.1.tgz#828e4c469697d43bb586144be152198b91e96ed6" - integrity sha512-32h99BoLYStT1iq1v2P9uwpyznQ4M2jRiFB6acitKz52Gqn+vPaMDUTB1bYi1WN4Nquj2w+t+bimYUG83DC55A== +eslint@7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-7.0.0.tgz#c35dfd04a4372110bd78c69a8d79864273919a08" + integrity sha512-qY1cwdOxMONHJfGqw52UOpZDeqXy8xmD0u8CT6jIstil72jkhURC704W8CFyTPDPllz4z4lu0Ql1+07PG/XdIg== dependencies: "@babel/code-frame" "^7.0.0" ajv "^6.10.0" - chalk "^2.1.0" - cross-spawn "^6.0.5" + chalk "^4.0.0" + cross-spawn "^7.0.2" debug "^4.0.1" doctrine "^3.0.0" eslint-scope "^5.0.0" - eslint-utils "^1.4.2" + eslint-utils "^2.0.0" eslint-visitor-keys "^1.1.0" - espree "^6.1.1" - esquery "^1.0.1" + espree "^7.0.0" + esquery "^1.2.0" esutils "^2.0.2" file-entry-cache "^5.0.1" functional-red-black-tree "^1.0.1" glob-parent "^5.0.0" - globals "^11.7.0" + globals "^12.1.0" ignore "^4.0.6" import-fresh "^3.0.0" imurmurhash "^0.1.4" - inquirer "^6.4.1" + inquirer "^7.0.0" is-glob "^4.0.0" js-yaml "^3.13.1" json-stable-stringify-without-jsonify "^1.0.1" - levn "^0.3.0" + levn "^0.4.1" lodash "^4.17.14" minimatch "^3.0.4" - mkdirp "^0.5.1" natural-compare "^1.4.0" - optionator "^0.8.2" + optionator "^0.9.1" progress "^2.0.0" - regexpp "^2.0.1" - semver "^6.1.2" - strip-ansi "^5.2.0" - strip-json-comments "^3.0.1" + regexpp "^3.1.0" + semver "^7.2.1" + strip-ansi "^6.0.0" + strip-json-comments "^3.1.0" table "^5.2.3" text-table "^0.2.0" v8-compile-cache "^2.0.3" @@ -3990,13 +4136,13 @@ espree@^5.0.1: acorn-jsx "^5.0.0" eslint-visitor-keys "^1.0.0" -espree@^6.1.1: - version "6.1.1" - resolved "https://registry.yarnpkg.com/espree/-/espree-6.1.1.tgz#7f80e5f7257fc47db450022d723e356daeb1e5de" - integrity sha512-EYbr8XZUhWbYCqQRW0duU5LxzL5bETN6AjKBGy1302qqzPaCH10QbRg3Wvco79Z8x9WbiE8HYB4e75xl6qUYvQ== +espree@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/espree/-/espree-7.0.0.tgz#8a7a60f218e69f120a842dc24c5a88aa7748a74e" + integrity sha512-/r2XEx5Mw4pgKdyb7GNLQNsu++asx/dltf/CI8RFi9oGHxmQFgvLbc5Op4U6i8Oaj+kdslhJtVlEZeAqH5qOTw== dependencies: - acorn "^7.0.0" - acorn-jsx "^5.0.2" + acorn "^7.1.1" + acorn-jsx "^5.2.0" eslint-visitor-keys "^1.1.0" esprima@^4.0.0: @@ -4004,12 +4150,12 @@ esprima@^4.0.0: resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== -esquery@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.0.1.tgz#406c51658b1f5991a5f9b62b1dc25b00e3e5c708" - integrity sha512-SmiyZ5zIWH9VM+SRUReLS5Q8a7GxtRdxEBVZpm98rJM7Sb+A9DVCndXfkeFUd3byderg+EbDkfnevfCwynWaNA== +esquery@^1.0.1, esquery@^1.2.0: + version "1.3.1" + resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.3.1.tgz#b78b5828aa8e214e29fb74c4d5b752e1c033da57" + integrity sha512-olpvt9QG0vniUBZspVRN6lwB7hOZoTRtT+jzR+tS4ffYx2mzbw+z0XCOk44aaLYKApNX5nMm+E+P6o25ip/DHQ== dependencies: - estraverse "^4.0.0" + estraverse "^5.1.0" esrecurse@^4.1.0: version "4.2.1" @@ -4018,30 +4164,45 @@ esrecurse@^4.1.0: dependencies: estraverse "^4.1.0" -estraverse@^4.0.0, estraverse@^4.1.0, estraverse@^4.1.1: - version "4.2.0" - resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.2.0.tgz#0dee3fed31fcd469618ce7342099fc1afa0bdb13" - integrity sha1-De4/7TH81GlhjOc0IJn8GvoL2xM= +estraverse@^4.1.0, estraverse@^4.1.1: + version "4.3.0" + resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.3.0.tgz#398ad3f3c5a24948be7725e83d11a7de28cdbd1d" + integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw== + +estraverse@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.1.0.tgz#374309d39fd935ae500e7b92e8a6b4c720e59642" + integrity sha512-FyohXK+R0vE+y1nHLoBM7ZTyqRpqAlhdZHCWIWEviFLiGB8b04H6bQs8G+XTthacvT8VuwvteiP7RJSxMs8UEw== esutils@^2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.2.tgz#0abf4f1caa5bcb1f7a9d8acc6dea4faaa04bac9b" - integrity sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs= + version "2.0.3" + resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" + integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== etag@~1.8.1: version "1.8.1" resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" integrity sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc= +event-target-shim@^5.0.0: + version "5.0.1" + resolved "https://registry.yarnpkg.com/event-target-shim/-/event-target-shim-5.0.1.tgz#5d4d3ebdf9583d63a5333ce2deb7480ab2b05789" + integrity sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ== + eventemitter3@^3.1.0: version "3.1.2" resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-3.1.2.tgz#2d3d48f9c346698fce83a85d7d664e98535df6e7" integrity sha512-tvtQIeLVHjDkJYnzf2dgVMxfuSGJeM/7UCG17TT4EumTfNtF+0nebF/4zWOIkCreAbtNqhGEboB6BWrwqNaw4Q== +events@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/events/-/events-1.1.1.tgz#9ebdb7635ad099c70dcc4c2a1f5004288e8bd924" + integrity sha1-nr23Y1rQmccNzEwqH1AEKI6L2SQ= + events@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/events/-/events-3.0.0.tgz#9a0a0dfaf62893d92b875b8f2698ca4114973e88" - integrity sha512-Dc381HFWJzEOhQ+d8pkNon++bk9h6cdAoAj4iE6Q4y6xgTzySWXlKn05/TVNpjnfRqi/X0EpJEJohPjNI3zpVA== + version "3.1.0" + resolved "https://registry.yarnpkg.com/events/-/events-3.1.0.tgz#84279af1b34cb75aa88bf5ff291f6d0bd9b31a59" + integrity sha512-Rv+u8MLHNOdMjTAFeT3nCjHn2aGlx435FP/sDHNaRhDEMwyI/aB22Kj2qIN8R0cw3z28psEQLYwxVKLsKrMgWg== evp_bytestokey@^1.0.0, evp_bytestokey@^1.0.3: version "1.0.3" @@ -4049,33 +4210,7 @@ evp_bytestokey@^1.0.0, evp_bytestokey@^1.0.3: integrity sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA== dependencies: md5.js "^1.3.4" - safe-buffer "^5.1.1" - -execa@0.10.0: - version "0.10.0" - resolved "https://registry.yarnpkg.com/execa/-/execa-0.10.0.tgz#ff456a8f53f90f8eccc71a96d11bdfc7f082cb50" - integrity sha512-7XOMnz8Ynx1gGo/3hyV9loYNPWM94jG3+3T3Y8tsfSstFmETmENCMU/A/zj8Lyaj1lkgEepKepvd6240tBRvlw== - dependencies: - cross-spawn "^6.0.0" - get-stream "^3.0.0" - is-stream "^1.1.0" - npm-run-path "^2.0.0" - p-finally "^1.0.0" - signal-exit "^3.0.0" - strip-eof "^1.0.0" - -execa@^0.7.0: - version "0.7.0" - resolved "https://registry.yarnpkg.com/execa/-/execa-0.7.0.tgz#944becd34cc41ee32a63a9faf27ad5a65fc59777" - integrity sha1-lEvs00zEHuMqY6n68nrVpl/Fl3c= - dependencies: - cross-spawn "^5.0.1" - get-stream "^3.0.0" - is-stream "^1.1.0" - npm-run-path "^2.0.0" - p-finally "^1.0.0" - signal-exit "^3.0.0" - strip-eof "^1.0.0" + safe-buffer "^5.1.1" execa@^1.0.0: version "1.0.0" @@ -4090,18 +4225,6 @@ execa@^1.0.0: signal-exit "^3.0.0" strip-eof "^1.0.0" -executable@4.1.1: - version "4.1.1" - resolved "https://registry.yarnpkg.com/executable/-/executable-4.1.1.tgz#41532bff361d3e57af4d763b70582db18f5d133c" - integrity sha512-8iA79xD3uAch729dUG8xaaBBFGaEa0wdD2VkYLFHwlqosEj/jT66AzcreRDSgV7ehnNLBW2WR5jIXwGKjVdTLg== - dependencies: - pify "^2.2.0" - -exit-hook@^1.0.0: - version "1.1.1" - resolved "https://registry.yarnpkg.com/exit-hook/-/exit-hook-1.1.1.tgz#f05ca233b48c05d54fff07765df8507e95c02ff8" - integrity sha1-8FyiM7SMBdVP/wd2XfhQfpXAL/g= - expand-brackets@^2.1.4: version "2.1.4" resolved "https://registry.yarnpkg.com/expand-brackets/-/expand-brackets-2.1.4.tgz#b77735e315ce30f6b6eff0f83b04151a22449622" @@ -4115,6 +4238,11 @@ expand-brackets@^2.1.4: snapdragon "^0.8.1" to-regex "^3.0.1" +expand-template@^2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/expand-template/-/expand-template-2.0.3.tgz#6e14b3fcee0f3a6340ecb57d2e8918692052a47c" + integrity sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg== + expand-tilde@^2.0.0, expand-tilde@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/expand-tilde/-/expand-tilde-2.0.2.tgz#97e801aa052df02454de46b02bf621642cdc8502" @@ -4122,7 +4250,7 @@ expand-tilde@^2.0.0, expand-tilde@^2.0.2: dependencies: homedir-polyfill "^1.0.1" -express@4.17.1, express@^4.16.3: +express@4.17.1: version "4.17.1" resolved "https://registry.yarnpkg.com/express/-/express-4.17.1.tgz#4491fc38605cf51f8629d39c2b5d026f98a4c134" integrity sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g== @@ -4173,7 +4301,7 @@ extend-shallow@^3.0.0, extend-shallow@^3.0.2: assign-symbols "^1.0.0" is-extendable "^1.0.1" -extend@^3.0.0, extend@~3.0.2: +extend@^3.0.0, extend@^3.0.2, extend@~3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa" integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g== @@ -4201,16 +4329,6 @@ extglob@^2.0.4: snapdragon "^0.8.1" to-regex "^3.0.1" -extract-zip@1.6.7: - version "1.6.7" - resolved "https://registry.yarnpkg.com/extract-zip/-/extract-zip-1.6.7.tgz#a840b4b8af6403264c8db57f4f1a74333ef81fe9" - integrity sha1-qEC0uK9kAyZMjbV/Txp0Mz74H+k= - dependencies: - concat-stream "1.6.2" - debug "2.6.9" - mkdirp "0.5.1" - yauzl "2.4.1" - extsprintf@1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.3.0.tgz#96918440e3041a7a414f8c52e3c574eb3c3e1e05" @@ -4221,10 +4339,10 @@ extsprintf@^1.2.0: resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.4.0.tgz#e2689f8f356fad62cca65a3a91c5df5f9551692f" integrity sha1-4mifjzVvrWLMplo6kcXfX5VRaS8= -fast-deep-equal@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz#7b05218ddf9667bf7f370bf7fdb2cb15fdd0aa49" - integrity sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk= +fast-deep-equal@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.1.tgz#545145077c501491e33b15ec408c294376e94ae4" + integrity sha512-8UEa58QDLauDNfpbrX55Q9jrGHThw2ZMdOky5Gl1CDtVeJDPVrG4Jxx1N8jw2gkWaff5UUuX1KJd+9zGe2B+ZA== fast-diff@^1.1.2: version "1.2.0" @@ -4244,41 +4362,29 @@ fast-glob@^2.2.6: micromatch "^3.1.10" fast-json-stable-stringify@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz#d5142c0caee6b1189f87d3a76111064f86c8bbf2" - integrity sha1-1RQsDK7msRifh9OnYREGT4bIu/I= + version "2.1.0" + resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" + integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== -fast-levenshtein@~2.0.4: +fast-levenshtein@^2.0.6, fast-levenshtein@~2.0.6: version "2.0.6" resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" integrity sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc= -fd-slicer@~1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/fd-slicer/-/fd-slicer-1.0.1.tgz#8b5bcbd9ec327c5041bf9ab023fd6750f1177e65" - integrity sha1-i1vL2ewyfFBBv5qwI/1nUPEXfmU= - dependencies: - pend "~1.2.0" - -fd-slicer@~1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/fd-slicer/-/fd-slicer-1.1.0.tgz#25c7c89cb1f9077f8891bbe61d8f390eae256f1e" - integrity sha1-JcfInLH5B3+IkbvmHY85Dq4lbx4= - dependencies: - pend "~1.2.0" +fast-text-encoding@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/fast-text-encoding/-/fast-text-encoding-1.0.2.tgz#ff1ad5677bde049e0f8656aa6083a7ef2c5836e2" + integrity sha512-5rQdinSsycpzvAoHga2EDn+LRX1d5xLFsuNG0Kg61JrAT/tASXcLL0nf/33v+sAxlQcfYmWbTURa1mmAf55jGw== figgy-pudding@^3.4.1, figgy-pudding@^3.5.1: - version "3.5.1" - resolved "https://registry.yarnpkg.com/figgy-pudding/-/figgy-pudding-3.5.1.tgz#862470112901c727a0e495a80744bd5baa1d6790" - integrity sha512-vNKxJHTEKNThjfrdJwHc7brvM6eVevuO5nTj6ez8ZQ1qbXTvGthucRF7S4vf2cr71QVnT70V34v0S1DyQsti0w== + version "3.5.2" + resolved "https://registry.yarnpkg.com/figgy-pudding/-/figgy-pudding-3.5.2.tgz#b4eee8148abb01dcf1d1ac34367d59e12fa61d6e" + integrity sha512-0btnI/H8f2pavGMN8w40mlSKOfTK2SVJmBfBeVIj3kNw0swwgzyRq0d5TJVOwodFmtvpPeWPN/MCcfuWF0Ezbw== -figures@^1.7.0: - version "1.7.0" - resolved "https://registry.yarnpkg.com/figures/-/figures-1.7.0.tgz#cbe1e3affcf1cd44b80cadfed28dc793a9701d2e" - integrity sha1-y+Hjr/zxzUS4DK3+0o3Hk6lwHS4= - dependencies: - escape-string-regexp "^1.0.5" - object-assign "^4.1.0" +figlet@^1.1.1: + version "1.4.0" + resolved "https://registry.yarnpkg.com/figlet/-/figlet-1.4.0.tgz#21c5878b3752a932ebdb8be400e2d10bbcddfd60" + integrity sha512-CxxIjEKHlqGosgXaIA+sikGDdV6KZOOlzPJnYuPgQlOSHZP5h9WIghYI30fyXnwEVeSH7Hedy72gC6zJrFC+SQ== figures@^2.0.0: version "2.0.0" @@ -4287,6 +4393,13 @@ figures@^2.0.0: dependencies: escape-string-regexp "^1.0.5" +figures@^3.0.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/figures/-/figures-3.2.0.tgz#625c18bd293c604dc4a8ddb2febf0c88341746af" + integrity sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg== + dependencies: + escape-string-regexp "^1.0.5" + file-entry-cache@^5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-5.0.1.tgz#ca0f6efa6dd3d561333fb14515065c2fafdf439c" @@ -4294,10 +4407,10 @@ file-entry-cache@^5.0.1: dependencies: flat-cache "^2.0.1" -filesize@^3.6.1: - version "3.6.1" - resolved "https://registry.yarnpkg.com/filesize/-/filesize-3.6.1.tgz#090bb3ee01b6f801a8a8be99d31710b3422bb317" - integrity sha512-7KjR1vv6qnicaPMi1iiTcI85CyYwRO/PSFCu6SvqL8jN2Wjt/NIYQTFtFs7fSDCYOstUkEWIQGFUg5YZQfjlcg== +file-uri-to-path@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz#553a7b8446ff6f684359c445f1e37a05dacc33dd" + integrity sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw== fill-range@^4.0.0: version "4.0.0" @@ -4338,6 +4451,15 @@ find-cache-dir@^2.1.0: make-dir "^2.0.0" pkg-dir "^3.0.0" +find-cache-dir@^3.2.0: + version "3.3.1" + resolved "https://registry.yarnpkg.com/find-cache-dir/-/find-cache-dir-3.3.1.tgz#89b33fad4a4670daa94f855f7fbe31d6d84fe880" + integrity sha512-t2GDMt3oGC/v+BMwzmllWDuJF/xcDtE5j/fCGbqDD7OLuJkj0cfh1YSA5VKPvwMeLFLNDBkwOKZ2X85jGLVftQ== + dependencies: + commondir "^1.0.1" + make-dir "^3.0.2" + pkg-dir "^4.1.0" + find-up@3.0.0, find-up@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/find-up/-/find-up-3.0.0.tgz#49169f1d7993430646da61ecc5ae355c21c97b73" @@ -4360,7 +4482,7 @@ find-up@^2.0.0, find-up@^2.1.0: dependencies: locate-path "^2.0.0" -find-up@^4.0.0: +find-up@^4.0.0, find-up@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/find-up/-/find-up-4.1.0.tgz#97afe7d6cdc0bc5928584b7c8d7b16e8a9aa5d19" integrity sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw== @@ -4368,6 +4490,13 @@ find-up@^4.0.0: locate-path "^5.0.0" path-exists "^4.0.0" +find-versions@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/find-versions/-/find-versions-3.2.0.tgz#10297f98030a786829681690545ef659ed1d254e" + integrity sha512-P8WRou2S+oe222TOCHitLy8zj+SIsVJh52VP4lvXkaFVnOFFdoWv1H1Jjvel1aI6NCFOAaeAVm8qrI0odiLcww== + dependencies: + semver-regex "^2.0.0" + findup-sync@3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/findup-sync/-/findup-sync-3.0.0.tgz#17b108f9ee512dfb7a5c7f3c8b27ea9e1a9c08d1" @@ -4378,11 +4507,6 @@ findup-sync@3.0.0: micromatch "^3.0.4" resolve-dir "^1.0.1" -finity@^0.5.4: - version "0.5.4" - resolved "https://registry.yarnpkg.com/finity/-/finity-0.5.4.tgz#f2a8a9198e8286467328ec32c8bfcc19a2229c11" - integrity sha512-3l+5/1tuw616Lgb0QBimxfdd2TqaDGpfCBpfX6EqtFmqUV3FtQnVEX4Aa62DagYEqnsTIjZcTfbq9msDbXYgyA== - flat-cache@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-2.0.1.tgz#5d296d6f04bda44a4630a301413bdbc2ec085ec0" @@ -4407,9 +4531,9 @@ flat@^4.1.0: is-buffer "~2.0.3" flatted@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/flatted/-/flatted-2.0.1.tgz#69e57caa8f0eacbc281d2e2cb458d46fdb449e08" - integrity sha512-a1hQMktqW9Nmqr5aktAux3JMNqaucxGcjtjWnZLHX7yyPCmlSV3M54nGYbqT8K+0GhF3NBgmJCc3ma+WOgX8Jg== + version "2.0.2" + resolved "https://registry.yarnpkg.com/flatted/-/flatted-2.0.2.tgz#4575b21e2bcee7434aa9be662f4b7b5f9c2b5138" + integrity sha512-r5wGx7YeOwNWNlCA0wQ86zKyDLMQr+/RB8xy74M4hTphfmjlijTSSXGuH8rnvKZnfT9i+75zmd8jcKdMR4O6jA== flush-write-stream@^1.0.0: version "1.1.1" @@ -4431,13 +4555,13 @@ for-in@^1.0.2: resolved "https://registry.yarnpkg.com/for-in/-/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80" integrity sha1-gQaNKVqBQuwKxybG4iAMMPttXoA= -foreground-child@^1.5.6: - version "1.5.6" - resolved "https://registry.yarnpkg.com/foreground-child/-/foreground-child-1.5.6.tgz#4fd71ad2dfde96789b980a5c0a295937cb2f5ce9" - integrity sha1-T9ca0t/elnibmApcCilZN8svXOk= +foreground-child@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/foreground-child/-/foreground-child-2.0.0.tgz#71b32800c9f15aa8f2f83f4a6bd9bff35d861a53" + integrity sha512-dCIq9FpEcyQyXKCkyzmlPTFNgrCzPudOe+mhvJU5zAtlBnGVy2yKxtfsxK2tQBThwq225jcvBjpw1Gr40uzZCA== dependencies: - cross-spawn "^4" - signal-exit "^3.0.0" + cross-spawn "^7.0.0" + signal-exit "^3.0.2" forever-agent@~0.6.1: version "0.6.1" @@ -4445,9 +4569,9 @@ forever-agent@~0.6.1: integrity sha1-+8cfDEGt6zf5bFd60e1C2P2sypE= form-data@^2.3.1, form-data@^2.5.0: - version "2.5.0" - resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.5.0.tgz#094ec359dc4b55e7d62e0db4acd76e89fe874d37" - integrity sha512-WXieX3G/8side6VIqx44ablyULoGruSde5PNTxoUyo5CeyAMX6nVWUd0rgist/EuX655cjhUhTo1Fo3tRYqbcA== + version "2.5.1" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.5.1.tgz#f2cbec57b5e59e23716e128fe44d4e5dd23895f4" + integrity sha512-m21N3WOmEEURgk6B9GLOE4RuWOFf28Lhh9qGYeNlGq4VDXUlJy2th2slBNU8Gp8EzloYZOibZJ7t5ecIrFSjVA== dependencies: asynckit "^0.4.0" combined-stream "^1.0.6" @@ -4463,9 +4587,9 @@ form-data@~2.3.2: mime-types "^2.1.12" formidable@^1.2.0: - version "1.2.1" - resolved "https://registry.yarnpkg.com/formidable/-/formidable-1.2.1.tgz#70fb7ca0290ee6ff961090415f4b3df3d2082659" - integrity sha512-Fs9VRguL0gqGHkXS5GQiMCr1VhZBxz0JnJs4JmMp/2jL18Fmbzvv7vOFRU+U8TBkHEE/CX1qDXzJplVULgsLeg== + version "1.2.2" + resolved "https://registry.yarnpkg.com/formidable/-/formidable-1.2.2.tgz#bf69aea2972982675f00865342b982986f6b8dd9" + integrity sha512-V8gLm+41I/8kguQ4/o1D3RIHRmhYFG4pnNyonvua+40rqcEmT4+V71yaZ3B457xbbgCsCfjSPi65u/W6vK1U5Q== forwarded@~0.1.2: version "0.1.2" @@ -4492,14 +4616,15 @@ from2@^2.1.0: inherits "^2.0.1" readable-stream "^2.0.0" -fs-extra@5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-5.0.0.tgz#414d0110cdd06705734d055652c5411260c31abd" - integrity sha512-66Pm4RYbjzdyeuqudYqhFiNBbCIuI9kgRqLPSHIlXHidW8NIQtVdkM1yeZ4lXwuhbTETv3EUGMNHAAw6hiundQ== - dependencies: - graceful-fs "^4.1.2" - jsonfile "^4.0.0" - universalify "^0.1.0" +fromentries@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/fromentries/-/fromentries-1.2.0.tgz#e6aa06f240d6267f913cea422075ef88b63e7897" + integrity sha512-33X7H/wdfO99GdRLLgkjUrD4geAFdq/Uv0kl3HD4da6HDixd2GUg8Mw7dahLCV9r/EARkmtYBB6Tch4EEokFTQ== + +fs-constants@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/fs-constants/-/fs-constants-1.0.0.tgz#6be0de9be998ce16af8afc24497b9ee9b7ccd9ad" + integrity sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow== fs-extra@^7.0.0: version "7.0.1" @@ -4520,11 +4645,18 @@ fs-extra@^8.1.0: universalify "^0.1.0" fs-minipass@^1.2.5: - version "1.2.6" - resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-1.2.6.tgz#2c5cc30ded81282bfe8a0d7c7c1853ddeb102c07" - integrity sha512-crhvyXcMejjv3Z5d2Fa9sf5xLYVCF5O1c71QxbVnbLsmYMBEvDAftewesN/HhY03YRoA7zOMxjNGrF5svGaaeQ== + version "1.2.7" + resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-1.2.7.tgz#ccff8570841e7fe4265693da88936c55aed7f7c7" + integrity sha512-GWSSJGFy4e9GUeCcbIkED+bgAoFyj7XF1mV8rma3QW4NIqX9Kyx79N/PF61H5udOV3aY1IaMLs6pGbH71nlCTA== + dependencies: + minipass "^2.6.0" + +fs-minipass@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-2.1.0.tgz#7f5036fdbf12c63c169190cbe4199c852271f9fb" + integrity sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg== dependencies: - minipass "^2.2.1" + minipass "^3.0.0" fs-write-stream-atomic@^1.0.8: version "1.0.10" @@ -4547,12 +4679,17 @@ fs@0.0.1-security: integrity sha1-invTcYa23d84E/I4WLV+yq9eQdQ= fsevents@^1.2.7: - version "1.2.9" - resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-1.2.9.tgz#3f5ed66583ccd6f400b5a00db6f7e861363e388f" - integrity sha512-oeyj2H3EjjonWcFjD5NvZNE9Rqe4UW+nQBU2HNeKw0koVLEFIhtyETyAakeAM3de7Z/SW5kcA+fZUait9EApnw== + version "1.2.13" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-1.2.13.tgz#f325cb0455592428bcf11b383370ef70e3bfcc38" + integrity sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw== dependencies: + bindings "^1.5.0" nan "^2.12.1" - node-pre-gyp "^0.12.0" + +fsevents@~2.1.1, fsevents@~2.1.2: + version "2.1.3" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.1.3.tgz#fb738703ae8d2f9fe900c33836ddebee8b97f23e" + integrity sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ== fsu@^1.0.2: version "1.1.1" @@ -4583,21 +4720,40 @@ gauge@~2.7.3: strip-ansi "^3.0.1" wide-align "^1.1.0" +gaxios@^2.1.0: + version "2.3.4" + resolved "https://registry.yarnpkg.com/gaxios/-/gaxios-2.3.4.tgz#eea99353f341c270c5f3c29fc46b8ead56f0a173" + integrity sha512-US8UMj8C5pRnao3Zykc4AAVr+cffoNKRTg9Rsf2GiuZCW69vgJj38VK2PzlPuQU73FZ/nTk9/Av6/JGcE1N9vA== + dependencies: + abort-controller "^3.0.0" + extend "^3.0.2" + https-proxy-agent "^5.0.0" + is-stream "^2.0.0" + node-fetch "^2.3.0" + +gcp-metadata@^3.4.0: + version "3.5.0" + resolved "https://registry.yarnpkg.com/gcp-metadata/-/gcp-metadata-3.5.0.tgz#6d28343f65a6bbf8449886a0c0e4a71c77577055" + integrity sha512-ZQf+DLZ5aKcRpLzYUyBS3yo3N0JSa82lNDO8rj3nMSlovLcz2riKFBsYgDzeXcv75oo5eqB2lx+B14UvPoCRnA== + dependencies: + gaxios "^2.1.0" + json-bigint "^0.3.0" + genfun@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/genfun/-/genfun-5.0.0.tgz#9dd9710a06900a5c4a5bf57aca5da4e52fe76537" integrity sha512-KGDOARWVga7+rnB3z9Sd2Letx515owfk0hSxHGuqjANb1M+x2bGZGqHLiozPsYMdM2OubeMni/Hpwmjq6qIUhA== +gensync@^1.0.0-beta.1: + version "1.0.0-beta.1" + resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.1.tgz#58f4361ff987e5ff6e1e7a210827aa371eaac269" + integrity sha512-r8EC6NO1sngH/zdD9fiRDLdcgnbayXah+mLgManTaIZJqEC1MZstmnox8KpnI2/fxQwrp5OpCOYWLp4rBl4Jcg== + get-caller-file@^2.0.1: version "2.0.5" resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== -get-func-name@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/get-func-name/-/get-func-name-2.0.0.tgz#ead774abee72e20409433a066366023dd6887a41" - integrity sha1-6td0q+5y4gQJQzoGY2YCPdaIekE= - get-pkg-repo@^1.0.0: version "1.4.0" resolved "https://registry.yarnpkg.com/get-pkg-repo/-/get-pkg-repo-1.4.0.tgz#c73b489c06d80cc5536c2c853f9e05232056972d" @@ -4624,16 +4780,6 @@ get-stdin@^6.0.0: resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-6.0.0.tgz#9e09bf712b360ab9225e812048f71fde9c89657b" integrity sha512-jp4tHawyV7+fkkSKyvjuLZswblUtz+SQKzSWnBbii16BuZksJlU1wuBYXY75r+duh/llF1ur6oNwi+2ZzjKZ7g== -get-stdin@^7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-7.0.0.tgz#8d5de98f15171a125c5e516643c7a6d0ea8a96f6" - integrity sha512-zRKcywvrXlXsA0v0i9Io4KDRaAw7+a1ZpjRwl9Wox8PFlVCCHra7E9c4kqXCoCM9nR5tBkaTTZRBoCm60bFqTQ== - -get-stream@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-3.0.0.tgz#8e943d1358dc37555054ecbe2edb05aa174ede14" - integrity sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ= - get-stream@^4.0.0, get-stream@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-4.1.0.tgz#c1b255575f3dc21d59bfc79cd3d2b46b1c3a54b5" @@ -4641,18 +4787,18 @@ get-stream@^4.0.0, get-stream@^4.1.0: dependencies: pump "^3.0.0" +get-stream@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-5.1.0.tgz#01203cdc92597f9b909067c3e656cc1f4d3c4dc9" + integrity sha512-EXr1FOzrzTfGeL0gQdeFEvOMm2mzMOglyiOXSTpPC+iAjAKftbr3jpCMWynogwYnM+eSj9sHGc6wjIcDvYiygw== + dependencies: + pump "^3.0.0" + get-value@^2.0.3, get-value@^2.0.6: version "2.0.6" resolved "https://registry.yarnpkg.com/get-value/-/get-value-2.0.6.tgz#dc15ca1c672387ca76bd37ac0a395ba2042a2c28" integrity sha1-3BXKHGcjh8p2vTesCjlbogQqLCg= -getos@3.1.1: - version "3.1.1" - resolved "https://registry.yarnpkg.com/getos/-/getos-3.1.1.tgz#967a813cceafee0156b0483f7cffa5b3eff029c5" - integrity sha512-oUP1rnEhAr97rkitiszGP9EgDVYnmchgFzfqRzSkgtfv7ai6tEi7Ko8GgjNXts7VLWEqrTWyhsOKLe5C5b/Zkg== - dependencies: - async "2.6.1" - getpass@^0.1.1: version "0.1.7" resolved "https://registry.yarnpkg.com/getpass/-/getpass-0.1.7.tgz#5eff8e3e684d569ae4cb2b1282604e8ba62149fa" @@ -4679,10 +4825,10 @@ git-remote-origin-url@^2.0.0: gitconfiglocal "^1.0.0" pify "^2.3.0" -git-revision-webpack-plugin@3.0.4: - version "3.0.4" - resolved "https://registry.yarnpkg.com/git-revision-webpack-plugin/-/git-revision-webpack-plugin-3.0.4.tgz#ed1eaad094d6956d3299381050a5a50f662550bd" - integrity sha512-ym4Zkl32HtTRZVVgl1KoE+sWtgeFyDjN3vaBQfn8cCv1btAX7rdDY9tgpv4Zi+yxq150pp+pUkGH9L1lRpZOUg== +git-revision-webpack-plugin@3.0.6: + version "3.0.6" + resolved "https://registry.yarnpkg.com/git-revision-webpack-plugin/-/git-revision-webpack-plugin-3.0.6.tgz#5dd6c6829fae05b405059dea6195b23875d69d4d" + integrity sha512-vW/9dBahGbpKPcccy3xKkHgdWoH/cAPPc3lQw+3edl7b4j29JfNGVrja0a1d8ZoRe4nTN8GCPrF9aBErDnzx5Q== git-semver-tags@^2.0.3: version "2.0.3" @@ -4714,6 +4860,11 @@ gitconfiglocal@^1.0.0: dependencies: ini "^1.3.2" +github-from-package@0.0.0: + version "0.0.0" + resolved "https://registry.yarnpkg.com/github-from-package/-/github-from-package-0.0.0.tgz#97fb5d96bfde8973313f20e8288ef9a167fa64ce" + integrity sha1-l/tdlr/eiXMxPyDoKI75oWf6ZM4= + glob-parent@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-3.1.0.tgz#9e6af6299d8d3bd2bd40430832bd113df906c5ae" @@ -4722,10 +4873,10 @@ glob-parent@^3.1.0: is-glob "^3.1.0" path-dirname "^1.0.0" -glob-parent@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.0.0.tgz#1dc99f0f39b006d3e92c2c284068382f0c20e954" - integrity sha512-Z2RwiujPRGluePM6j699ktJYxmPpJKCfpGA13jz2hmFZC7gKetzrWvg5KN3+OsIFmydGyZ1AVwERCq1w/ZZwRg== +glob-parent@^5.0.0, glob-parent@~5.1.0: + version "5.1.1" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.1.tgz#b6c1ef417c4e5663ea498f1c45afac6916bbc229" + integrity sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ== dependencies: is-glob "^4.0.1" @@ -4746,10 +4897,10 @@ glob@7.1.3: once "^1.3.0" path-is-absolute "^1.0.0" -glob@^7.0.3, glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4: - version "7.1.4" - resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.4.tgz#aa608a2f6c577ad357e1ae5a5c26d9a8d1969255" - integrity sha512-hkLPepehmnKk41pUGm3sYxoFs/umurYfYJCerbXEyFIWcAzvpipAgVkBqqT9RBKMGjnq6kMuyYwha6csxbiM1A== +glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6: + version "7.1.6" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.6.tgz#141f33b81a7c2492e125594307480c46679278a6" + integrity sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA== dependencies: fs.realpath "^1.0.0" inflight "^1.0.4" @@ -4758,12 +4909,12 @@ glob@^7.0.3, glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4: once "^1.3.0" path-is-absolute "^1.0.0" -global-dirs@^0.1.0: - version "0.1.1" - resolved "https://registry.yarnpkg.com/global-dirs/-/global-dirs-0.1.1.tgz#b319c0dd4607f353f3be9cca4c72fc148c49f445" - integrity sha1-sxnA3UYH81PzvpzKTHL8FIxJ9EU= +global-dirs@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/global-dirs/-/global-dirs-2.0.1.tgz#acdf3bb6685bcd55cb35e8a052266569e9469201" + integrity sha512-5HqUqdhkEovj2Of/ms3IeS/EekcO54ytHRLV4PEY2rhRwrHXLQjeVEES0Lhka0xwNDtGYn58wyC4s5+MHsOO6A== dependencies: - ini "^1.3.4" + ini "^1.3.5" global-modules@2.0.0: version "2.0.0" @@ -4806,6 +4957,13 @@ globals@^11.1.0, globals@^11.7.0: resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e" integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA== +globals@^12.1.0: + version "12.4.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-12.4.0.tgz#a18813576a41b00a24a97e7f815918c2e19925f8" + integrity sha512-BWICuzzDvDoH54NHKCseDanAhE3CeDorgDL5MT6LMXXj2WCnd9UC2szdk4AWLfjdgNBCXLUanXYcpBBKOSWGwg== + dependencies: + type-fest "^0.8.1" + globby@^9.2.0: version "9.2.0" resolved "https://registry.yarnpkg.com/globby/-/globby-9.2.0.tgz#fd029a706c703d29bdd170f4b6db3a3f7a7cb63d" @@ -4820,92 +4978,83 @@ globby@^9.2.0: pify "^4.0.1" slash "^2.0.0" -google-auth-library@^0.10.0: - version "0.10.0" - resolved "https://registry.yarnpkg.com/google-auth-library/-/google-auth-library-0.10.0.tgz#6e15babee85fd1dd14d8d128a295b6838d52136e" - integrity sha1-bhW6vuhf0d0U2NEoopW2g41SE24= - dependencies: - gtoken "^1.2.1" - jws "^3.1.4" - lodash.noop "^3.0.1" - request "^2.74.0" - -google-p12-pem@^0.1.0: - version "0.1.2" - resolved "https://registry.yarnpkg.com/google-p12-pem/-/google-p12-pem-0.1.2.tgz#33c46ab021aa734fa0332b3960a9a3ffcb2f3177" - integrity sha1-M8RqsCGqc0+gMys5YKmj/8svMXc= +google-auth-library@^5.9.1: + version "5.10.1" + resolved "https://registry.yarnpkg.com/google-auth-library/-/google-auth-library-5.10.1.tgz#504ec75487ad140e68dd577c21affa363c87ddff" + integrity sha512-rOlaok5vlpV9rSiUu5EpR0vVpc+PhN62oF4RyX/6++DG1VsaulAFEMlDYBLjJDDPI6OcNOCGAKy9UVB/3NIDXg== + dependencies: + arrify "^2.0.0" + base64-js "^1.3.0" + ecdsa-sig-formatter "^1.0.11" + fast-text-encoding "^1.0.0" + gaxios "^2.1.0" + gcp-metadata "^3.4.0" + gtoken "^4.1.0" + jws "^4.0.0" + lru-cache "^5.0.0" + +google-p12-pem@^2.0.0: + version "2.0.4" + resolved "https://registry.yarnpkg.com/google-p12-pem/-/google-p12-pem-2.0.4.tgz#036462394e266472632a78b685f0cc3df4ef337b" + integrity sha512-S4blHBQWZRnEW44OcR7TL9WR+QCqByRvhNDZ/uuQfpxywfupikf/miba8js1jZi6ZOGv5slgSuoshCWh6EMDzg== dependencies: - node-forge "^0.7.1" + node-forge "^0.9.0" -google-spreadsheet@2.0.8: - version "2.0.8" - resolved "https://registry.yarnpkg.com/google-spreadsheet/-/google-spreadsheet-2.0.8.tgz#4c52c13bec439010010d9123ebc3cd06f43a51c0" - integrity sha512-mOcYls5JlJN/nVcqVLrdvQ9XV6Z2OnAPuh8t8FhirAYYVCXv3QuLple8kknQ6gO+8i88T+rxrpJZ8PLjx6BvuQ== +google-spreadsheet@3.0.11: + version "3.0.11" + resolved "https://registry.yarnpkg.com/google-spreadsheet/-/google-spreadsheet-3.0.11.tgz#6114fda30bafe371fa454337ef2e7d01d1abf564" + integrity sha512-bkYUdsq4Nwg7klnFevG6MRHXAfGh9gYVymGp001YdtThct5uSIB/41lUOsGzFbQQSR5FcBTwAu9nZVYvaNdv9Q== dependencies: - async "^1.3.0" - google-auth-library "^0.10.0" + axios "^0.19.1" + google-auth-library "^5.9.1" lodash "^4.17.15" - request "^2.69.0" - xml2js "~0.4.0" -got@^6.7.1: - version "6.7.1" - resolved "https://registry.yarnpkg.com/got/-/got-6.7.1.tgz#240cd05785a9a18e561dc1b44b41c763ef1e8db0" - integrity sha1-JAzQV4WpoY5WHcG0S0HHY+8ejbA= +got@^9.6.0: + version "9.6.0" + resolved "https://registry.yarnpkg.com/got/-/got-9.6.0.tgz#edf45e7d67f99545705de1f7bbeeeb121765ed85" + integrity sha512-R7eWptXuGYxwijs0eV+v3o6+XH1IqVK8dJOEecQfTmkncw9AV4dcw/Dhxi8MdlqPthxxpZyizMzyg8RTmEsG+Q== dependencies: - create-error-class "^3.0.0" + "@sindresorhus/is" "^0.14.0" + "@szmarczak/http-timer" "^1.1.2" + cacheable-request "^6.0.0" + decompress-response "^3.3.0" duplexer3 "^0.1.4" - get-stream "^3.0.0" - is-redirect "^1.0.0" - is-retry-allowed "^1.0.0" - is-stream "^1.0.0" - lowercase-keys "^1.0.0" - safe-buffer "^5.0.1" - timed-out "^4.0.0" - unzip-response "^2.0.1" - url-parse-lax "^1.0.0" - -graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1.2: - version "4.2.0" - resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.0.tgz#8d8fdc73977cb04104721cb53666c1ca64cd328b" - integrity sha512-jpSvDPV4Cq/bgtpndIWbI5hmYxhQGHPC4d4cqBPb4DLniCfhJokdXhwhaDuLBGLQdvvRum/UiX6ECVIPvDXqdg== + get-stream "^4.1.0" + lowercase-keys "^1.0.1" + mimic-response "^1.0.1" + p-cancelable "^1.0.0" + to-readable-stream "^1.0.0" + url-parse-lax "^3.0.0" -graceful-fs@^4.1.6, graceful-fs@^4.2.0: - version "4.2.2" - resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.2.tgz#6f0952605d0140c1cfdb138ed005775b92d67b02" - integrity sha512-IItsdsea19BoLC7ELy13q1iJFNmd7ofZH5+X/pJr90/nRoPEX0DJo1dHDbgtYWOhJhcCgMDTOw84RZ72q6lB+Q== +graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.2: + version "4.2.4" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.4.tgz#2256bde14d3632958c465ebc96dc467ca07a29fb" + integrity sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw== growl@1.10.5: version "1.10.5" resolved "https://registry.yarnpkg.com/growl/-/growl-1.10.5.tgz#f2735dc2283674fa67478b10181059355c369e5e" integrity sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA== -gtoken@^1.2.1: - version "1.2.3" - resolved "https://registry.yarnpkg.com/gtoken/-/gtoken-1.2.3.tgz#5509571b8afd4322e124cf66cf68115284c476d8" - integrity sha512-wQAJflfoqSgMWrSBk9Fg86q+sd6s7y6uJhIvvIPz++RElGlMtEqsdAR2oWwZ/WTEtp7P9xFbJRrT976oRgzJ/w== - dependencies: - google-p12-pem "^0.1.0" - jws "^3.0.0" - mime "^1.4.1" - request "^2.72.0" - -gzip-size@^5.0.0: - version "5.1.1" - resolved "https://registry.yarnpkg.com/gzip-size/-/gzip-size-5.1.1.tgz#cb9bee692f87c0612b232840a873904e4c135274" - integrity sha512-FNHi6mmoHvs1mxZAds4PpdCS6QG8B4C1krxJsMutgxl5t3+GlRTzzI3NEkifXx2pVsOvJdOGSmIgDhQ55FwdPA== +gtoken@^4.1.0: + version "4.1.4" + resolved "https://registry.yarnpkg.com/gtoken/-/gtoken-4.1.4.tgz#925ff1e7df3aaada06611d30ea2d2abf60fcd6a7" + integrity sha512-VxirzD0SWoFUo5p8RDP8Jt2AGyOmyYcT/pOUgDKJCK+iSw0TMqwrVfY37RXTNmoKwrzmDHSk0GMT9FsgVmnVSA== dependencies: - duplexer "^0.1.1" - pify "^4.0.1" + gaxios "^2.1.0" + google-p12-pem "^2.0.0" + jws "^4.0.0" + mime "^2.2.0" -handlebars@^4.1.2: - version "4.1.2" - resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.1.2.tgz#b6b37c1ced0306b221e094fc7aca3ec23b131b67" - integrity sha512-nvfrjqvt9xQ8Z/w0ijewdD/vvWDTOweBUm96NTr66Wfvo1mJenBLwcYmPs3TIBP5ruzYGD7Hx/DaM9RmhroGPw== +handlebars@^4.7.6: + version "4.7.6" + resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.7.6.tgz#d4c05c1baf90e9945f77aa68a7a219aa4a7df74e" + integrity sha512-1f2BACcBfiwAfStCKZNrUCgqNZkGsAT7UM3kkYtXuLo0KnaVfjKOyf7PRzB6++aK9STyT1Pd2ZCPe3EGOXleXA== dependencies: + minimist "^1.2.5" neo-async "^2.6.0" - optimist "^0.6.1" source-map "^0.6.1" + wordwrap "^1.0.0" optionalDependencies: uglify-js "^3.1.4" @@ -4914,7 +5063,7 @@ har-schema@^2.0.0: resolved "https://registry.yarnpkg.com/har-schema/-/har-schema-2.0.0.tgz#a94c2224ebcac04782a0d9035521f24735b7ec92" integrity sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI= -har-validator@~5.1.0: +har-validator@~5.1.3: version "5.1.3" resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-5.1.3.tgz#1ef89ebd3e4996557675eed9893110dc350fa080" integrity sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g== @@ -4922,6 +5071,11 @@ har-validator@~5.1.0: ajv "^6.5.5" har-schema "^2.0.0" +hard-rejection@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/hard-rejection/-/hard-rejection-2.1.0.tgz#1c6eda5c1685c63942766d79bb40ae773cecd883" + integrity sha512-VIZB+ibDhx7ObhAe7OVtoEbuP4h/MuOTHJ+J8h/eBXotJYl0fBgR72xDFCKgIh22OJZIOVNxBMWuhAr10r8HdA== + has-ansi@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/has-ansi/-/has-ansi-2.0.0.tgz#34f5049ce1ecdf2b0649af3ef24e45ed35416d91" @@ -4934,10 +5088,15 @@ has-flag@^3.0.0: resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" integrity sha1-tdRU3CGZriJWmfNGfloH87lVuv0= -has-symbols@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.0.tgz#ba1a8f1af2a0fc39650f5c850367704122063b44" - integrity sha1-uhqPGvKg/DllD1yFA2dwQSIGO0Q= +has-flag@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" + integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== + +has-symbols@^1.0.0, has-symbols@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.1.tgz#9f5214758a44196c406d9bd76cebf81ec2dd31e8" + integrity sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg== has-unicode@^2.0.0, has-unicode@^2.0.1: version "2.0.1" @@ -4975,7 +5134,12 @@ has-values@^1.0.0: is-number "^3.0.0" kind-of "^4.0.0" -has@^1.0.1, has@^1.0.3: +has-yarn@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/has-yarn/-/has-yarn-2.1.0.tgz#137e11354a7b5bf11aa5cb649cf0c6f3ff2b2e77" + integrity sha512-UqBRqi4ju7T+TqGNdqAO0PaSVGsDGJUBQvk9eUWNGRY1CFGDzYhLWoM7JQEemnlvVcv/YEmc2wNW8BC24EnUsw== + +has@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796" integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw== @@ -4983,12 +5147,13 @@ has@^1.0.1, has@^1.0.3: function-bind "^1.1.1" hash-base@^3.0.0: - version "3.0.4" - resolved "https://registry.yarnpkg.com/hash-base/-/hash-base-3.0.4.tgz#5fc8686847ecd73499403319a6b0a3f3f6ae4918" - integrity sha1-X8hoaEfs1zSZQDMZprCj8/auSRg= + version "3.1.0" + resolved "https://registry.yarnpkg.com/hash-base/-/hash-base-3.1.0.tgz#55c381d9e06e1d2997a883b4a3fddfe7f0d3af33" + integrity sha512-1nmYp/rhMDiE7AYkDw+lLwlAzz0AntGIe51F3RfFfEqyQ3feY2eI/NcwC6umIQVOASPMsWJLJScWKSSvzL9IVA== dependencies: - inherits "^2.0.1" - safe-buffer "^5.0.1" + inherits "^2.0.4" + readable-stream "^3.6.0" + safe-buffer "^5.2.0" hash.js@^1.0.0, hash.js@^1.0.3: version "1.1.7" @@ -4998,18 +5163,24 @@ hash.js@^1.0.0, hash.js@^1.0.3: inherits "^2.0.3" minimalistic-assert "^1.0.1" -hasha@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/hasha/-/hasha-3.0.0.tgz#52a32fab8569d41ca69a61ff1a214f8eb7c8bd39" - integrity sha1-UqMvq4Vp1BymmmH/GiFPjrfIvTk= +hasha@^5.0.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/hasha/-/hasha-5.2.0.tgz#33094d1f69c40a4a6ac7be53d5fe3ff95a269e0c" + integrity sha512-2W+jKdQbAdSIrggA8Q35Br8qKadTrqCTC8+XZvBWepKDK6m9XkX6Iz1a2yh2KP01kzAR/dpuMeUnocoLYDcskw== dependencies: - is-stream "^1.0.1" + is-stream "^2.0.0" + type-fest "^0.8.0" he@1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f" integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw== +highlight.js@^9.6.0: + version "9.18.1" + resolved "https://registry.yarnpkg.com/highlight.js/-/highlight.js-9.18.1.tgz#ed21aa001fe6252bb10a3d76d47573c6539fe13c" + integrity sha512-OrVKYz70LHsnCgmbXctv/bfuvntIKDz177h0Co37DQ5jamGZLVmoCVMtjMtNZY3X9DrCcKfklHPNeA0uPZhSJg== + hmac-drbg@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/hmac-drbg/-/hmac-drbg-1.0.1.tgz#d2745701025a6c775a6c545793ed502fc0c649a1" @@ -5026,26 +5197,26 @@ homedir-polyfill@^1.0.1: dependencies: parse-passwd "^1.0.0" -hoopy@^0.1.4: - version "0.1.4" - resolved "https://registry.yarnpkg.com/hoopy/-/hoopy-0.1.4.tgz#609207d661100033a9a9402ad3dea677381c1b1d" - integrity sha512-HRcs+2mr52W0K+x8RzcLzuPPmVIKMSv97RGHy0Ea9y/mpcaK+xTrjICA04KAHi4GRzxliNqNJEFYWHghy3rSfQ== - -hosted-git-info@^2.1.4: - version "2.7.1" - resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.7.1.tgz#97f236977bd6e125408930ff6de3eec6281ec047" - integrity sha512-7T/BxH19zbcCTa8XkMlbK5lTo1WtgkFi3GvdWEyNuc4Vex7/9Dqbnpsf4JMydcfj9HCg4zUWFTL3Za6lapg5/w== +hosted-git-info@^2.1.4, hosted-git-info@^2.7.1: + version "2.8.8" + resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.8.tgz#7539bd4bc1e0e0a895815a2e0262420b12858488" + integrity sha512-f/wzC2QaWBs7t9IYqB4T3sR1xviIViXJRJTWBlx2Gf3g0Xi5vI7Yy4koXQ1c9OYDGHN9sBy1DQ2AB8fqZBWhUg== -hosted-git-info@^2.7.1: - version "2.8.4" - resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.4.tgz#44119abaf4bc64692a16ace34700fed9c03e2546" - integrity sha512-pzXIvANXEFrc5oFFXRMkbLPQ2rXRoDERwDLyrcUxGhaZhgP54BBSl9Oheh7Vv0T090cszWBxPjkQQ5Sq1PbBRQ== +html-escaper@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/html-escaper/-/html-escaper-2.0.2.tgz#dfd60027da36a36dfcbe236262c00a5822681453" + integrity sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg== http-cache-semantics@^3.8.1: version "3.8.1" resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-3.8.1.tgz#39b0e16add9b605bf0a9ef3d9daaf4843b4cacd2" integrity sha512-5ai2iksyV8ZXmnZhHH4rWPoxxistEexSi5936zIQ1bnNTW5VnA85B6P/VpXiRM017IgRvb2kKo1a//y+0wSp3w== +http-cache-semantics@^4.0.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz#49e91c5cbf36c9b94bcfcd71c23d5249ec74e390" + integrity sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ== + http-errors@1.7.2: version "1.7.2" resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.7.2.tgz#4f5029cf13239f31036e5b2e55292bcfbcc85c8f" @@ -5090,14 +5261,22 @@ https-browserify@^1.0.0: resolved "https://registry.yarnpkg.com/https-browserify/-/https-browserify-1.0.0.tgz#ec06c10e0a34c0f2faf199f7fd7fc78fffd03c73" integrity sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM= -https-proxy-agent@^2.2.1: - version "2.2.2" - resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-2.2.2.tgz#271ea8e90f836ac9f119daccd39c19ff7dfb0793" - integrity sha512-c8Ndjc9Bkpfx/vCJueCPy0jlP4ccCCSNDp8xwCZzPjKJUm+B+u9WX2x98Qx4n1PiMNTWo3D7KK5ifNV/yJyRzg== +https-proxy-agent@^2.2.3: + version "2.2.4" + resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-2.2.4.tgz#4ee7a737abd92678a293d9b34a1af4d0d08c787b" + integrity sha512-OmvfoQ53WLjtA9HeYP9RNrWMJzzAz1JGaSFr1nijg0PVR1JaD/xbJq1mdEIIlxGpXp9eSe/O2LgU9DJmTPd0Eg== dependencies: agent-base "^4.3.0" debug "^3.1.0" +https-proxy-agent@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz#e2a90542abb68a762e0a0850f6c9edadfd8506b2" + integrity sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA== + dependencies: + agent-base "6" + debug "4" + humanize-ms@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/humanize-ms/-/humanize-ms-1.2.1.tgz#c46e3159a293f6b896da29316d8b6fe8bb79bbed" @@ -5105,31 +5284,30 @@ humanize-ms@^1.2.1: dependencies: ms "^2.0.0" -husky@3.0.9: - version "3.0.9" - resolved "https://registry.yarnpkg.com/husky/-/husky-3.0.9.tgz#a2c3e9829bfd6b4957509a9500d2eef5dbfc8044" - integrity sha512-Yolhupm7le2/MqC1VYLk/cNmYxsSsqKkTyBhzQHhPK1jFnC89mmmNVuGtLNabjDI6Aj8UNIr0KpRNuBkiC4+sg== +husky@4.2.5: + version "4.2.5" + resolved "https://registry.yarnpkg.com/husky/-/husky-4.2.5.tgz#2b4f7622673a71579f901d9885ed448394b5fa36" + integrity sha512-SYZ95AjKcX7goYVZtVZF2i6XiZcHknw50iXvY7b0MiGoj5RwdgRQNEHdb+gPDPCXKlzwrybjFjkL6FOj8uRhZQ== dependencies: - chalk "^2.4.2" + chalk "^4.0.0" ci-info "^2.0.0" - cosmiconfig "^5.2.1" - execa "^1.0.0" - get-stdin "^7.0.0" + compare-versions "^3.6.0" + cosmiconfig "^6.0.0" + find-versions "^3.2.0" opencollective-postinstall "^2.0.2" pkg-dir "^4.2.0" please-upgrade-node "^3.2.0" - read-pkg "^5.2.0" - run-node "^1.0.0" slash "^3.0.0" + which-pm-runs "^1.0.0" -iconv-lite@0.4.24, iconv-lite@^0.4.24, iconv-lite@^0.4.4, iconv-lite@~0.4.13: +iconv-lite@0.4.24, iconv-lite@^0.4.24, iconv-lite@~0.4.13: version "0.4.24" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== dependencies: safer-buffer ">= 2.1.2 < 3" -ieee754@^1.1.4: +ieee754@1.1.13, ieee754@^1.1.4: version "1.1.13" resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.1.13.tgz#ec168558e95aa181fd87d37f55c32bbcb6708b84" integrity sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg== @@ -5145,9 +5323,9 @@ ignore-by-default@^1.0.1: integrity sha1-SMptcvbGo68Aqa1K5odr44ieKwk= ignore-walk@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/ignore-walk/-/ignore-walk-3.0.1.tgz#a83e62e7d272ac0e3b551aaa82831a19b69f82f8" - integrity sha512-DTVlMx3IYPe0/JJcYP7Gxg7ttZZu3IInhuEhbchuqneY9wWe5Ojy2mXLBaQFUQmo0AW2r3qG7m1mg86js+gnlQ== + version "3.0.3" + resolved "https://registry.yarnpkg.com/ignore-walk/-/ignore-walk-3.0.3.tgz#017e2447184bfeade7c238e4aefdd1e8f95b1e37" + integrity sha512-m7o6xuOaT1aqheYHKf8W6J5pYH85ZI9w077erOzLje3JsB1gkafkAhHHY19dqjulgIZHFm32Cp5uNZgcQqdJKw== dependencies: minimatch "^3.0.4" @@ -5164,10 +5342,10 @@ import-fresh@^2.0.0: caller-path "^2.0.0" resolve-from "^3.0.0" -import-fresh@^3.0.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.1.0.tgz#6d33fa1dcef6df930fae003446f33415af905118" - integrity sha512-PpuksHKGt8rXfWEr9m9EHIpgyyaltBy8+eF6GJM0QCAxMgxCfucMF3mjecK2QsJr0amJW7gTqh5/wht0z2UhEQ== +import-fresh@^3.0.0, import-fresh@^3.1.0: + version "3.2.1" + resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.2.1.tgz#633ff618506e793af5ac91bf48b72677e15cbe66" + integrity sha512-6e1q1cnWP2RXD9/keSkxHScg508CdXqXWgWBaETNhyuBFz+kUZlKboh+ISK+bU++DmbHimVBrOz/zzPe0sZ3sQ== dependencies: parent-module "^1.0.0" resolve-from "^4.0.0" @@ -5202,6 +5380,11 @@ indent-string@^3.0.0: resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-3.2.0.tgz#4a5fd6d27cc332f37e5419a504dbb837105c9289" integrity sha1-Sl/W0nzDMvN+VBmlBNu4NxBckok= +indent-string@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-4.0.0.tgz#624f8f4497d619b2d9768531d58f4122854d7251" + integrity sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg== + infer-owner@^1.0.3, infer-owner@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/infer-owner/-/infer-owner-1.0.4.tgz#c4cefcaa8e51051c2a40ba2ce8a3d27295af9467" @@ -5215,7 +5398,7 @@ inflight@^1.0.4: once "^1.3.0" wrappy "1" -inherits@2, inherits@2.0.4, inherits@^2.0.1, inherits@^2.0.3, inherits@~2.0.1, inherits@~2.0.3: +inherits@2, inherits@2.0.4, inherits@^2.0.1, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.1, inherits@~2.0.3: version "2.0.4" resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== @@ -5249,7 +5432,7 @@ init-package-json@^1.10.3: validate-npm-package-license "^3.0.1" validate-npm-package-name "^3.0.0" -inquirer@^6.2.0: +inquirer@^6.2.0, inquirer@^6.2.2: version "6.5.2" resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-6.5.2.tgz#ad50942375d036d327ff528c08bd5fab089928ca" integrity sha512-cntlB5ghuB0iuO65Ovoi8ogLHiWGs/5yNrtUcKjFhSSiVeAIVpD7koaSU9RM8mpXw5YDi9RdYXGQMaOURB7ycQ== @@ -5268,25 +5451,34 @@ inquirer@^6.2.0: strip-ansi "^5.1.0" through "^2.3.6" -inquirer@^6.2.2, inquirer@^6.4.1: - version "6.5.0" - resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-6.5.0.tgz#2303317efc9a4ea7ec2e2df6f86569b734accf42" - integrity sha512-scfHejeG/lVZSpvCXpsB4j/wQNPM5JC8kiElOI0OUTwmc1RTpXr4H32/HOlQHcZiYl2z2VElwuCVDRG8vFmbnA== +inquirer@^7.0.0: + version "7.1.0" + resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-7.1.0.tgz#1298a01859883e17c7264b82870ae1034f92dd29" + integrity sha512-5fJMWEmikSYu0nv/flMc475MhGbB7TSPd/2IpFV4I4rMklboCH2rQjYY5kKiYGHqUF9gvaambupcJFFG9dvReg== dependencies: - ansi-escapes "^3.2.0" - chalk "^2.4.2" - cli-cursor "^2.1.0" + ansi-escapes "^4.2.1" + chalk "^3.0.0" + cli-cursor "^3.1.0" cli-width "^2.0.0" external-editor "^3.0.3" - figures "^2.0.0" - lodash "^4.17.12" - mute-stream "0.0.7" - run-async "^2.2.0" - rxjs "^6.4.0" - string-width "^2.1.0" - strip-ansi "^5.1.0" + figures "^3.0.0" + lodash "^4.17.15" + mute-stream "0.0.8" + run-async "^2.4.0" + rxjs "^6.5.3" + string-width "^4.1.0" + strip-ansi "^6.0.0" through "^2.3.6" +internal-slot@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/internal-slot/-/internal-slot-1.0.2.tgz#9c2e9fb3cd8e5e4256c6f45fe310067fcfa378a3" + integrity sha512-2cQNfwhAfJIkU4KZPkDI+Gj5yNNnbqi40W9Gge6dfnk4TocEVm00B3bdiL+JINrbGJil2TeHvM4rETGzk/f/0g== + dependencies: + es-abstract "^1.17.0-next.1" + has "^1.0.3" + side-channel "^1.0.2" + interpret@1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/interpret/-/interpret-1.2.0.tgz#d5061a6224be58e8083985f5014d844359576296" @@ -5297,15 +5489,15 @@ invert-kv@^2.0.0: resolved "https://registry.yarnpkg.com/invert-kv/-/invert-kv-2.0.0.tgz#7393f5afa59ec9ff5f67a27620d11c226e3eec02" integrity sha512-wPVv/y/QQ/Uiirj/vh3oP+1Ww+AWehmi1g5fFWGPF6IpCBCDVrhgHRMvrLfdYcwDh3QJbGXDW4JAuzxElLSqKA== -ip@^1.1.5: +ip@1.1.5: version "1.1.5" resolved "https://registry.yarnpkg.com/ip/-/ip-1.1.5.tgz#bdded70114290828c0a039e72ef25f5aaec4354a" integrity sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo= -ipaddr.js@1.9.0: - version "1.9.0" - resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.9.0.tgz#37df74e430a0e47550fe54a2defe30d8acd95f65" - integrity sha512-M4Sjn6N/+O6/IXSJseKqHoFc+5FdGJ22sXqnjTpdZweHK64MzEPAyQZyEU3R/KRv2GLoa7nNtg/C2Ev6m7z+eA== +ipaddr.js@1.9.1: + version "1.9.1" + resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.9.1.tgz#bff38543eeb8984825079ff3a2a8e6cbd46781b3" + integrity sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g== is-accessor-descriptor@^0.1.6: version "0.1.6" @@ -5326,6 +5518,11 @@ is-arrayish@^0.2.1: resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" integrity sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0= +is-arrayish@^0.3.1: + version "0.3.2" + resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.3.2.tgz#4574a2ae56f7ab206896fb431eaeed066fdf8f03" + integrity sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ== + is-binary-path@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-1.0.1.tgz#75f16642b480f187a711c814161fd3a4a7655898" @@ -5333,32 +5530,27 @@ is-binary-path@^1.0.0: dependencies: binary-extensions "^1.0.0" +is-binary-path@~2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09" + integrity sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw== + dependencies: + binary-extensions "^2.0.0" + is-buffer@^1.1.5: version "1.1.6" resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be" integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w== -is-buffer@^2.0.2, is-buffer@~2.0.3: - version "2.0.3" - resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-2.0.3.tgz#4ecf3fcf749cbd1e472689e109ac66261a25e725" - integrity sha512-U15Q7MXTuZlrbymiz95PJpZxu8IlipAp4dtS3wOdgPXx3mqBnslrWU14kxfHB+Py/+2PVKSr37dMAgM2A4uArw== - -is-buffer@~2.0.4: +is-buffer@~2.0.3, is-buffer@~2.0.4: version "2.0.4" resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-2.0.4.tgz#3e572f23c8411a5cfd9557c849e3665e0b290623" integrity sha512-Kq1rokWXOPXWuaMAqZiJW4XxsmD9zGx9q4aePabbn3qCRGedtH7Cm+zV8WETitMfu1wdh+Rvd6w5egwSngUX2A== -is-callable@^1.1.4: - version "1.1.4" - resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.1.4.tgz#1e1adf219e1eeb684d691f9d6a05ff0d30a24d75" - integrity sha512-r5p9sxJjYnArLjObpjA4xu5EKI3CuKHkJXMhT7kwbpUyIFD1n5PMAsoPvWnvtZiNz7LjkYDRZhd7FlI0eMijEA== - -is-ci@1.2.1, is-ci@^1.0.10: - version "1.2.1" - resolved "https://registry.yarnpkg.com/is-ci/-/is-ci-1.2.1.tgz#e3779c8ee17fccf428488f6e281187f2e632841c" - integrity sha512-s6tfsaQaQi3JNciBH6shVqEDvhGut0SUXr31ag8Pd8BBbVVlcGfWhpPmEOoM6RJ5TFhbypvf5yyRw/VXW1IiWg== - dependencies: - ci-info "^1.5.0" +is-callable@^1.1.4, is-callable@^1.1.5: + version "1.1.5" + resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.1.5.tgz#f7e46b596890456db74e7f6e976cb3273d06faab" + integrity sha512-ESKv5sMCJB2jnHTWZ3O5itG+O128Hsus4K4Qh1h2/cgn2vbgnLSVqfV46AeJA9D5EeeLa9w81KUXMtn34zhX+Q== is-ci@^2.0.0: version "2.0.0" @@ -5382,9 +5574,9 @@ is-data-descriptor@^1.0.0: kind-of "^6.0.0" is-date-object@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.1.tgz#9aa20eb6aeebbff77fbd33e74ca01b33581d3a16" - integrity sha1-mqIOtq7rv/d/vTPnTKAbM1gdOhY= + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.2.tgz#bda736f2cd8fd06d32844e7743bfa7494c3bfd7e" + integrity sha512-USlDT524woQ08aoZFzh3/Z6ch9Y/EWXEHQ/AaRN0SkKq4t2Jw2R2339tSXmwuVoY7LLlBCbOIlx2myP/L5zk0g== is-descriptor@^0.1.0: version "0.1.6" @@ -5409,11 +5601,6 @@ is-directory@^0.3.1: resolved "https://registry.yarnpkg.com/is-directory/-/is-directory-0.3.1.tgz#61339b6f2475fc772fd9c9d83f5c8575dc154ae1" integrity sha1-YTObbyR1/Hcv2cnYP1yFddwVSuE= -is-empty@1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/is-empty/-/is-empty-1.2.0.tgz#de9bb5b278738a05a0b09a57e1fb4d4a341a9f6b" - integrity sha1-3pu1snhzigWgsJpX4ftNSjQan2s= - is-extendable@^0.1.0, is-extendable@^0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-0.1.1.tgz#62b110e289a471418e3ec36a617d472e301dfc89" @@ -5432,11 +5619,9 @@ is-extglob@^2.1.0, is-extglob@^2.1.1: integrity sha1-qIwCU1eR8C7TfHahueqXc8gz+MI= is-finite@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/is-finite/-/is-finite-1.0.2.tgz#cc6677695602be550ef11e8b4aa6305342b6d0aa" - integrity sha1-zGZ3aVYCvlUO8R6LSqYwU0K20Ko= - dependencies: - number-is-nan "^1.0.0" + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-finite/-/is-finite-1.1.0.tgz#904135c77fb42c0641d6aa1bcdbc4daa8da082f3" + integrity sha512-cdyMtqX/BOqqNBBiKlIVkytNHm49MtMlYyn1zxzvJKWmFMlGzm+ry5BBfYyeY9YmNKbRSo/o7OX9w9ale0wg3w== is-fullwidth-code-point@^1.0.0: version "1.0.0" @@ -5450,6 +5635,11 @@ is-fullwidth-code-point@^2.0.0: resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f" integrity sha1-o7MKXE8ZkYMWeqq5O+764937ZU8= +is-fullwidth-code-point@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" + integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== + is-glob@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-3.1.0.tgz#7ba5ae24217804ac70707b96922567486cc3e84a" @@ -5457,25 +5647,25 @@ is-glob@^3.1.0: dependencies: is-extglob "^2.1.0" -is-glob@^4.0.0, is-glob@^4.0.1: +is-glob@^4.0.0, is-glob@^4.0.1, is-glob@~4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.1.tgz#7567dbe9f2f5e2467bc77ab83c4a29482407a5dc" integrity sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg== dependencies: is-extglob "^2.1.1" -is-installed-globally@0.1.0, is-installed-globally@^0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/is-installed-globally/-/is-installed-globally-0.1.0.tgz#0dfd98f5a9111716dd535dda6492f67bf3d25a80" - integrity sha1-Df2Y9akRFxbdU13aZJL2e/PSWoA= +is-installed-globally@^0.3.1: + version "0.3.2" + resolved "https://registry.yarnpkg.com/is-installed-globally/-/is-installed-globally-0.3.2.tgz#fd3efa79ee670d1187233182d5b0a1dd00313141" + integrity sha512-wZ8x1js7Ia0kecP/CHM/3ABkAmujX7WPvQk6uu3Fly/Mk44pySulQpnHG46OMjHGXApINnV4QhY3SWnECO2z5g== dependencies: - global-dirs "^0.1.0" - is-path-inside "^1.0.0" + global-dirs "^2.0.1" + is-path-inside "^3.0.1" -is-npm@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-npm/-/is-npm-1.0.0.tgz#f2fb63a65e4905b406c86072765a1a4dc793b9f4" - integrity sha1-8vtjpl5JBbQGyGBydloaTceTufQ= +is-npm@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/is-npm/-/is-npm-4.0.0.tgz#c90dd8380696df87a7a6d823c20d0b12bbe3c84d" + integrity sha512-96ECIfh9xtDDlPylNPXhzjsykHsMJZ18ASpaWzQyBr4YRTcVjUvzaHayDAES2oU/3KpljhHUjtSRNiDwi0F0ig== is-number@^3.0.0: version "3.0.0" @@ -5494,12 +5684,15 @@ is-obj@^1.0.0: resolved "https://registry.yarnpkg.com/is-obj/-/is-obj-1.0.1.tgz#3e4729ac1f5fde025cd7d83a896dab9f4f67db0f" integrity sha1-PkcprB9f3gJc19g6iW2rn09n2w8= -is-path-inside@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-1.0.1.tgz#8ef5b7de50437a3fdca6b4e865ef7aa55cb48036" - integrity sha1-jvW33lBDej/cprToZe96pVy0gDY= - dependencies: - path-is-inside "^1.0.1" +is-obj@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/is-obj/-/is-obj-2.0.0.tgz#473fb05d973705e3fd9620545018ca8e22ef4982" + integrity sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w== + +is-path-inside@^3.0.1: + version "3.0.2" + resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-3.0.2.tgz#f5220fc82a3e233757291dddc9c5877f2a1f3017" + integrity sha512-/2UGPSgmtqwo1ktx8NDHjuPwZWmHhO+gj0f93EkhLB5RgW9RZevWYYlIkS6zePc6U2WpOdQYIwHe9YC4DWEBVg== is-plain-obj@^1.0.0, is-plain-obj@^1.1.0: version "1.1.0" @@ -5520,27 +5713,12 @@ is-plain-object@^3.0.0: dependencies: isobject "^4.0.0" -is-promise@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/is-promise/-/is-promise-2.1.0.tgz#79a2a9ece7f096e80f36d2b2f3bc16c1ff4bf3fa" - integrity sha1-eaKp7OfwlugPNtKy87wWwf9L8/o= - -is-redirect@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-redirect/-/is-redirect-1.0.0.tgz#1d03dded53bd8db0f30c26e4f95d36fc7c87dc24" - integrity sha1-HQPd7VO9jbDzDCbk+V02/HyH3CQ= - -is-regex@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.0.4.tgz#5517489b547091b0930e095654ced25ee97e9491" - integrity sha1-VRdIm1RwkbCTDglWVM7SXul+lJE= +is-regex@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.0.5.tgz#39d589a358bf18967f726967120b8fc1aed74eae" + integrity sha512-vlKW17SNq44owv5AQR3Cq0bQPEb8+kF3UKZ2fiZNOWtztYE5i0CzCZxFDwO58qAOWtxdBRVO/V5Qin1wjCqFYQ== dependencies: - has "^1.0.1" - -is-retry-allowed@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/is-retry-allowed/-/is-retry-allowed-1.1.0.tgz#11a060568b67339444033d0125a61a20d564fb34" - integrity sha1-EaBgVotnM5REAz0BJaYaINVk+zQ= + has "^1.0.3" is-ssh@^1.3.0: version "1.3.1" @@ -5549,26 +5727,36 @@ is-ssh@^1.3.0: dependencies: protocols "^1.1.0" -is-stream@^1.0.0, is-stream@^1.0.1, is-stream@^1.1.0: +is-stream@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44" integrity sha1-EtSj3U5o4Lec6428hBc66A2RykQ= +is-stream@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-2.0.0.tgz#bde9c32680d6fae04129d6ac9d921ce7815f78e3" + integrity sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw== + +is-string@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/is-string/-/is-string-1.0.5.tgz#40493ed198ef3ff477b8c7f92f644ec82a5cd3a6" + integrity sha512-buY6VNRjhQMiF1qWDouloZlQbRhDPCebwxSjxMjxgemYT46YMd2NR0/H+fBhEfWX4A/w9TBJ+ol+okqJKFE6vQ== + is-symbol@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.2.tgz#a055f6ae57192caee329e7a860118b497a950f38" - integrity sha512-HS8bZ9ox60yCJLH9snBpIwv9pYUAkcuLhSA1oero1UB5y9aiQpRA8y2ex945AOtCZL1lJDeIk3G5LthswI46Lw== + version "1.0.3" + resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.3.tgz#38e1014b9e6329be0de9d24a414fd7441ec61937" + integrity sha512-OwijhaRSgqvhm/0ZdAcXNZt9lYdKFpcRDT5ULUuYXPoT794UNOdU+gpT6Rzo7b4V2HUl/op6GqY894AZwv9faQ== dependencies: - has-symbols "^1.0.0" + has-symbols "^1.0.1" -is-text-path@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/is-text-path/-/is-text-path-2.0.0.tgz#b2484e2b720a633feb2e85b67dc193ff72c75636" - integrity sha512-+oDTluR6WEjdXEJMnC2z6A4FRwFoYuvShVVEGsS7ewc0UTi2QtAKMDJuL4BDEVt+5T7MjFo12RP8ghOM75oKJw== +is-text-path@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-text-path/-/is-text-path-1.0.1.tgz#4e1aa0fb51bfbcb3e92688001397202c1775b66e" + integrity sha1-Thqg+1G/vLPpJogAE5cgLBd1tm4= dependencies: - text-extensions "^2.0.0" + text-extensions "^1.0.0" -is-typedarray@~1.0.0: +is-typedarray@^1.0.0, is-typedarray@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" integrity sha1-5HnICFjfDBsR3dppQPlgEfzaSpo= @@ -5588,6 +5776,11 @@ is-wsl@^1.1.0: resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-1.1.0.tgz#1f16e4aa22b04d1336b66188a66af3c600c3a66d" integrity sha1-HxbkqiKwTRM2tmGIpmrzxgDDpm0= +is-yarn-global@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/is-yarn-global/-/is-yarn-global-0.3.0.tgz#d502d3382590ea3004893746754c89139973e232" + integrity sha512-VjSeb/lHmkoyd8ryPVIKvOCn4D1koMqY+vqyjjUfc3xyKtP4dYOxM44sZrnqQSzSds3xyOrUTLTC9LVCVgLngw== + isarray@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/isarray/-/isarray-0.0.1.tgz#8a18acfca9a8f4177e09abfc6038939b05d1eedf" @@ -5625,57 +5818,71 @@ isstream@~0.1.2: resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a" integrity sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo= -istanbul-lib-coverage@^2.0.5: - version "2.0.5" - resolved "https://registry.yarnpkg.com/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.5.tgz#675f0ab69503fad4b1d849f736baaca803344f49" - integrity sha512-8aXznuEPCJvGnMSRft4udDRDtb1V3pkQkMMI5LI+6HuQz5oQ4J2UFn1H82raA3qJtyOLkkwVqICBQkjnGtn5mA== +istanbul-lib-coverage@^3.0.0, istanbul-lib-coverage@^3.0.0-alpha.1: + version "3.0.0" + resolved "https://registry.yarnpkg.com/istanbul-lib-coverage/-/istanbul-lib-coverage-3.0.0.tgz#f5944a37c70b550b02a78a5c3b2055b280cec8ec" + integrity sha512-UiUIqxMgRDET6eR+o5HbfRYP1l0hqkWOs7vNxC/mggutCMUIhWMm8gAHb8tHlyfD3/l6rlgNA5cKdDzEAf6hEg== -istanbul-lib-hook@^2.0.7: - version "2.0.7" - resolved "https://registry.yarnpkg.com/istanbul-lib-hook/-/istanbul-lib-hook-2.0.7.tgz#c95695f383d4f8f60df1f04252a9550e15b5b133" - integrity sha512-vrRztU9VRRFDyC+aklfLoeXyNdTfga2EI3udDGn4cZ6fpSXpHLV9X6CHvfoMCPtggg8zvDDmC4b9xfu0z6/llA== +istanbul-lib-hook@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/istanbul-lib-hook/-/istanbul-lib-hook-3.0.0.tgz#8f84c9434888cc6b1d0a9d7092a76d239ebf0cc6" + integrity sha512-Pt/uge1Q9s+5VAZ+pCo16TYMWPBIl+oaNIjgLQxcX0itS6ueeaA+pEfThZpH8WxhFgCiEb8sAJY6MdUKgiIWaQ== dependencies: - append-transform "^1.0.0" + append-transform "^2.0.0" -istanbul-lib-instrument@^3.3.0: - version "3.3.0" - resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-3.3.0.tgz#a5f63d91f0bbc0c3e479ef4c5de027335ec6d630" - integrity sha512-5nnIN4vo5xQZHdXno/YDXJ0G+I3dAm4XgzfSVTPLQpj/zAV2dV6Juy0yaf10/zrJOJeHoN3fraFe+XRq2bFVZA== - dependencies: - "@babel/generator" "^7.4.0" - "@babel/parser" "^7.4.3" - "@babel/template" "^7.4.0" - "@babel/traverse" "^7.4.3" - "@babel/types" "^7.4.0" - istanbul-lib-coverage "^2.0.5" - semver "^6.0.0" +istanbul-lib-instrument@^4.0.0: + version "4.0.3" + resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-4.0.3.tgz#873c6fff897450118222774696a3f28902d77c1d" + integrity sha512-BXgQl9kf4WTCPCCpmFGoJkz/+uhvm7h7PFKUYxh7qarQd3ER33vHG//qaE8eN25l07YqZPpHXU9I09l/RD5aGQ== + dependencies: + "@babel/core" "^7.7.5" + "@istanbuljs/schema" "^0.1.2" + istanbul-lib-coverage "^3.0.0" + semver "^6.3.0" -istanbul-lib-report@^2.0.8: - version "2.0.8" - resolved "https://registry.yarnpkg.com/istanbul-lib-report/-/istanbul-lib-report-2.0.8.tgz#5a8113cd746d43c4889eba36ab10e7d50c9b4f33" - integrity sha512-fHBeG573EIihhAblwgxrSenp0Dby6tJMFR/HvlerBsrCTD5bkUuoNtn3gVh29ZCS824cGGBPn7Sg7cNk+2xUsQ== +istanbul-lib-processinfo@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/istanbul-lib-processinfo/-/istanbul-lib-processinfo-2.0.2.tgz#e1426514662244b2f25df728e8fd1ba35fe53b9c" + integrity sha512-kOwpa7z9hme+IBPZMzQ5vdQj8srYgAtaRqeI48NGmAQ+/5yKiHLV0QbYqQpxsdEF0+w14SoB8YbnHKcXE2KnYw== dependencies: - istanbul-lib-coverage "^2.0.5" - make-dir "^2.1.0" - supports-color "^6.1.0" + archy "^1.0.0" + cross-spawn "^7.0.0" + istanbul-lib-coverage "^3.0.0-alpha.1" + make-dir "^3.0.0" + p-map "^3.0.0" + rimraf "^3.0.0" + uuid "^3.3.3" + +istanbul-lib-report@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz#7518fe52ea44de372f460a76b5ecda9ffb73d8a6" + integrity sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw== + dependencies: + istanbul-lib-coverage "^3.0.0" + make-dir "^3.0.0" + supports-color "^7.1.0" -istanbul-lib-source-maps@^3.0.6: - version "3.0.6" - resolved "https://registry.yarnpkg.com/istanbul-lib-source-maps/-/istanbul-lib-source-maps-3.0.6.tgz#284997c48211752ec486253da97e3879defba8c8" - integrity sha512-R47KzMtDJH6X4/YW9XTx+jrLnZnscW4VpNN+1PViSYTejLVPWv7oov+Duf8YQSPyVRUvueQqz1TcsC6mooZTXw== +istanbul-lib-source-maps@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.0.tgz#75743ce6d96bb86dc7ee4352cf6366a23f0b1ad9" + integrity sha512-c16LpFRkR8vQXyHZ5nLpY35JZtzj1PQY1iZmesUbf1FZHbIupcWfjgOXBY9YHkLEQ6puz1u4Dgj6qmU/DisrZg== dependencies: debug "^4.1.1" - istanbul-lib-coverage "^2.0.5" - make-dir "^2.1.0" - rimraf "^2.6.3" + istanbul-lib-coverage "^3.0.0" source-map "^0.6.1" -istanbul-reports@^2.2.4: - version "2.2.6" - resolved "https://registry.yarnpkg.com/istanbul-reports/-/istanbul-reports-2.2.6.tgz#7b4f2660d82b29303a8fe6091f8ca4bf058da1af" - integrity sha512-SKi4rnMyLBKe0Jy2uUdx28h8oG7ph2PPuQPvIAh31d+Ci+lSiEu4C+h3oBPuJ9+mPKhOyW0M8gY4U5NM1WLeXA== +istanbul-reports@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/istanbul-reports/-/istanbul-reports-3.0.2.tgz#d593210e5000683750cb09fc0644e4b6e27fd53b" + integrity sha512-9tZvz7AiR3PEDNGiV9vIouQ/EAcqMXFmkcA1CDFTwOB98OZVDL0PH9glHotf5Ugp6GCOTypfzGWI/OqjWNCRUw== dependencies: - handlebars "^4.1.2" + html-escaper "^2.0.0" + istanbul-lib-report "^3.0.0" + +jmespath@0.15.0: + version "0.15.0" + resolved "https://registry.yarnpkg.com/jmespath/-/jmespath-0.15.0.tgz#a3f222a9aae9f966f5d27c796510e28091764217" + integrity sha1-o/Iiqarp+Wb10nx5ZRDigJF2Qhc= joi-to-json-schema@5.1.0: version "5.1.0" @@ -5705,6 +5912,18 @@ jsesc@^2.5.1: resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-2.5.2.tgz#80564d2e483dacf6e8ef209650a67df3f0c283a4" integrity sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA== +json-bigint@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/json-bigint/-/json-bigint-0.3.0.tgz#0ccd912c4b8270d05f056fbd13814b53d3825b1e" + integrity sha1-DM2RLEuCcNBfBW+9E4FLU9OCWx4= + dependencies: + bignumber.js "^7.0.0" + +json-buffer@3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.0.tgz#5b1f397afc75d677bde8bcfc0e47e1f9a3d9a898" + integrity sha1-Wx85evx11ne96Lz8Dkfh+aPZqJg= + json-parse-better-errors@^1.0.0, json-parse-better-errors@^1.0.1, json-parse-better-errors@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz#bb867cfb3450e69107c131d1c514bab3dc8bcaa9" @@ -5737,6 +5956,13 @@ json5@^1.0.1: dependencies: minimist "^1.2.0" +json5@^2.1.2: + version "2.1.3" + resolved "https://registry.yarnpkg.com/json5/-/json5-2.1.3.tgz#c9b0f7fa9233bfe5807fe66fcf3a5617ed597d43" + integrity sha512-KXPvOm8K9IJKFM0bmdn8QXh7udDh1g/giieX0NLCaMnb4hEiVFqnop2ImTXCc5e0/oHz3LTqmHGtExn5hfMkOA== + dependencies: + minimist "^1.2.5" + jsonfile@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-4.0.0.tgz#8771aae0799b64076b76640fca058f9c10e33ecb" @@ -5749,6 +5975,22 @@ jsonparse@^1.2.0: resolved "https://registry.yarnpkg.com/jsonparse/-/jsonparse-1.3.1.tgz#3f4dae4a91fac315f71062f8521cc239f1366280" integrity sha1-P02uSpH6wxX3EGL4UhzCOfE2YoA= +jsonwebtoken@8.5.1, jsonwebtoken@^8.5.1: + version "8.5.1" + resolved "https://registry.yarnpkg.com/jsonwebtoken/-/jsonwebtoken-8.5.1.tgz#00e71e0b8df54c2121a1f26137df2280673bcc0d" + integrity sha512-XjwVfRS6jTMsqYs0EsuJ4LGxXV14zQybNd4L2r0UvbVnSF9Af8x7p5MzbJ90Ioz/9TI41/hTCvznF/loiSzn8w== + dependencies: + jws "^3.2.2" + lodash.includes "^4.3.0" + lodash.isboolean "^3.0.3" + lodash.isinteger "^4.0.4" + lodash.isnumber "^3.0.3" + lodash.isplainobject "^4.0.6" + lodash.isstring "^4.0.1" + lodash.once "^4.0.0" + ms "^2.1.1" + semver "^5.6.0" + jsprim@^1.2.2: version "1.4.1" resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-1.4.1.tgz#313e66bc1e5cc06e438bc1b7499c2e5c56acb6a2" @@ -5759,18 +6001,18 @@ jsprim@^1.2.2: json-schema "0.2.3" verror "1.10.0" -jsx-ast-utils@^2.1.0: - version "2.2.1" - resolved "https://registry.yarnpkg.com/jsx-ast-utils/-/jsx-ast-utils-2.2.1.tgz#4d4973ebf8b9d2837ee91a8208cc66f3a2776cfb" - integrity sha512-v3FxCcAf20DayI+uxnCuw795+oOIkVu6EnJ1+kSzhqqTZHNkTZ7B66ZgLp4oLJ/gbA64cI0B7WRoHZMSRdyVRQ== +jsx-ast-utils@^2.2.3: + version "2.2.3" + resolved "https://registry.yarnpkg.com/jsx-ast-utils/-/jsx-ast-utils-2.2.3.tgz#8a9364e402448a3ce7f14d357738310d9248054f" + integrity sha512-EdIHFMm+1BPynpKOpdPqiOsvnIrInRGJD7bzPZdPkjitQEqpdpUuFpq4T0npZFKTiB3RhWFdGN+oqOJIdhDhQA== dependencies: array-includes "^3.0.3" object.assign "^4.1.0" just-extend@^4.0.2: - version "4.0.2" - resolved "https://registry.yarnpkg.com/just-extend/-/just-extend-4.0.2.tgz#f3f47f7dfca0f989c55410a7ebc8854b07108afc" - integrity sha512-FrLwOgm+iXrPV+5zDU6Jqu4gCRXbWEQg2O3SKONsWE4w7AXFRkryS53bpWdaL9cNol+AmR3AEYz6kn+o0fCPnw== + version "4.1.0" + resolved "https://registry.yarnpkg.com/just-extend/-/just-extend-4.1.0.tgz#7278a4027d889601640ee0ce0e5a00b992467da4" + integrity sha512-ApcjaOdVTJ7y4r08xI5wIqpvwS48Q0PBG4DJROcEkH1f8MdAiNFyFxz3xoL0LWAVwjrwPYZdVHHxhRHcx/uGLA== jwa@^1.4.1: version "1.4.1" @@ -5781,7 +6023,29 @@ jwa@^1.4.1: ecdsa-sig-formatter "1.0.11" safe-buffer "^5.0.1" -jws@^3.0.0, jws@^3.1.4: +jwa@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/jwa/-/jwa-2.0.0.tgz#a7e9c3f29dae94027ebcaf49975c9345593410fc" + integrity sha512-jrZ2Qx916EA+fq9cEAeCROWPTfCwi1IVHqT2tapuqLEVVDKFDENFw1oL+MwrTvH6msKxsd1YTDVw6uKEcsrLEA== + dependencies: + buffer-equal-constant-time "1.0.1" + ecdsa-sig-formatter "1.0.11" + safe-buffer "^5.0.1" + +jwks-rsa@1.8.0: + version "1.8.0" + resolved "https://registry.yarnpkg.com/jwks-rsa/-/jwks-rsa-1.8.0.tgz#4b13d962f82128ed06e30c7cc9d3a04652c1ec7c" + integrity sha512-+HYROHD5fsYQCNrJ37RSr2NjbN2/V9YT+yVF3oJxLmPIZWrmp1SOl1hMw2RcuNh+LGA2bGZIhRKGiMjhQa/b7Q== + dependencies: + "@types/express-jwt" "0.0.42" + axios "^0.19.2" + debug "^4.1.0" + jsonwebtoken "^8.5.1" + limiter "^1.1.4" + lru-memoizer "^2.0.1" + ms "^2.1.2" + +jws@^3.2.2: version "3.2.2" resolved "https://registry.yarnpkg.com/jws/-/jws-3.2.2.tgz#001099f3639468c9414000e99995fa52fb478304" integrity sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA== @@ -5789,10 +6053,27 @@ jws@^3.0.0, jws@^3.1.4: jwa "^1.4.1" safe-buffer "^5.0.1" -jwt-decode@2.2.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/jwt-decode/-/jwt-decode-2.2.0.tgz#7d86bd56679f58ce6a84704a657dd392bba81a79" - integrity sha1-fYa9VmefWM5qhHBKZX3TkruoGnk= +jws@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/jws/-/jws-4.0.0.tgz#2d4e8cf6a318ffaa12615e9dec7e86e6c97310f4" + integrity sha512-KDncfTmOZoOMTFG4mBlG0qUIOlc03fmzH+ru6RgYVZhPkyiy/92Owlt/8UEN+a4TXR1FQetfIpJE8ApdvdVxTg== + dependencies: + jwa "^2.0.0" + safe-buffer "^5.0.1" + +kafkajs@1.12.0: + version "1.12.0" + resolved "https://registry.yarnpkg.com/kafkajs/-/kafkajs-1.12.0.tgz#50ad336baee95f3324af8ae8df6fadc96e07c613" + integrity sha512-Izkd9iFRgeeKaHEgVpGQH08ygzCbHSxTbnu8W3G3uiNaVjGibUTmTwjv1Qf2M8NORXcPfzwVyg6bBlVj4SKr9g== + dependencies: + long "^4.0.0" + +keyv@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/keyv/-/keyv-3.1.0.tgz#ecc228486f69991e49e9476485a5be1e8fc5c4d9" + integrity sha512-9ykJ/46SN/9KPM/sichzQ7OvXyGDYKGTaDlKMGCAlg2UK8KRy4jb0d8sFc+0Tt0YYnThq8X2RZgCg74RPxgcVA== + dependencies: + json-buffer "3.0.0" kind-of@^3.0.2, kind-of@^3.0.3, kind-of@^3.2.0: version "3.2.2" @@ -5814,26 +6095,21 @@ kind-of@^5.0.0: integrity sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw== kind-of@^6.0.0, kind-of@^6.0.2: - version "6.0.2" - resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.2.tgz#01146b36a6218e64e58f3a8d66de5d7fc6f6d051" - integrity sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA== + version "6.0.3" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.3.tgz#07c05034a6c349fa06e24fa35aa76db4580ce4dd" + integrity sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw== "kindof@>= 2.0.0 < 3": version "2.0.0" resolved "https://registry.yarnpkg.com/kindof/-/kindof-2.0.0.tgz#c335baf603a77cc37f8b406b73b6463fdbdf1abe" integrity sha1-wzW69gOnfMN/i0Brc7ZGP9vfGr4= -latest-version@^3.0.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/latest-version/-/latest-version-3.1.0.tgz#a205383fea322b33b5ae3b18abee0dc2f356ee15" - integrity sha1-ogU4P+oyKzO1rjsYq+4NwvNW7hU= +latest-version@^5.0.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/latest-version/-/latest-version-5.1.0.tgz#119dfe908fe38d15dfa43ecd13fa12ec8832face" + integrity sha512-weT+r0kTkRQdCdYCNtkMwWXQTMEswKrFBkm4ckQOMVhhqhIMI1UT2hMj+1iigIhgSZm5gTmrRXBNoGUgaTY1xA== dependencies: - package-json "^4.0.0" - -lazy-ass@1.6.0: - version "1.6.0" - resolved "https://registry.yarnpkg.com/lazy-ass/-/lazy-ass-1.6.0.tgz#7999655e8646c17f089fdd187d150d3324d54513" - integrity sha1-eZllXoZGwX8In90YfRUNMyTVRRM= + package-json "^6.3.0" lcid@^2.0.0: version "2.0.0" @@ -5842,26 +6118,27 @@ lcid@^2.0.0: dependencies: invert-kv "^2.0.0" -lerna@3.18.3: - version "3.18.3" - resolved "https://registry.yarnpkg.com/lerna/-/lerna-3.18.3.tgz#c94556e76f98df9c7ae4ed3bc0166117cc42cd13" - integrity sha512-Bnr/RjyDSVA2Vu+NArK7do4UIpyy+EShOON7tignfAekPbi7cNDnMMGgSmbCQdKITkqPACMfCMdyq0hJlg6n3g== - dependencies: - "@lerna/add" "3.18.0" - "@lerna/bootstrap" "3.18.0" - "@lerna/changed" "3.18.3" - "@lerna/clean" "3.18.0" - "@lerna/cli" "3.18.0" - "@lerna/create" "3.18.0" - "@lerna/diff" "3.18.0" - "@lerna/exec" "3.18.0" - "@lerna/import" "3.18.0" - "@lerna/init" "3.18.0" - "@lerna/link" "3.18.0" - "@lerna/list" "3.18.0" - "@lerna/publish" "3.18.3" - "@lerna/run" "3.18.0" - "@lerna/version" "3.18.3" +lerna@3.21.0: + version "3.21.0" + resolved "https://registry.yarnpkg.com/lerna/-/lerna-3.21.0.tgz#c81a0f8df45c6b7c9d3fc9fdcd0f846aca2375c6" + integrity sha512-ux8yOwQEgIXOZVUfq+T8nVzPymL19vlIoPbysOP3YA4hcjKlqQIlsjI/1ugBe6b4MF7W4iV5vS3gH9cGqBBc1A== + dependencies: + "@lerna/add" "3.21.0" + "@lerna/bootstrap" "3.21.0" + "@lerna/changed" "3.21.0" + "@lerna/clean" "3.21.0" + "@lerna/cli" "3.18.5" + "@lerna/create" "3.21.0" + "@lerna/diff" "3.21.0" + "@lerna/exec" "3.21.0" + "@lerna/import" "3.21.0" + "@lerna/info" "3.21.0" + "@lerna/init" "3.21.0" + "@lerna/link" "3.21.0" + "@lerna/list" "3.21.0" + "@lerna/publish" "3.21.0" + "@lerna/run" "3.21.0" + "@lerna/version" "3.21.0" import-local "^2.0.0" npmlog "^4.1.2" @@ -5873,62 +6150,24 @@ levn@^0.3.0, levn@~0.3.0: prelude-ls "~1.1.2" type-check "~0.3.2" +levn@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/levn/-/levn-0.4.1.tgz#ae4562c007473b932a6200d403268dd2fffc6ade" + integrity sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ== + dependencies: + prelude-ls "^1.2.1" + type-check "~0.4.0" + +limiter@^1.1.4: + version "1.1.5" + resolved "https://registry.yarnpkg.com/limiter/-/limiter-1.1.5.tgz#8f92a25b3b16c6131293a0cc834b4a838a2aa7c2" + integrity sha512-FWWMIEOxz3GwUI4Ts/IvgVy6LPvoMPgjMdQ185nN6psJyBJ4yOpzqm695/h5umdLJg2vW3GR5iG11MAkR2AzJA== + lines-and-columns@^1.1.6: version "1.1.6" resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.1.6.tgz#1c00c743b433cd0a4e80758f7b64a57440d9ff00" integrity sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA= -listr-silent-renderer@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/listr-silent-renderer/-/listr-silent-renderer-1.1.1.tgz#924b5a3757153770bf1a8e3fbf74b8bbf3f9242e" - integrity sha1-kktaN1cVN3C/Go4/v3S4u/P5JC4= - -listr-update-renderer@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/listr-update-renderer/-/listr-update-renderer-0.2.0.tgz#ca80e1779b4e70266807e8eed1ad6abe398550f9" - integrity sha1-yoDhd5tOcCZoB+ju0a1qvjmFUPk= - dependencies: - chalk "^1.1.3" - cli-truncate "^0.2.1" - elegant-spinner "^1.0.1" - figures "^1.7.0" - indent-string "^3.0.0" - log-symbols "^1.0.2" - log-update "^1.0.2" - strip-ansi "^3.0.1" - -listr-verbose-renderer@^0.4.0: - version "0.4.1" - resolved "https://registry.yarnpkg.com/listr-verbose-renderer/-/listr-verbose-renderer-0.4.1.tgz#8206f4cf6d52ddc5827e5fd14989e0e965933a35" - integrity sha1-ggb0z21S3cWCfl/RSYng6WWTOjU= - dependencies: - chalk "^1.1.3" - cli-cursor "^1.0.2" - date-fns "^1.27.2" - figures "^1.7.0" - -listr@0.12.0: - version "0.12.0" - resolved "https://registry.yarnpkg.com/listr/-/listr-0.12.0.tgz#6bce2c0f5603fa49580ea17cd6a00cc0e5fa451a" - integrity sha1-a84sD1YD+klYDqF81qAMwOX6RRo= - dependencies: - chalk "^1.1.3" - cli-truncate "^0.2.1" - figures "^1.7.0" - indent-string "^2.1.0" - is-promise "^2.1.0" - is-stream "^1.1.0" - listr-silent-renderer "^1.1.1" - listr-update-renderer "^0.2.0" - listr-verbose-renderer "^0.4.0" - log-symbols "^1.0.2" - log-update "^1.0.2" - ora "^0.2.3" - p-map "^1.1.1" - rxjs "^5.0.0-beta.11" - stream-to-observable "^0.1.0" - strip-ansi "^3.0.1" - load-json-file@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-1.1.0.tgz#956905708d58b4bab4c2261b04f59f31c99374c0" @@ -5976,7 +6215,7 @@ loader-runner@^2.4.0: resolved "https://registry.yarnpkg.com/loader-runner/-/loader-runner-2.4.0.tgz#ed47066bfe534d7e84c4c7b9998c2a75607d9357" integrity sha512-Jsmr89RcXGIwivFY21FcRrisYZfvLMTWx5kOLc+JTxtpBOG6xML0vzbc6SEQG2FO9/4Fc3wW4LVcB5DmGflaRw== -loader-utils@1.2.3, loader-utils@^1.0.2, loader-utils@^1.2.3: +loader-utils@1.2.3: version "1.2.3" resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-1.2.3.tgz#1ff5dc6911c9f0a062531a4c04b609406108c2c7" integrity sha512-fkpz8ejdnEMG3s37wGL07iSBDg99O9D5yflE9RGNH3hRdx9SOwYfnGYdZOUIZitN8E+E2vkq3MUMYMvPYl5ZZA== @@ -5985,6 +6224,15 @@ loader-utils@1.2.3, loader-utils@^1.0.2, loader-utils@^1.2.3: emojis-list "^2.0.0" json5 "^1.0.1" +loader-utils@^1.0.2, loader-utils@^1.2.3: + version "1.4.0" + resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-1.4.0.tgz#c579b5e34cb34b1a74edc6c1fb36bfa371d5a613" + integrity sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA== + dependencies: + big.js "^5.2.2" + emojis-list "^3.0.0" + json5 "^1.0.1" + locate-path@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-2.0.0.tgz#2b568b265eec944c6d9c0de9c3dbbbca0354cd8e" @@ -6040,6 +6288,16 @@ lodash.get@^4.4.2: resolved "https://registry.yarnpkg.com/lodash.get/-/lodash.get-4.4.2.tgz#2d177f652fa31e939b4438d5341499dfa3825e99" integrity sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk= +lodash.includes@^4.3.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/lodash.includes/-/lodash.includes-4.3.0.tgz#60bb98a87cb923c68ca1e51325483314849f553f" + integrity sha1-YLuYqHy5I8aMoeUTJUgzFISfVT8= + +lodash.isboolean@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz#6c2e171db2a257cd96802fd43b01b20d5f5870f6" + integrity sha1-bC4XHbKiV82WgC/UOwGyDV9YcPY= + lodash.isempty@^4.4.0: version "4.4.0" resolved "https://registry.yarnpkg.com/lodash.isempty/-/lodash.isempty-4.4.0.tgz#6f86cbedd8be4ec987be9aaf33c9684db1b31e7e" @@ -6050,27 +6308,37 @@ lodash.isfunction@^3.0.8, lodash.isfunction@^3.0.9: resolved "https://registry.yarnpkg.com/lodash.isfunction/-/lodash.isfunction-3.0.9.tgz#06de25df4db327ac931981d1bdb067e5af68d051" integrity sha512-AirXNj15uRIMMPihnkInB4i3NHeb4iBtNg9WRWuK2o31S+ePwwNmDPaTL3o7dTJ+VXNZim7rFs4rxN4YU1oUJw== +lodash.isinteger@^4.0.4: + version "4.0.4" + resolved "https://registry.yarnpkg.com/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz#619c0af3d03f8b04c31f5882840b77b11cd68343" + integrity sha1-YZwK89A/iwTDH1iChAt3sRzWg0M= + lodash.ismatch@^4.4.0: version "4.4.0" resolved "https://registry.yarnpkg.com/lodash.ismatch/-/lodash.ismatch-4.4.0.tgz#756cb5150ca3ba6f11085a78849645f188f85f37" integrity sha1-dWy1FQyjum8RCFp4hJZF8Yj4Xzc= +lodash.isnumber@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz#3ce76810c5928d03352301ac287317f11c0b1ffc" + integrity sha1-POdoEMWSjQM1IwGsKHMX8RwLH/w= + lodash.isobject@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/lodash.isobject/-/lodash.isobject-3.0.2.tgz#3c8fb8d5b5bf4bf90ae06e14f2a530a4ed935e1d" integrity sha1-PI+41bW/S/kK4G4U8qUwpO2TXh0= +lodash.isplainobject@^4.0.6: + version "4.0.6" + resolved "https://registry.yarnpkg.com/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz#7c526a52d89b45c45cc690b88163be0497f550cb" + integrity sha1-fFJqUtibRcRcxpC4gWO+BJf1UMs= + lodash.isstring@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/lodash.isstring/-/lodash.isstring-4.0.1.tgz#d527dfb5456eca7cc9bb95d5daeaf88ba54a5451" integrity sha1-1SfftUVuynzJu5XV2ur4i6VKVFE= -lodash.noop@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/lodash.noop/-/lodash.noop-3.0.1.tgz#38188f4d650a3a474258439b96ec45b32617133c" - integrity sha1-OBiPTWUKOkdCWEObluxFsyYXEzw= - -lodash.once@^4.1.1: +lodash.once@^4.0.0: version "4.1.1" resolved "https://registry.yarnpkg.com/lodash.once/-/lodash.once-4.1.1.tgz#0dd3971213c7c56df880977d504c88fb471a97ac" integrity sha1-DdOXEhPHxW34gJd9UEyI+0cal6w= @@ -6100,11 +6368,6 @@ lodash.templatesettings@^4.0.0: dependencies: lodash._reinterpolate "^3.0.0" -lodash.unescape@4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/lodash.unescape/-/lodash.unescape-4.0.1.tgz#bf2249886ce514cda112fae9218cdc065211fc9c" - integrity sha1-vyJJiGzlFM2hEvrpIYzcBlIR/Jw= - lodash.uniq@^4.5.0: version "4.5.0" resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773" @@ -6117,44 +6380,36 @@ lodash.uniq@^4.5.0: dependencies: lodash._createwrapper "^3.0.0" -lodash@4.17.15, lodash@^4.17.10, lodash@^4.17.11, lodash@^4.17.12, lodash@^4.17.13, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.2.1: +lodash@^4.17.11, lodash@^4.17.12, lodash@^4.17.13, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.2.1: version "4.17.15" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.15.tgz#b447f6670a0455bbfeedd11392eff330ea097548" integrity sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A== -log-symbols@2.2.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-2.2.0.tgz#5740e1c5d6f0dfda4ad9323b5332107ef6b4c40a" - integrity sha512-VeIAFslyIerEJLXHziedo2basKbMKtTw3vfn5IzG0XTjhAVEJyNHnL2p7vc+wBDSdQuUpNw3M2u6xb9QsAY5Eg== - dependencies: - chalk "^2.0.1" - -log-symbols@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-1.0.2.tgz#376ff7b58ea3086a0f09facc74617eca501e1a18" - integrity sha1-N2/3tY6jCGoPCfrMdGF+ylAeGhg= - dependencies: - chalk "^1.0.0" - -log-update@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/log-update/-/log-update-1.0.2.tgz#19929f64c4093d2d2e7075a1dad8af59c296b8d1" - integrity sha1-GZKfZMQJPS0ucHWh2tivWcKWuNE= +log-symbols@3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-3.0.0.tgz#f3a08516a5dea893336a7dee14d18a1cfdab77c4" + integrity sha512-dSkNGuI7iG3mfvDzUuYZyvk5dD9ocYCYzNU6CYDE6+Xqd+gwme6Z00NS3dUh8mq/73HaEtT7m6W+yUPtU6BZnQ== dependencies: - ansi-escapes "^1.0.0" - cli-cursor "^1.0.2" - -lolex@^4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/lolex/-/lolex-4.1.0.tgz#ecdd7b86539391d8237947a3419aa8ac975f0fe1" - integrity sha512-BYxIEXiVq5lGIXeVHnsFzqa1TxN5acnKnPCdlZSpzm8viNEOhiigupA4vTQ9HEFQ6nLTQ9wQOgBknJgzUYQ9Aw== + chalk "^2.4.2" lolex@^4.2.0: version "4.2.0" resolved "https://registry.yarnpkg.com/lolex/-/lolex-4.2.0.tgz#ddbd7f6213ca1ea5826901ab1222b65d714b3cd7" integrity sha512-gKO5uExCXvSm6zbF562EvM+rd1kQDnB9AZBbiQVzf1ZmdDpxUSvpnAaVOP83N/31mRK8Ml8/VE8DMvsAZQ+7wg== -loose-envify@^1.1.0, loose-envify@^1.4.0: +lolex@^5.0.1: + version "5.1.2" + resolved "https://registry.yarnpkg.com/lolex/-/lolex-5.1.2.tgz#953694d098ce7c07bc5ed6d0e42bc6c0c6d5a367" + integrity sha512-h4hmjAvHTmd+25JSwrtTIuwbKdwg5NzZVRMLn9saij4SZaepCrTCxPr35H/3bjwfMJtN+t3CX8672UIkglz28A== + dependencies: + "@sinonjs/commons" "^1.7.0" + +long@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/long/-/long-4.0.0.tgz#9a7b71cfb7d361a194ea555241c92f7468d5bf28" + integrity sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA== + +loose-envify@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf" integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q== @@ -6169,26 +6424,39 @@ loud-rejection@^1.0.0: currently-unhandled "^0.4.1" signal-exit "^3.0.0" -lowercase-keys@^1.0.0: +lowercase-keys@^1.0.0, lowercase-keys@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-1.0.1.tgz#6f9e30b47084d971a7c820ff15a6c5167b74c26f" integrity sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA== -lru-cache@^4.0.1: - version "4.1.5" - resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-4.1.5.tgz#8bbe50ea85bed59bc9e33dcab8235ee9bcf443cd" - integrity sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g== - dependencies: - pseudomap "^1.0.2" - yallist "^2.1.2" +lowercase-keys@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-2.0.0.tgz#2603e78b7b4b0006cbca2fbcc8a3202558ac9479" + integrity sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA== -lru-cache@^5.1.1: +lru-cache@^5.0.0, lru-cache@^5.1.1: version "5.1.1" resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-5.1.1.tgz#1da27e6710271947695daf6848e847f01d84b920" integrity sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w== dependencies: yallist "^3.0.2" +lru-cache@~4.0.0: + version "4.0.2" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-4.0.2.tgz#1d17679c069cda5d040991a09dbc2c0db377e55e" + integrity sha1-HRdnnAac2l0ECZGgnbwsDbN35V4= + dependencies: + pseudomap "^1.0.1" + yallist "^2.0.0" + +lru-memoizer@^2.0.1: + version "2.1.2" + resolved "https://registry.yarnpkg.com/lru-memoizer/-/lru-memoizer-2.1.2.tgz#5c6b43659c78ad0e9e65bf81a9e5ef1ee109a2dd" + integrity sha512-N5L5xlnVcbIinNn/TJ17vHBZwBMt9t7aJDz2n97moWubjNl6VO9Ao2XuAGBBddkYdjrwR9HfzXbT6NfMZXAZ/A== + dependencies: + lodash.clonedeep "^4.5.0" + lru-cache "~4.0.0" + macos-release@^2.2.0: version "2.3.0" resolved "https://registry.yarnpkg.com/macos-release/-/macos-release-2.3.0.tgz#eb1930b036c0800adebccd5f17bc4c12de8bb71f" @@ -6209,21 +6477,28 @@ make-dir@^2.0.0, make-dir@^2.1.0: pify "^4.0.1" semver "^5.6.0" +make-dir@^3.0.0, make-dir@^3.0.2: + version "3.1.0" + resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-3.1.0.tgz#415e967046b3a7f1d185277d84aa58203726a13f" + integrity sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw== + dependencies: + semver "^6.0.0" + make-error@^1.1.1: - version "1.3.5" - resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.5.tgz#efe4e81f6db28cadd605c70f29c831b58ef776c8" - integrity sha512-c3sIjNUow0+8swNwVpqoH4YCShKNFkMaw6oH1mNS2haDZQqkeZFlHS3dhoeEbKKmJB4vXpJucU6oH75aDYeE9g== + version "1.3.6" + resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.6.tgz#2eb2e37ea9b67c4891f684a1394799af484cf7a2" + integrity sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw== make-fetch-happen@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/make-fetch-happen/-/make-fetch-happen-5.0.0.tgz#a8e3fe41d3415dd656fe7b8e8172e1fb4458b38d" - integrity sha512-nFr/vpL1Jc60etMVKeaLOqfGjMMb3tAHFVJWxHOFCFS04Zmd7kGlMxo0l1tzfhoQje0/UPnd0X8OeGUiXXnfPA== + version "5.0.2" + resolved "https://registry.yarnpkg.com/make-fetch-happen/-/make-fetch-happen-5.0.2.tgz#aa8387104f2687edca01c8687ee45013d02d19bd" + integrity sha512-07JHC0r1ykIoruKO8ifMXu+xEU8qOXDFETylktdug6vJDACnP+HKevOu3PXyNPzFyTSlz8vrBYlBO1JZRe8Cag== dependencies: agentkeepalive "^3.4.1" cacache "^12.0.0" http-cache-semantics "^3.8.1" http-proxy-agent "^2.1.0" - https-proxy-agent "^2.2.1" + https-proxy-agent "^2.2.3" lru-cache "^5.1.1" mississippi "^3.0.0" node-fetch-npm "^2.0.2" @@ -6231,11 +6506,6 @@ make-fetch-happen@^5.0.0: socks-proxy-agent "^4.0.0" ssri "^6.0.0" -mamacro@^0.0.3: - version "0.0.3" - resolved "https://registry.yarnpkg.com/mamacro/-/mamacro-0.0.3.tgz#ad2c9576197c9f1abf308d0787865bd975a3f3e4" - integrity sha512-qMEwh+UujcQ+kbz3T6V+wAmO2U8veoq2w+3wY8MquqwVA3jChfwY+Tk52GZKDfACEPjuZ7r2oJLejwpt8jtwTA== - map-age-cleaner@^0.1.1: version "0.1.3" resolved "https://registry.yarnpkg.com/map-age-cleaner/-/map-age-cleaner-0.1.3.tgz#7d583a7306434c055fe474b0f45078e6e1b4b92a" @@ -6258,6 +6528,11 @@ map-obj@^2.0.0: resolved "https://registry.yarnpkg.com/map-obj/-/map-obj-2.0.0.tgz#a65cd29087a92598b8791257a523e021222ac1f9" integrity sha1-plzSkIepJZi4eRJXpSPgISIqwfk= +map-obj@^4.0.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/map-obj/-/map-obj-4.1.0.tgz#b91221b542734b9f14256c0132c897c5d7256fd5" + integrity sha512-glc9y00wgtwcDmp7GaE/0b0OnxpNJsVf3ael/An6Fe2Q51LLwN1er6sdomLRzz5h0+yMpiYLhWYF5R7HeqVd4g== + map-values@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/map-values/-/map-values-1.0.1.tgz#768b8e79c009bf2b64fee806e22a7b1c4190c990" @@ -6301,6 +6576,14 @@ memory-fs@^0.4.0, memory-fs@^0.4.1: errno "^0.1.3" readable-stream "^2.0.1" +memory-fs@^0.5.0: + version "0.5.0" + resolved "https://registry.yarnpkg.com/memory-fs/-/memory-fs-0.5.0.tgz#324c01288b88652966d161db77838720845a8e3c" + integrity sha512-jA0rdU5KoQMC0e6ppoNRtpp6vjFq6+NY7r8hywnC7V+1Xj/MtHwGIbB1QaK/dunyjWteJzmkpd7ooeWg10T7GA== + dependencies: + errno "^0.1.3" + readable-stream "^2.0.1" + meow@^3.3.0: version "3.7.0" resolved "https://registry.yarnpkg.com/meow/-/meow-3.7.0.tgz#72cb668b425228290abbfa856892587308a801fb" @@ -6332,18 +6615,30 @@ meow@^4.0.0: redent "^2.0.0" trim-newlines "^2.0.0" +meow@^7.0.0: + version "7.0.1" + resolved "https://registry.yarnpkg.com/meow/-/meow-7.0.1.tgz#1ed4a0a50b3844b451369c48362eb0515f04c1dc" + integrity sha512-tBKIQqVrAHqwit0vfuFPY3LlzJYkEOFyKa3bPgxzNl6q/RtN8KQ+ALYEASYuFayzSAsjlhXj/JZ10rH85Q6TUw== + dependencies: + "@types/minimist" "^1.2.0" + arrify "^2.0.1" + camelcase "^6.0.0" + camelcase-keys "^6.2.2" + decamelize-keys "^1.1.0" + hard-rejection "^2.1.0" + minimist-options "^4.0.2" + normalize-package-data "^2.5.0" + read-pkg-up "^7.0.1" + redent "^3.0.0" + trim-newlines "^3.0.0" + type-fest "^0.13.1" + yargs-parser "^18.1.3" + merge-descriptors@1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61" integrity sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E= -merge-source-map@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/merge-source-map/-/merge-source-map-1.1.0.tgz#2fdde7e6020939f70906a68f2d7ae685e4c8c646" - integrity sha512-Qkcp7P2ygktpMPh2mCQZaf3jhN6D3Z/qVZHSdWvQ+2Ef5HgRAPBO57A77+ENm0CPx2+1Ce/MYKi3ymqdfuqibw== - dependencies: - source-map "^0.6.1" - merge2@^1.2.3: version "1.3.0" resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.3.0.tgz#5b366ee83b2f1582c48f87e47cf1a9352103ca81" @@ -6389,33 +6684,53 @@ miller-rabin@^4.0.0: bn.js "^4.0.0" brorand "^1.0.1" -mime-db@1.40.0: - version "1.40.0" - resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.40.0.tgz#a65057e998db090f732a68f6c276d387d4126c32" - integrity sha512-jYdeOMPy9vnxEqFRRo6ZvTZ8d9oPb+k18PKoYNYUe2stVEBPPwsln/qWzdbmaIvnhZ9v2P+CuecK+fpUfsV2mA== +mime-db@1.44.0: + version "1.44.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.44.0.tgz#fa11c5eb0aca1334b4233cb4d52f10c5a6272f92" + integrity sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg== mime-types@^2.1.12, mime-types@~2.1.19, mime-types@~2.1.24: - version "2.1.24" - resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.24.tgz#b6f8d0b3e951efb77dedeca194cff6d16f676f81" - integrity sha512-WaFHS3MCl5fapm3oLxU4eYDw77IQM2ACcxQ9RIxfaC3ooc6PFuBMGZZsYpvoXS5D5QTWPieo1jjLdAm3TBP3cQ== + version "2.1.27" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.27.tgz#47949f98e279ea53119f5722e0f34e529bec009f" + integrity sha512-JIhqnCasI9yD+SsmkquHBxTSEuZdQX5BuQnS2Vc7puQQQ+8yiP5AY5uWhpdv4YL4VM5c6iliiYWPgJ/nJQLp7w== dependencies: - mime-db "1.40.0" + mime-db "1.44.0" mime@1.6.0, mime@^1.4.1: version "1.6.0" resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1" integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg== +mime@^2.2.0: + version "2.4.5" + resolved "https://registry.yarnpkg.com/mime/-/mime-2.4.5.tgz#d8de2ecb92982dedbb6541c9b6841d7f218ea009" + integrity sha512-3hQhEUF027BuxZjQA3s7rIv/7VCQPa27hN9u9g87sEkWaKwQPuXOkVKtOeiyUrnWqTDiOs8Ed2rwg733mB0R5w== + mimic-fn@^1.0.0: version "1.2.0" resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-1.2.0.tgz#820c86a39334640e99516928bd03fca88057d022" integrity sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ== -mimic-fn@^2.0.0: +mimic-fn@^2.0.0, mimic-fn@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== +mimic-response@^1.0.0, mimic-response@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-1.0.1.tgz#4923538878eef42063cb8a3e3b0798781487ab1b" + integrity sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ== + +mimic-response@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-2.1.0.tgz#d13763d35f613d09ec37ebb30bac0469c0ee8f43" + integrity sha512-wXqjST+SLt7R009ySCglWBCFpjUygmCIfD790/kVbiGmUgfYGuB14PiTd5DwVxSV4NcYHjzMkoj5LjQZwTQLEA== + +min-indent@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/min-indent/-/min-indent-1.0.0.tgz#cfc45c37e9ec0d8f0a0ec3dd4ef7f7c3abe39256" + integrity sha1-z8RcN+nsDY8KDsPdTvf3w6vjklY= + minimalistic-assert@^1.0.0, minimalistic-assert@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz#2e194de044626d4a10e7f7fbc00ce73e83e4d5c7" @@ -6441,30 +6756,20 @@ minimist-options@^3.0.1: arrify "^1.0.1" is-plain-obj "^1.1.0" -minimist@0.0.8: - version "0.0.8" - resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d" - integrity sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0= - -minimist@1.2.0, minimist@^1.1.3, minimist@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284" - integrity sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ= - -minimist@~0.0.1: - version "0.0.10" - resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.10.tgz#de3f98543dbf96082be48ad1a0c7cda836301dcf" - integrity sha1-3j+YVD2/lggr5IrRoMfNqDYwHc8= - -minipass@^2.2.1, minipass@^2.3.5: - version "2.3.5" - resolved "https://registry.yarnpkg.com/minipass/-/minipass-2.3.5.tgz#cacebe492022497f656b0f0f51e2682a9ed2d848" - integrity sha512-Gi1W4k059gyRbyVUZQ4mEqLm0YIUiGYfvxhF6SIlk3ui1WVxMTGfGdQ2SInh3PDrRTVvPKgULkpJtT4RH10+VA== +minimist-options@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/minimist-options/-/minimist-options-4.0.2.tgz#29c4021373ded40d546186725e57761e4b1984a7" + integrity sha512-seq4hpWkYSUh1y7NXxzucwAN9yVlBc3Upgdjz8vLCP97jG8kaOmzYrVH/m7tQ1NYD1wdtZbSLfdy4zFmRWuc/w== dependencies: - safe-buffer "^5.1.2" - yallist "^3.0.0" + arrify "^1.0.1" + is-plain-obj "^1.1.0" -minipass@^2.8.6: +minimist@^1.1.3, minimist@^1.2.0, minimist@^1.2.5: + version "1.2.5" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602" + integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw== + +minipass@^2.3.5, minipass@^2.6.0, minipass@^2.8.6, minipass@^2.9.0: version "2.9.0" resolved "https://registry.yarnpkg.com/minipass/-/minipass-2.9.0.tgz#e713762e7d3e32fed803115cf93e04bca9fcc9a6" integrity sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg== @@ -6472,12 +6777,27 @@ minipass@^2.8.6: safe-buffer "^5.1.2" yallist "^3.0.0" +minipass@^3.0.0: + version "3.1.3" + resolved "https://registry.yarnpkg.com/minipass/-/minipass-3.1.3.tgz#7d42ff1f39635482e15f9cdb53184deebd5815fd" + integrity sha512-Mgd2GdMVzY+x3IJ+oHnVM+KG3lA5c8tnabyJKmHSaG2kAGpudxuOf8ToDkhumF7UzME7DecbQE9uOZhNm7PuJg== + dependencies: + yallist "^4.0.0" + minizlib@^1.2.1: - version "1.2.1" - resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-1.2.1.tgz#dd27ea6136243c7c880684e8672bb3a45fd9b614" - integrity sha512-7+4oTUOWKg7AuL3vloEWekXY2/D20cevzsrNT2kGWm+39J9hGTCBv8VI5Pm5lXZ/o3/mdR4f8rflAPhnQb8mPA== + version "1.3.3" + resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-1.3.3.tgz#2290de96818a34c29551c8a8d301216bd65a861d" + integrity sha512-6ZYMOEnmVsdCeTJVE0W9ZD+pVnE8h9Hma/iOwwRDsdQoePpoX56/8B6z3P9VNwppJuBKNRuFDRNRqRWexT9G9Q== + dependencies: + minipass "^2.9.0" + +minizlib@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-2.1.0.tgz#fd52c645301ef09a63a2c209697c294c6ce02cf3" + integrity sha512-EzTZN/fjSvifSX0SlqUERCN39o6T40AMarPbv0MrarSFtIITCBh7bi+dU8nxGFHuqs9jdIAeoYoKuQAAASsPPA== dependencies: - minipass "^2.2.1" + minipass "^3.0.0" + yallist "^4.0.0" mississippi@^3.0.0: version "3.0.0" @@ -6503,6 +6823,11 @@ mixin-deep@^1.2.0: for-in "^1.0.2" is-extendable "^1.0.1" +mkdirp-classic@^0.5.2: + version "0.5.3" + resolved "https://registry.yarnpkg.com/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz#fa10c9115cc6d8865be221ba47ee9bed78601113" + integrity sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A== + mkdirp-promise@^5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/mkdirp-promise/-/mkdirp-promise-5.0.1.tgz#e9b8f68e552c68a9c1713b84883f7a1dd039b8a1" @@ -6510,20 +6835,26 @@ mkdirp-promise@^5.0.1: dependencies: mkdirp "*" -mkdirp@*, mkdirp@0.5.1, mkdirp@^0.5.0, mkdirp@^0.5.1: - version "0.5.1" - resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903" - integrity sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM= +mkdirp@*, mkdirp@^1.0.3: + version "1.0.4" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e" + integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== + +mkdirp@0.5.5, mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@^0.5.3: + version "0.5.5" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.5.tgz#d91cefd62d1436ca0f41620e251288d420099def" + integrity sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ== dependencies: - minimist "0.0.8" + minimist "^1.2.5" -mocha@6.2.2: - version "6.2.2" - resolved "https://registry.yarnpkg.com/mocha/-/mocha-6.2.2.tgz#5d8987e28940caf8957a7d7664b910dc5b2fea20" - integrity sha512-FgDS9Re79yU1xz5d+C4rv1G7QagNGHZ+iXF81hO8zY35YZZcLEsJVfFolfsqKFWunATEvNzMK0r/CwWd/szO9A== +mocha@7.1.2: + version "7.1.2" + resolved "https://registry.yarnpkg.com/mocha/-/mocha-7.1.2.tgz#8e40d198acf91a52ace122cd7599c9ab857b29e6" + integrity sha512-o96kdRKMKI3E8U0bjnfqW4QMk12MwZ4mhdBTf+B5a1q9+aq2HRnj+3ZdJu0B/ZhJeK78MgYuv6L8d/rA5AeBJA== dependencies: ansi-colors "3.2.3" browser-stdout "1.3.1" + chokidar "3.3.0" debug "3.2.6" diff "3.5.0" escape-string-regexp "1.0.5" @@ -6532,54 +6863,53 @@ mocha@6.2.2: growl "1.10.5" he "1.2.0" js-yaml "3.13.1" - log-symbols "2.2.0" + log-symbols "3.0.0" minimatch "3.0.4" - mkdirp "0.5.1" + mkdirp "0.5.5" ms "2.1.1" - node-environment-flags "1.0.5" + node-environment-flags "1.0.6" object.assign "4.1.0" strip-json-comments "2.0.1" supports-color "6.0.0" which "1.3.1" wide-align "1.1.3" - yargs "13.3.0" - yargs-parser "13.1.1" + yargs "13.3.2" + yargs-parser "13.1.2" yargs-unparser "1.6.0" -mochawesome-report-generator@^4.0.0: - version "4.0.1" - resolved "https://registry.yarnpkg.com/mochawesome-report-generator/-/mochawesome-report-generator-4.0.1.tgz#0a010d1ecf379eb26ba05300feb59e2665076080" - integrity sha512-hQbmQt8/yCT68GjrQFat+Diqeuka3haNllexYfja1+y0hpwi3yCJwFpQCdWK9ezzcXL3Nu80f2I6SZeyspwsqg== +mochawesome-report-generator@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/mochawesome-report-generator/-/mochawesome-report-generator-5.1.0.tgz#b8809e7661ac31732fa7ae7210380f704f7c68f6" + integrity sha512-5cI4Jh+sD+jIxc7q94961vnm/6VKDI7TFUPt9dps6oAc4y4WMpEeeOlmgKKM81q2eGaviNUYw+acFalGK6EJ9g== dependencies: chalk "^2.4.2" dateformat "^3.0.2" + escape-html "^1.0.3" fs-extra "^7.0.0" fsu "^1.0.2" lodash.isfunction "^3.0.8" opener "^1.4.2" prop-types "^15.7.2" - react "^16.8.5" - react-dom "^16.8.5" tcomb "^3.2.17" tcomb-validation "^3.3.0" validator "^10.11.0" yargs "^13.2.2" -mochawesome@4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/mochawesome/-/mochawesome-4.1.0.tgz#57cdb9509a9fc54790884ec867e109644ba949ee" - integrity sha512-U23K19mLqmuBqFyIBl7FVkcIuG/2JYStCj+91WmxK1/psLgHlWBEZsNe25U0x4t1Eqgu55aHv+0utLwzfhnupw== +mochawesome@6.1.1: + version "6.1.1" + resolved "https://registry.yarnpkg.com/mochawesome/-/mochawesome-6.1.1.tgz#9155ac13edb42c9eb17a7adbbcbbb5c6ecefb972" + integrity sha512-fRGZkSJI+u4ODRQCFg3JqI4pqZGzOpfZqlEjvUoZ78l2JnBWwepPu7dMTAcyAEUsZbTSx7S4eVnsi+/Zh1Qq6A== dependencies: - chalk "^2.4.1" + chalk "^4.0.0" diff "^4.0.1" json-stringify-safe "^5.0.1" lodash.isempty "^4.4.0" lodash.isfunction "^3.0.9" lodash.isobject "^3.0.2" lodash.isstring "^4.0.1" - mochawesome-report-generator "^4.0.0" - strip-ansi "^5.0.0" - uuid "^3.3.2" + mochawesome-report-generator "^5.1.0" + strip-ansi "^6.0.0" + uuid "^7.0.3" mockdate@2.0.5: version "2.0.5" @@ -6591,28 +6921,28 @@ modify-values@^1.0.0: resolved "https://registry.yarnpkg.com/modify-values/-/modify-values-1.0.1.tgz#b3939fa605546474e3e3e3c63d64bd43b4ee6022" integrity sha512-xV2bxeN6F7oYjZWTe/YPAy6MN2M+sL4u/Rlm2AHCIVGfo2p1yGmBHQ6vHehl4bRTZBdHu3TSkWdYgkwpYzAGSw== -moment-timezone@0.5.27: - version "0.5.27" - resolved "https://registry.yarnpkg.com/moment-timezone/-/moment-timezone-0.5.27.tgz#73adec8139b6fe30452e78f210f27b1f346b8877" - integrity sha512-EIKQs7h5sAsjhPCqN6ggx6cEbs94GK050254TIJySD1bzoM5JTYDwAU1IoVOeTOL6Gm27kYJ51/uuvq1kIlrbw== +moment-timezone@0.5.28: + version "0.5.28" + resolved "https://registry.yarnpkg.com/moment-timezone/-/moment-timezone-0.5.28.tgz#f093d789d091ed7b055d82aa81a82467f72e4338" + integrity sha512-TDJkZvAyKIVWg5EtVqRzU97w0Rb0YVbfpqyjgu6GwXCAohVRqwZjf4fOzDE6p1Ch98Sro/8hQQi65WDXW5STPw== dependencies: moment ">= 2.9.0" -moment@2.24.0, "moment@>= 2.9.0", moment@>=2.14.0: - version "2.24.0" - resolved "https://registry.yarnpkg.com/moment/-/moment-2.24.0.tgz#0d055d53f5052aa653c9f6eb68bb5d12bf5c2b5b" - integrity sha512-bV7f+6l2QigeBBZSM/6yTNq4P2fNpSWj/0e7jQcy87A8e7o2nAfP/34/2ky5Vw4B9S446EtIhodAzkFCcR4dQg== +"moment@>= 2.9.0", moment@>=2.14.0: + version "2.25.3" + resolved "https://registry.yarnpkg.com/moment/-/moment-2.25.3.tgz#252ff41319cf41e47761a1a88cab30edfe9808c0" + integrity sha512-PuYv0PHxZvzc15Sp8ybUCoQ+xpyPWvjOuK72a5ovzp2LI32rJXOiIfyoFoYvG3s6EwwrdkMyWuRiEHSZRLJNdg== -morgan@1.9.1: - version "1.9.1" - resolved "https://registry.yarnpkg.com/morgan/-/morgan-1.9.1.tgz#0a8d16734a1d9afbc824b99df87e738e58e2da59" - integrity sha512-HQStPIV4y3afTiCYVxirakhlCfGkI161c76kKFca7Fk1JusM//Qeo1ej2XaMniiNeaZklMVrh3vTtIzpzwbpmA== +morgan@1.10.0: + version "1.10.0" + resolved "https://registry.yarnpkg.com/morgan/-/morgan-1.10.0.tgz#091778abc1fc47cd3509824653dae1faab6b17d7" + integrity sha512-AbegBVI4sh6El+1gNwvD5YIck7nSA36weD7xvIxG4in80j/UoK8AEGaWnnz8v1GxonMCltmlNs5ZKbGvl9b1XQ== dependencies: - basic-auth "~2.0.0" + basic-auth "~2.0.1" debug "2.6.9" - depd "~1.1.2" + depd "~2.0.0" on-finished "~2.3.0" - on-headers "~1.0.1" + on-headers "~1.0.2" move-concurrently@^1.0.1: version "1.0.1" @@ -6636,11 +6966,25 @@ ms@2.1.1: resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.1.tgz#30a5864eb3ebb0a66f2ebe6d727af06a09d86e0a" integrity sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg== -ms@^2.0.0, ms@^2.1.1: +ms@^2.0.0, ms@^2.1.1, ms@^2.1.2: version "2.1.2" resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== +multer@1.4.2: + version "1.4.2" + resolved "https://registry.yarnpkg.com/multer/-/multer-1.4.2.tgz#2f1f4d12dbaeeba74cb37e623f234bf4d3d2057a" + integrity sha512-xY8pX7V+ybyUpbYMxtjM9KAiD9ixtg5/JkeKUTD6xilfDv0vzzOFcCp4Ljb1UU3tSOM3VTZtKo63OmzOrGi3Cg== + dependencies: + append-field "^1.0.0" + busboy "^0.2.11" + concat-stream "^1.5.2" + mkdirp "^0.5.1" + object-assign "^4.1.1" + on-finished "^2.3.0" + type-is "^1.6.4" + xtend "^4.0.0" + multimatch@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/multimatch/-/multimatch-3.0.0.tgz#0e2534cc6bc238d9ab67e1b9cd5fcd85a6dbf70b" @@ -6667,12 +7011,12 @@ mute-stream@0.0.7: resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.7.tgz#3075ce93bc21b8fab43e1bc4da7e8115ed1e7bab" integrity sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s= -mute-stream@~0.0.4: +mute-stream@0.0.8, mute-stream@~0.0.4: version "0.0.8" resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.8.tgz#1630c42b2251ff81e2a283de96a5497ea92e5e0d" integrity sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA== -mz@^2.5.0: +mz@^2.4.0, mz@^2.5.0: version "2.7.0" resolved "https://registry.yarnpkg.com/mz/-/mz-2.7.0.tgz#95008057a56cafadc2bc63dde7f9ff6955948e32" integrity sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q== @@ -6682,9 +7026,9 @@ mz@^2.5.0: thenify-all "^1.0.0" nan@^2.12.1: - version "2.14.0" - resolved "https://registry.yarnpkg.com/nan/-/nan-2.14.0.tgz#7818f722027b2459a86f0295d434d1fc2336c52c" - integrity sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg== + version "2.14.1" + resolved "https://registry.yarnpkg.com/nan/-/nan-2.14.1.tgz#d7be34dfa3105b91494c3147089315eff8874b01" + integrity sha512-isWHgVjnFjh2x2yuJ/tj3JbwoHu3UC2dX5G/88Cm24yB6YopVgxvBObDY7n5xW6ExmFhJpSEQqFPvq9zaXc8Jw== nanomatch@^1.2.9: version "1.2.13" @@ -6703,20 +7047,16 @@ nanomatch@^1.2.9: snapdragon "^0.8.1" to-regex "^3.0.1" +napi-build-utils@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/napi-build-utils/-/napi-build-utils-1.0.2.tgz#b1fddc0b2c46e380a0b7a76f984dd47c41a13806" + integrity sha512-ONmRUqK7zj7DWX0D9ADe03wbwOBZxNAfF20PlGfCWQcD3+/MakShIHrMqx9YwPTfxDdF1zLeL+RGZiR9kGMLdg== + natural-compare@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" integrity sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc= -needle@^2.2.1: - version "2.4.0" - resolved "https://registry.yarnpkg.com/needle/-/needle-2.4.0.tgz#6833e74975c444642590e15a750288c5f939b57c" - integrity sha512-4Hnwzr3mi5L97hMYeNl8wRW/Onhy4nUKR/lVemJ8gJedxxUyBLm9kkrDColJvoSfwi0jCNhD+xCdOtiGDQiRZg== - dependencies: - debug "^3.2.6" - iconv-lite "^0.4.4" - sax "^1.2.4" - negotiator@0.6.2: version "0.6.2" resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.2.tgz#feacf7ccf525a77ae9634436a64883ffeca346fb" @@ -6727,39 +7067,57 @@ neo-async@^2.5.0, neo-async@^2.6.0, neo-async@^2.6.1: resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.1.tgz#ac27ada66167fa8849a6addd837f6b189ad2081c" integrity sha512-iyam8fBuCUpWeKPGpaNMetEocMt364qkCsfL9JuhjXX6dRnguRVOfk2GZaDpPjcOKiiXCPINZC1GczQ7iTq3Zw== -nested-error-stacks@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/nested-error-stacks/-/nested-error-stacks-2.1.0.tgz#0fbdcf3e13fe4994781280524f8b96b0cdff9c61" - integrity sha512-AO81vsIO1k1sM4Zrd6Hu7regmJN1NSiAja10gc4bX3F0wd+9rQmcuHQaHVQCYIEC8iFXnE+mavh23GOt7wBgug== - nice-try@^1.0.4: version "1.0.5" resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366" integrity sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ== nise@^1.5.2: - version "1.5.2" - resolved "https://registry.yarnpkg.com/nise/-/nise-1.5.2.tgz#b6d29af10e48b321b307e10e065199338eeb2652" - integrity sha512-/6RhOUlicRCbE9s+94qCUsyE+pKlVJ5AhIv+jEE7ESKwnbXqulKZ1FYU+XAtHHWE9TinYvAxDUJAb912PwPoWA== + version "1.5.3" + resolved "https://registry.yarnpkg.com/nise/-/nise-1.5.3.tgz#9d2cfe37d44f57317766c6e9408a359c5d3ac1f7" + integrity sha512-Ymbac/94xeIrMf59REBPOv0thr+CJVFMhrlAkW/gjCIE58BGQdCj0x7KRCb3yz+Ga2Rz3E9XXSvUyyxqqhjQAQ== dependencies: "@sinonjs/formatio" "^3.2.1" "@sinonjs/text-encoding" "^0.7.1" just-extend "^4.0.2" - lolex "^4.1.0" + lolex "^5.0.1" path-to-regexp "^1.7.0" -node-environment-flags@1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/node-environment-flags/-/node-environment-flags-1.0.5.tgz#fa930275f5bf5dae188d6192b24b4c8bbac3d76a" - integrity sha512-VNYPRfGfmZLx0Ye20jWzHUjyTW/c+6Wq+iLhDzUI4XmhrDd9l/FozXV3F2xOaXjvp0co0+v1YSR3CMP6g+VvLQ== +nise@^4.0.1: + version "4.0.3" + resolved "https://registry.yarnpkg.com/nise/-/nise-4.0.3.tgz#9f79ff02fa002ed5ffbc538ad58518fa011dc913" + integrity sha512-EGlhjm7/4KvmmE6B/UFsKh7eHykRl9VH+au8dduHLCyWUO/hr7+N+WtTvDUwc9zHuM1IaIJs/0lQ6Ag1jDkQSg== + dependencies: + "@sinonjs/commons" "^1.7.0" + "@sinonjs/fake-timers" "^6.0.0" + "@sinonjs/text-encoding" "^0.7.1" + just-extend "^4.0.2" + path-to-regexp "^1.7.0" + +node-abi@^2.7.0: + version "2.16.0" + resolved "https://registry.yarnpkg.com/node-abi/-/node-abi-2.16.0.tgz#7df94e9c0a7a189f4197ab84bac8089ef5894992" + integrity sha512-+sa0XNlWDA6T+bDLmkCUYn6W5k5W6BPRL6mqzSCs6H/xUgtl4D5x2fORKDzopKiU6wsyn/+wXlRXwXeSp+mtoA== + dependencies: + semver "^5.4.1" + +node-addon-api@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-2.0.0.tgz#f9afb8d777a91525244b01775ea0ddbe1125483b" + integrity sha512-ASCL5U13as7HhOExbT6OlWJJUV/lLzL2voOSP1UVehpRD8FbSrSDjfScK/KwAvVTI5AS6r4VwbOMlIqtvRidnA== + +node-environment-flags@1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/node-environment-flags/-/node-environment-flags-1.0.6.tgz#a30ac13621f6f7d674260a54dede048c3982c088" + integrity sha512-5Evy2epuL+6TM0lCQGpFIj6KwiEsGh1SrHUhTbNX+sLbBtjidPZFAnVK9y5yU1+h//RitLbRHTIMyxQPtxMdHw== dependencies: object.getownpropertydescriptors "^2.0.3" semver "^5.7.0" node-fetch-npm@^2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/node-fetch-npm/-/node-fetch-npm-2.0.2.tgz#7258c9046182dca345b4208eda918daf33697ff7" - integrity sha512-nJIxm1QmAj4v3nfCvEeCrYSoVwXyxLnaPBK5W1W5DGEJwjlKuC2VEUycGw5oxk+4zZahRrB84PUJJgEmhFTDFw== + version "2.0.4" + resolved "https://registry.yarnpkg.com/node-fetch-npm/-/node-fetch-npm-2.0.4.tgz#6507d0e17a9ec0be3bec516958a497cec54bf5a4" + integrity sha512-iOuIQDWDyjhv9qSDrj9aq/klt6F9z1p2otB3AV7v3zBDcL/x+OfGsvGQZZCcMZbUf4Ujw1xGNQkjvGnVT22cKg== dependencies: encoding "^0.1.11" json-parse-better-errors "^1.0.0" @@ -6770,32 +7128,27 @@ node-fetch@^2.3.0, node-fetch@^2.5.0: resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.0.tgz#e633456386d4aa55863f676a7ab0daa8fdecb0fd" integrity sha512-8dG4H5ujfvFiqDmVu9fQ5bOHUC15JMjMY/Zumv26oOvvVJjM67KF8koCWIabKQ1GJIa9r2mMZscBq/TbdOcmNA== -node-forge@^0.7.1: - version "0.7.6" - resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-0.7.6.tgz#fdf3b418aee1f94f0ef642cd63486c77ca9724ac" - integrity sha512-sol30LUpz1jQFBjOKwbjxijiE3b6pjd74YwfD0fJOKPjF+fONKb2Yg8rYgS6+bK6VDl+/wfr4IYpC7jDzLUIfw== +node-forge@^0.9.0: + version "0.9.1" + resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-0.9.1.tgz#775368e6846558ab6676858a4d8c6e8d16c677b5" + integrity sha512-G6RlQt5Sb4GMBzXvhfkeFmbqR6MzhtnT7VTHuLadjkii3rdYHNdw0m8zA4BTxVIh68FicCQ2NSUANpsqkr9jvQ== node-gyp@^5.0.2: - version "5.0.5" - resolved "https://registry.yarnpkg.com/node-gyp/-/node-gyp-5.0.5.tgz#f6cf1da246eb8c42b097d7cd4d6c3ce23a4163af" - integrity sha512-WABl9s4/mqQdZneZHVWVG4TVr6QQJZUC6PAx47ITSk9lreZ1n+7Z9mMAIbA3vnO4J9W20P7LhCxtzfWsAD/KDw== + version "5.1.0" + resolved "https://registry.yarnpkg.com/node-gyp/-/node-gyp-5.1.0.tgz#8e31260a7af4a2e2f994b0673d4e0b3866156332" + integrity sha512-OUTryc5bt/P8zVgNUmC6xdXiDJxLMAW8cF5tLQOT9E5sOQj+UeQxnnPy74K3CLCa/SOjjBlbuzDLR8ANwA+wmw== dependencies: - env-paths "^1.0.0" - glob "^7.0.3" - graceful-fs "^4.1.2" - mkdirp "^0.5.0" - nopt "2 || 3" - npmlog "0 || 1 || 2 || 3 || 4" - request "^2.87.0" - rimraf "2" - semver "~5.3.0" + env-paths "^2.2.0" + glob "^7.1.4" + graceful-fs "^4.2.2" + mkdirp "^0.5.1" + nopt "^4.0.1" + npmlog "^4.1.2" + request "^2.88.0" + rimraf "^2.6.3" + semver "^5.7.1" tar "^4.4.12" - which "1" - -node-gzip@1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/node-gzip/-/node-gzip-1.1.2.tgz#245bd171b31ce7c7f50fc4cd0ca7195534359afb" - integrity sha512-ZB6zWpfZHGtxZnPMrJSKHVPrRjURoUzaDbLFj3VO70mpLTW5np96vXyHwft4Id0o+PYIzgDkBUjIzaNHhQ8srw== + which "^1.3.1" node-libs-browser@^2.2.1: version "2.2.1" @@ -6826,28 +7179,26 @@ node-libs-browser@^2.2.1: util "^0.11.0" vm-browserify "^1.0.1" -node-pre-gyp@^0.12.0: - version "0.12.0" - resolved "https://registry.yarnpkg.com/node-pre-gyp/-/node-pre-gyp-0.12.0.tgz#39ba4bb1439da030295f899e3b520b7785766149" - integrity sha512-4KghwV8vH5k+g2ylT+sLTjy5wmUOb9vPhnM8NHvRf9dHmnW/CndrFXy2aRPaPST6dugXSdHXfeaHQm77PIz/1A== +node-preload@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/node-preload/-/node-preload-0.2.1.tgz#c03043bb327f417a18fee7ab7ee57b408a144301" + integrity sha512-RM5oyBy45cLEoHqCeh+MNuFAxO0vTFBLskvQbOKnEE7YTTSN4tbN8QWDIPQ6L+WvKsB/qLEGpYe2ZZ9d4W9OIQ== dependencies: - detect-libc "^1.0.2" - mkdirp "^0.5.1" - needle "^2.2.1" - nopt "^4.0.1" - npm-packlist "^1.1.6" - npmlog "^4.0.2" - rc "^1.2.7" - rimraf "^2.6.1" - semver "^5.3.0" - tar "^4" + process-on-spawn "^1.0.0" -nodemon@1.19.4: - version "1.19.4" - resolved "https://registry.yarnpkg.com/nodemon/-/nodemon-1.19.4.tgz#56db5c607408e0fdf8920d2b444819af1aae0971" - integrity sha512-VGPaqQBNk193lrJFotBU8nvWZPqEZY2eIzymy2jjY0fJ9qIsxA0sxQ8ATPl0gZC645gijYEc1jtZvpS8QWzJGQ== +node-rsa@1.0.8: + version "1.0.8" + resolved "https://registry.yarnpkg.com/node-rsa/-/node-rsa-1.0.8.tgz#29a4517380f3272cd2073ff4d4c1ca44127ea4ad" + integrity sha512-q8knkMHEqViIX/fshOltCHTtlt4Nw5wpBpu0//LB1tkxqYZB/001dYMwbPvTPiENwKvPqVDkhxK6J4fV09oa7w== dependencies: - chokidar "^2.1.8" + asn1 "^0.2.4" + +nodemon@2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/nodemon/-/nodemon-2.0.4.tgz#55b09319eb488d6394aa9818148c0c2d1c04c416" + integrity sha512-Ltced+hIfTmaS28Zjv1BM552oQ3dbwPqI4+zI0SLgq+wpJhSyqgYude/aZa/3i31VCQWMfXJVxvu86abcam3uQ== + dependencies: + chokidar "^3.2.2" debug "^3.2.6" ignore-by-default "^1.0.1" minimatch "^3.0.4" @@ -6856,24 +7207,22 @@ nodemon@1.19.4: supports-color "^5.5.0" touch "^3.1.0" undefsafe "^2.0.2" - update-notifier "^2.5.0" + update-notifier "^4.0.0" "noder.io@>= 1.0.0 < 2.0.0": version "1.2.0" resolved "https://registry.yarnpkg.com/noder.io/-/noder.io-1.2.0.tgz#ad2bc6c6c3f9465891edbc6dbf5e84dcae2fa9e6" integrity sha1-rSvGxsP5RliR7bxtv16E3K4vqeY= -"nopt@2 || 3": - version "3.0.6" - resolved "https://registry.yarnpkg.com/nopt/-/nopt-3.0.6.tgz#c6465dbf08abcd4db359317f79ac68a646b28ff9" - integrity sha1-xkZdvwirzU2zWTF/eaxopkayj/k= - dependencies: - abbrev "1" +noop-logger@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/noop-logger/-/noop-logger-0.1.1.tgz#94a2b1633c4f1317553007d8966fd0e841b6a4c2" + integrity sha1-lKKxYzxPExdVMAfYlm/Q6EG2pMI= nopt@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/nopt/-/nopt-4.0.1.tgz#d0d4685afd5415193c8c7505602d0d17cd64474d" - integrity sha1-0NRoWv1UFRk8jHUFYC0NF81kR00= + version "4.0.3" + resolved "https://registry.yarnpkg.com/nopt/-/nopt-4.0.3.tgz#a375cad9d02fd921278d954c2254d5aa57e15e48" + integrity sha512-CvaGwVMztSMJLOeXPrez7fyfObdZqNUK1cPAEzLHrTybIua9pMdmmPR5YwtfNftIOMv3DPUhFaxsZMNTQO20Kg== dependencies: abbrev "1" osenv "^0.1.4" @@ -6902,7 +7251,7 @@ normalize-path@^2.1.1: dependencies: remove-trailing-separator "^1.0.1" -normalize-path@^3.0.0: +normalize-path@^3.0.0, normalize-path@~3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== @@ -6912,15 +7261,22 @@ normalize-url@^3.3.0: resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-3.3.0.tgz#b2e1c4dc4f7c6d57743df733a4f5978d18650559" integrity sha512-U+JJi7duF1o+u2pynbp2zXDW2/PADgC30f0GsHZtRh+HOcXHnw137TrNlyxxRvWW5fjKd3bcLHPxofWuCjaeZg== +normalize-url@^4.1.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-4.5.0.tgz#453354087e6ca96957bd8f5baf753f5982142129" + integrity sha512-2s47yzUxdexf1OhyRi4Em83iQk0aPvwTddtFz4hnSSw9dCEsLEGf6SwIO8ss/19S9iBb5sJaOuTvTGDeZI00BQ== + npm-bundled@^1.0.1: - version "1.0.6" - resolved "https://registry.yarnpkg.com/npm-bundled/-/npm-bundled-1.0.6.tgz#e7ba9aadcef962bb61248f91721cd932b3fe6bdd" - integrity sha512-8/JCaftHwbd//k6y2rEWp6k1wxVfpFzB6t1p825+cUb7Ym2XQfhwIC5KwhrvzZRJu+LtDE585zVaS32+CGtf0g== + version "1.1.1" + resolved "https://registry.yarnpkg.com/npm-bundled/-/npm-bundled-1.1.1.tgz#1edd570865a94cdb1bc8220775e29466c9fb234b" + integrity sha512-gqkfgGePhTpAEgUsGEgcq1rqPXA+tv/aVBlgEzfXwA1yiUJF7xtEt3CtVwOjNYQOVknDk0F20w58Fnm3EtG0fA== + dependencies: + npm-normalize-package-bin "^1.0.1" npm-lifecycle@^3.1.2: - version "3.1.4" - resolved "https://registry.yarnpkg.com/npm-lifecycle/-/npm-lifecycle-3.1.4.tgz#de6975c7d8df65f5150db110b57cce498b0b604c" - integrity sha512-tgs1PaucZwkxECGKhC/stbEgFyc3TGh2TJcg2CDr6jbvQRdteHNhmMeljRzpe4wgFAXQADoy1cSqqi7mtiAa5A== + version "3.1.5" + resolved "https://registry.yarnpkg.com/npm-lifecycle/-/npm-lifecycle-3.1.5.tgz#9882d3642b8c82c815782a12e6a1bfeed0026309" + integrity sha512-lDLVkjfZmvmfvpvBzA4vzee9cn+Me4orq0QF8glbswJVEbIcSNWib7qGOffolysc3teCqbbPZZkzbr3GQZTL1g== dependencies: byline "^5.0.0" graceful-fs "^4.1.15" @@ -6931,6 +7287,11 @@ npm-lifecycle@^3.1.2: umask "^1.1.0" which "^1.3.1" +npm-normalize-package-bin@^1.0.0, npm-normalize-package-bin@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/npm-normalize-package-bin/-/npm-normalize-package-bin-1.0.1.tgz#6e79a41f23fd235c0623218228da7d9c23b8f6e2" + integrity sha512-EPfafl6JL5/rU+ot6P3gRSCpPDW5VmIzX959Ob1+ySFUuuYHWHekXpwdUZcKP5C+DS4GEtdJluwBjnsNDl+fSA== + "npm-package-arg@^4.0.0 || ^5.0.0 || ^6.0.0", npm-package-arg@^6.0.0, npm-package-arg@^6.1.0: version "6.1.1" resolved "https://registry.yarnpkg.com/npm-package-arg/-/npm-package-arg-6.1.1.tgz#02168cb0a49a2b75bf988a28698de7b529df5cb7" @@ -6941,13 +7302,14 @@ npm-lifecycle@^3.1.2: semver "^5.6.0" validate-npm-package-name "^3.0.0" -npm-packlist@^1.1.6, npm-packlist@^1.4.4: - version "1.4.4" - resolved "https://registry.yarnpkg.com/npm-packlist/-/npm-packlist-1.4.4.tgz#866224233850ac534b63d1a6e76050092b5d2f44" - integrity sha512-zTLo8UcVYtDU3gdeaFu2Xu0n0EvelfHDGuqtNIn5RO7yQj4H1TqNdBc/yZjxnWA0PVB8D3Woyp0i5B43JwQ6Vw== +npm-packlist@^1.4.4: + version "1.4.8" + resolved "https://registry.yarnpkg.com/npm-packlist/-/npm-packlist-1.4.8.tgz#56ee6cc135b9f98ad3d51c1c95da22bbb9b2ef3e" + integrity sha512-5+AZgwru5IevF5ZdnFglB5wNlHG1AOOuw28WhUq8/8emhBmLv6jX5by4WJCh7lW0uSYZYS6DXqIsyZVIXRZU9A== dependencies: ignore-walk "^3.0.1" npm-bundled "^1.0.1" + npm-normalize-package-bin "^1.0.1" npm-pick-manifest@^3.0.0: version "3.0.2" @@ -6965,7 +7327,7 @@ npm-run-path@^2.0.0: dependencies: path-key "^2.0.0" -"npmlog@0 || 1 || 2 || 3 || 4", npmlog@^4.0.2, npmlog@^4.1.2: +npmlog@^4.0.1, npmlog@^4.1.2: version "4.1.2" resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-4.1.2.tgz#08a7f2a8bf734604779a9efa4ad5cc717abb954b" integrity sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg== @@ -6975,41 +7337,47 @@ npm-run-path@^2.0.0: gauge "~2.7.3" set-blocking "~2.0.0" +nuid@^1.1.2: + version "1.1.4" + resolved "https://registry.yarnpkg.com/nuid/-/nuid-1.1.4.tgz#6145710b6cc9ef0df7b94af09c6d750925939097" + integrity sha512-PXiYyHhGfrq8H4g5HyC8enO1lz6SBe5z6x1yx/JG4tmADzDGJVQy3l1sRf3VtEvPsN8dGn9hRFRwDKWL62x0BA== + number-is-nan@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d" integrity sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0= -nyc@14.1.1: - version "14.1.1" - resolved "https://registry.yarnpkg.com/nyc/-/nyc-14.1.1.tgz#151d64a6a9f9f5908a1b73233931e4a0a3075eeb" - integrity sha512-OI0vm6ZGUnoGZv/tLdZ2esSVzDwUC88SNs+6JoSOMVxA+gKMB8Tk7jBwgemLx4O40lhhvZCVw1C+OYLOBOPXWw== +nyc@15.0.1: + version "15.0.1" + resolved "https://registry.yarnpkg.com/nyc/-/nyc-15.0.1.tgz#bd4d5c2b17f2ec04370365a5ca1fc0ed26f9f93d" + integrity sha512-n0MBXYBYRqa67IVt62qW1r/d9UH/Qtr7SF1w/nQLJ9KxvWF6b2xCHImRAixHN9tnMMYHC2P14uo6KddNGwMgGg== dependencies: - archy "^1.0.0" - caching-transform "^3.0.2" - convert-source-map "^1.6.0" - cp-file "^6.2.0" - find-cache-dir "^2.1.0" - find-up "^3.0.0" - foreground-child "^1.5.6" - glob "^7.1.3" - istanbul-lib-coverage "^2.0.5" - istanbul-lib-hook "^2.0.7" - istanbul-lib-instrument "^3.3.0" - istanbul-lib-report "^2.0.8" - istanbul-lib-source-maps "^3.0.6" - istanbul-reports "^2.2.4" - js-yaml "^3.13.1" - make-dir "^2.1.0" - merge-source-map "^1.1.0" - resolve-from "^4.0.0" - rimraf "^2.6.3" + "@istanbuljs/load-nyc-config" "^1.0.0" + "@istanbuljs/schema" "^0.1.2" + caching-transform "^4.0.0" + convert-source-map "^1.7.0" + decamelize "^1.2.0" + find-cache-dir "^3.2.0" + find-up "^4.1.0" + foreground-child "^2.0.0" + glob "^7.1.6" + istanbul-lib-coverage "^3.0.0" + istanbul-lib-hook "^3.0.0" + istanbul-lib-instrument "^4.0.0" + istanbul-lib-processinfo "^2.0.2" + istanbul-lib-report "^3.0.0" + istanbul-lib-source-maps "^4.0.0" + istanbul-reports "^3.0.2" + make-dir "^3.0.0" + node-preload "^0.2.1" + p-map "^3.0.0" + process-on-spawn "^1.0.0" + resolve-from "^5.0.0" + rimraf "^3.0.0" signal-exit "^3.0.2" - spawn-wrap "^1.4.2" - test-exclude "^5.2.3" - uuid "^3.3.2" - yargs "^13.2.2" - yargs-parser "^13.0.0" + spawn-wrap "^2.0.0" + test-exclude "^6.0.0" + yargs "^15.0.2" oauth-sign@~0.9.0: version "0.9.0" @@ -7035,7 +7403,12 @@ object-filter@^1.0.2: resolved "https://registry.yarnpkg.com/object-filter/-/object-filter-1.0.2.tgz#af0b797ffebeaf8a52c6637cedbe8816cfec1bc8" integrity sha1-rwt5f/6+r4pSxmN87b6IFs/sG8g= -object-keys@^1.0.11, object-keys@^1.0.12: +object-inspect@^1.7.0: + version "1.7.0" + resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.7.0.tgz#f4f6bd181ad77f006b5ece60bd0b6f398ff74a67" + integrity sha512-a7pEHdh1xKIAgTySUGgLMx/xwDZskN1Ud6egYYN3EdRW4ZMPNEDUTF+hwy2LUC+Bl+SyLXANnwz/jyh/qutKUw== + +object-keys@^1.0.11, object-keys@^1.0.12, object-keys@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e" integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA== @@ -7057,33 +7430,33 @@ object.assign@4.1.0, object.assign@^4.1.0: has-symbols "^1.0.0" object-keys "^1.0.11" -object.entries@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/object.entries/-/object.entries-1.1.0.tgz#2024fc6d6ba246aee38bdb0ffd5cfbcf371b7519" - integrity sha512-l+H6EQ8qzGRxbkHOd5I/aHRhHDKoQXQ8g0BYt4uSweQU1/J6dZUOyWh9a2Vky35YCKjzmgxOzta2hH6kf9HuXA== +object.entries@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/object.entries/-/object.entries-1.1.1.tgz#ee1cf04153de02bb093fec33683900f57ce5399b" + integrity sha512-ilqR7BgdyZetJutmDPfXCDffGa0/Yzl2ivVNpbx/g4UeWrCdRnFDUBrKJGLhGieRHDATnyZXWBeCb29k9CJysQ== dependencies: define-properties "^1.1.3" - es-abstract "^1.12.0" + es-abstract "^1.17.0-next.1" function-bind "^1.1.1" has "^1.0.3" -object.fromentries@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/object.fromentries/-/object.fromentries-2.0.0.tgz#49a543d92151f8277b3ac9600f1e930b189d30ab" - integrity sha512-9iLiI6H083uiqUuvzyY6qrlmc/Gz8hLQFOcb/Ri/0xXFkSNS3ctV+CbE6yM2+AnkYfOB3dGjdzC0wrMLIhQICA== +object.fromentries@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/object.fromentries/-/object.fromentries-2.0.2.tgz#4a09c9b9bb3843dd0f89acdb517a794d4f355ac9" + integrity sha512-r3ZiBH7MQppDJVLx6fhD618GKNG40CZYH9wgwdhKxBDDbQgjeWGGd4AtkZad84d291YxvWe7bJGuE65Anh0dxQ== dependencies: - define-properties "^1.1.2" - es-abstract "^1.11.0" + define-properties "^1.1.3" + es-abstract "^1.17.0-next.1" function-bind "^1.1.1" - has "^1.0.1" + has "^1.0.3" object.getownpropertydescriptors@^2.0.3: - version "2.0.3" - resolved "https://registry.yarnpkg.com/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.0.3.tgz#8758c846f5b407adab0f236e0986f14b051caa16" - integrity sha1-h1jIRvW0B62rDyNuCYbxSwUcqhY= + version "2.1.0" + resolved "https://registry.yarnpkg.com/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.0.tgz#369bf1f9592d8ab89d712dced5cb81c7c5352649" + integrity sha512-Z53Oah9A3TdLoblT7VKJaTDdXdT+lQO+cNpKVnya5JDe9uLvzu1YyY1yFDFrcxrlRgWrEFH0jJtD/IbuwjcEVg== dependencies: - define-properties "^1.1.2" - es-abstract "^1.5.1" + define-properties "^1.1.3" + es-abstract "^1.17.0-next.1" object.pick@^1.3.0: version "1.3.0" @@ -7092,13 +7465,13 @@ object.pick@^1.3.0: dependencies: isobject "^3.0.1" -object.values@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/object.values/-/object.values-1.1.0.tgz#bf6810ef5da3e5325790eaaa2be213ea84624da9" - integrity sha512-8mf0nKLAoFX6VlNVdhGj31SVYpaNFtUnuoOXWyFEstsWRgU837AK+JYM0iAxwkSzGRbwn8cbFmgbyxj1j4VbXg== +object.values@^1.1.0, object.values@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/object.values/-/object.values-1.1.1.tgz#68a99ecde356b7e9295a3c5e0ce31dc8c953de5e" + integrity sha512-WTa54g2K8iu0kmS/us18jEmdv1a4Wi//BZ/DTVYEcH0XhLM5NYdpDHja3gt57VrZLcNAO2WGA+KpWsDBaHt6eA== dependencies: define-properties "^1.1.3" - es-abstract "^1.12.0" + es-abstract "^1.17.0-next.1" function-bind "^1.1.1" has "^1.0.3" @@ -7107,14 +7480,14 @@ octokit-pagination-methods@^1.1.0: resolved "https://registry.yarnpkg.com/octokit-pagination-methods/-/octokit-pagination-methods-1.1.0.tgz#cf472edc9d551055f9ef73f6e42b4dbb4c80bea4" integrity sha512-fZ4qZdQ2nxJvtcasX7Ghl+WlWS/d9IgnBIwFZXVNNZUmzpno91SX5bc5vuxiuKoCtK78XxGGNuSCrDC7xYB3OQ== -on-finished@~2.3.0: +on-finished@^2.3.0, on-finished@~2.3.0: version "2.3.0" resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.3.0.tgz#20f1336481b083cd75337992a16971aa2d906947" integrity sha1-IPEzZIGwg811M3mSoWlxqi2QaUc= dependencies: ee-first "1.1.1" -on-headers@~1.0.1: +on-headers@~1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/on-headers/-/on-headers-1.0.2.tgz#772b0ae6aaa525c399e489adfad90c403eb3c28f" integrity sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA== @@ -7126,11 +7499,6 @@ once@^1.3.0, once@^1.3.1, once@^1.4.0: dependencies: wrappy "1" -onetime@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/onetime/-/onetime-1.1.0.tgz#a1f7838f8314c516f05ecefcbc4ccfe04b4ed789" - integrity sha1-ofeDj4MUxRbwXs78vEzP4EtO14k= - onetime@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/onetime/-/onetime-2.0.1.tgz#067428230fd67443b2794b22bba528b6867962d4" @@ -7138,6 +7506,13 @@ onetime@^2.0.0: dependencies: mimic-fn "^1.0.0" +onetime@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/onetime/-/onetime-5.1.0.tgz#fff0f3c91617fe62bb50189636e99ac8a6df7be5" + integrity sha512-5NcSkPHhwTVFIQN+TUqXoS5+dlElHXdpAWu9I0HP20YOtIi+aZ0Ct82jdlILDxjLEAWwvm+qj1m6aEtsDVmm6Q== + dependencies: + mimic-fn "^2.1.0" + "oolong@>= 1.11.0 < 2": version "1.15.1" resolved "https://registry.yarnpkg.com/oolong/-/oolong-1.15.1.tgz#90bac9e7ce52f60906ab2228d9e2b115f1086cea" @@ -7148,47 +7523,41 @@ opencollective-postinstall@^2.0.2: resolved "https://registry.yarnpkg.com/opencollective-postinstall/-/opencollective-postinstall-2.0.2.tgz#5657f1bede69b6e33a45939b061eb53d3c6c3a89" integrity sha512-pVOEP16TrAO2/fjej1IdOyupJY8KDUM1CvsaScRbw6oddvpQoOfGk4ywha0HKKVAD6RkW4x6Q+tNBwhf3Bgpuw== -opener@^1.4.2, opener@^1.5.1: +opener@^1.4.2: version "1.5.1" resolved "https://registry.yarnpkg.com/opener/-/opener-1.5.1.tgz#6d2f0e77f1a0af0032aca716c2c1fbb8e7e8abed" integrity sha512-goYSy5c2UXE4Ra1xixabeVh1guIX/ZV/YokJksb6q2lubWu6UbvPQ20p542/sFIll1nl8JnCyK9oBaOcCWXwvA== -optimist@^0.6.1: - version "0.6.1" - resolved "https://registry.yarnpkg.com/optimist/-/optimist-0.6.1.tgz#da3ea74686fa21a19a111c326e90eb15a0196686" - integrity sha1-2j6nRob6IaGaERwybpDrFaAZZoY= - dependencies: - minimist "~0.0.1" - wordwrap "~0.0.2" - optionator@^0.8.2: - version "0.8.2" - resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.8.2.tgz#364c5e409d3f4d6301d6c0b4c05bba50180aeb64" - integrity sha1-NkxeQJ0/TWMB1sC0wFu6UBgK62Q= + version "0.8.3" + resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.8.3.tgz#84fa1d036fe9d3c7e21d99884b601167ec8fb495" + integrity sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA== dependencies: deep-is "~0.1.3" - fast-levenshtein "~2.0.4" + fast-levenshtein "~2.0.6" levn "~0.3.0" prelude-ls "~1.1.2" type-check "~0.3.2" - wordwrap "~1.0.0" + word-wrap "~1.2.3" -ora@^0.2.3: - version "0.2.3" - resolved "https://registry.yarnpkg.com/ora/-/ora-0.2.3.tgz#37527d220adcd53c39b73571d754156d5db657a4" - integrity sha1-N1J9Igrc1Tw5tzVx11QVbV22V6Q= +optionator@^0.9.1: + version "0.9.1" + resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.9.1.tgz#4f236a6373dae0566a6d43e1326674f50c291499" + integrity sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw== dependencies: - chalk "^1.1.1" - cli-cursor "^1.0.2" - cli-spinners "^0.1.2" - object-assign "^4.0.1" + 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" os-browserify@^0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/os-browserify/-/os-browserify-0.3.0.tgz#854373c7f5c2315914fc9bfc6bd8238fdda1ec27" integrity sha1-hUNzx/XCMVkU/Jv8a9gjj92h7Cc= -os-homedir@^1.0.0, os-homedir@^1.0.1: +os-homedir@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/os-homedir/-/os-homedir-1.0.2.tgz#ffbc4988336e0e833de0c168c7ef152121aa7fb3" integrity sha1-/7xJiDNuDoM94MFox+8VISGqf7M= @@ -7223,7 +7592,7 @@ osenv@^0.1.4, osenv@^0.1.5: os-homedir "^1.0.0" os-tmpdir "^1.0.0" -p-cancelable@^1.1.0: +p-cancelable@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-1.1.0.tgz#d078d15a3af409220c886f1d9a0ca2e441ab26cc" integrity sha512-s73XxOZ4zpt1edZYZzvhqFa6uvQc1vwUa0K0BdtIZgQMAJj9IbebH+JkgKZc9h+B05PKHLOTl4ajG1BmNrVZlw== @@ -7251,9 +7620,9 @@ p-limit@^1.1.0: p-try "^1.0.0" p-limit@^2.0.0, p-limit@^2.2.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.2.0.tgz#417c9941e6027a9abcba5092dd2904e255b5fbc2" - integrity sha512-pZbTJpoUsCzV48Mc9Nh51VbwO0X9cuPFE8gYwx9BTCt9SF8/b7Zljd2fVgOxhIF/HDTKgpVzs+GPhyKfjLLFRQ== + version "2.3.0" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1" + integrity sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w== dependencies: p-try "^2.0.0" @@ -7285,26 +7654,23 @@ p-map-series@^1.0.0: dependencies: p-reduce "^1.0.0" -p-map@^1.1.1: - version "1.2.0" - resolved "https://registry.yarnpkg.com/p-map/-/p-map-1.2.0.tgz#e4e94f311eabbc8633a1e79908165fca26241b6b" - integrity sha512-r6zKACMNhjPJMTl8KcFH4li//gkrXWfbD6feV8l6doRHlzljFWGJ2AP6iKaCJXyZmAUMOPtvbW7EXkbWO/pLEA== - p-map@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/p-map/-/p-map-2.1.0.tgz#310928feef9c9ecc65b68b17693018a665cea175" integrity sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw== +p-map@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/p-map/-/p-map-3.0.0.tgz#d704d9af8a2ba684e2600d9a215983d4141a979d" + integrity sha512-d3qXVTF/s+W+CdJ5A29wywV2n8CQQYahlgz2bFiA+4eVNJbHJodPZ+/gXwPGh0bOqA+j8S+6+ckmvLGPk1QpxQ== + dependencies: + aggregate-error "^3.0.0" + p-pipe@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/p-pipe/-/p-pipe-1.2.0.tgz#4b1a11399a11520a67790ee5a0c1d5881d6befe9" integrity sha1-SxoROZoRUgpneQ7loMHViB1r7+k= -p-queue@^2.4.2: - version "2.4.2" - resolved "https://registry.yarnpkg.com/p-queue/-/p-queue-2.4.2.tgz#03609826682b743be9a22dba25051bd46724fc34" - integrity sha512-n8/y+yDJwBjoLQe1GSJbbaYQLTI7QHNZI2+rpmCDbe++WLf9HC3gf6iqj5yfPAV71W4UF3ql5W1+UBPXoXTxng== - p-queue@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/p-queue/-/p-queue-4.0.0.tgz#ed0eee8798927ed6f2c2f5f5b77fdb2061a5d346" @@ -7317,14 +7683,6 @@ p-reduce@^1.0.0: resolved "https://registry.yarnpkg.com/p-reduce/-/p-reduce-1.0.0.tgz#18c2b0dd936a4690a529f8231f58a0fdb6a47dfa" integrity sha1-GMKw3ZNqRpClKfgjH1ig/bakffo= -p-retry@^4.0.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/p-retry/-/p-retry-4.1.0.tgz#9ce7cef2069e84bf590df3b8ec18d740109338d6" - integrity sha512-oepllyG9gX1qH4Sm20YAKxg1GA7L7puhvGnTfimi31P07zSIj7SDV6YtuAx9nbJF51DES+2CIIRkXs8GKqWJxA== - dependencies: - "@types/retry" "^0.12.0" - retry "^0.12.0" - p-try@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/p-try/-/p-try-1.0.0.tgz#cbc79cdbaf8fd4228e13f621f2b1a237c1b207b3" @@ -7342,25 +7700,25 @@ p-waterfall@^1.0.0: dependencies: p-reduce "^1.0.0" -package-hash@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/package-hash/-/package-hash-3.0.0.tgz#50183f2d36c9e3e528ea0a8605dff57ce976f88e" - integrity sha512-lOtmukMDVvtkL84rJHI7dpTYq+0rli8N2wlnqUcBuDWCfVhRUfOmnR9SsoHFMLpACvEV60dX7rd0rFaYDZI+FA== +package-hash@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/package-hash/-/package-hash-4.0.0.tgz#3537f654665ec3cc38827387fc904c163c54f506" + integrity sha512-whdkPIooSu/bASggZ96BWVvZTRMOFxnyUG5PnTSGKoJE2gd5mbVNmR2Nj20QFzxYYgAXpoqC+AiXzl+UMRh7zQ== dependencies: graceful-fs "^4.1.15" - hasha "^3.0.0" + hasha "^5.0.0" lodash.flattendeep "^4.4.0" release-zalgo "^1.0.0" -package-json@^4.0.0: - version "4.0.1" - resolved "https://registry.yarnpkg.com/package-json/-/package-json-4.0.1.tgz#8869a0401253661c4c4ca3da6c2121ed555f5eed" - integrity sha1-iGmgQBJTZhxMTKPabCEh7VVfXu0= +package-json@^6.3.0: + version "6.5.0" + resolved "https://registry.yarnpkg.com/package-json/-/package-json-6.5.0.tgz#6feedaca35e75725876d0b0e64974697fed145b0" + integrity sha512-k3bdm2n25tkyxcjSKzB5x8kfVxlMdgsbPr0GkZcwHsLpba6cBjqCt1KlcChKEvxHIcTB1FVMuwoijZ26xex5MQ== dependencies: - got "^6.7.1" - registry-auth-token "^3.0.1" - registry-url "^3.0.3" - semver "^5.1.0" + got "^9.6.0" + registry-auth-token "^4.0.0" + registry-url "^5.0.0" + semver "^6.2.0" packet-reader@1.0.0: version "1.0.0" @@ -7368,16 +7726,16 @@ packet-reader@1.0.0: integrity sha512-HAKu/fG3HpHFO0AA8WE8q2g+gBJaZ9MG7fcKk+IJPLTGAD6Psw4443l+9DGRbOIh3/aXr7Phy0TjilYivJo5XQ== pako@~1.0.5: - version "1.0.10" - resolved "https://registry.yarnpkg.com/pako/-/pako-1.0.10.tgz#4328badb5086a426aa90f541977d4955da5c9732" - integrity sha512-0DTvPVU3ed8+HNXOu5Bs+o//Mbdj9VNQMUOe9oKCwh8l0GNwpTDMKCWbRjgtD291AWnkAgkqA/LOnQS8AmS1tw== + version "1.0.11" + resolved "https://registry.yarnpkg.com/pako/-/pako-1.0.11.tgz#6c9599d340d54dfd3946380252a35705a6b992bf" + integrity sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw== parallel-transform@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/parallel-transform/-/parallel-transform-1.1.0.tgz#d410f065b05da23081fcd10f28854c29bda33b06" - integrity sha1-1BDwZbBdojCB/NEPKIVMKb2jOwY= + version "1.2.0" + resolved "https://registry.yarnpkg.com/parallel-transform/-/parallel-transform-1.2.0.tgz#9049ca37d6cb2182c3b1d2c720be94d14a5814fc" + integrity sha512-P2vSmIu38uIlvdcU7fDkyrxj33gTUy/ABO5ZUbGowxNCopBq/OoD42bP4UmMrJoPyk4Uqf0mu3mtWBhHCZD8yg== dependencies: - cyclist "~0.2.2" + cyclist "^1.0.1" inherits "^2.0.3" readable-stream "^2.1.5" @@ -7388,10 +7746,15 @@ parent-module@^1.0.0: dependencies: callsites "^3.0.0" -parse-asn1@^5.0.0: - version "5.1.4" - resolved "https://registry.yarnpkg.com/parse-asn1/-/parse-asn1-5.1.4.tgz#37f6628f823fbdeb2273b4d540434a22f3ef1fcc" - integrity sha512-Qs5duJcuvNExRfFZ99HDD3z4mAi3r9Wl/FOjEOijlxwCZs7E7mW2vjTpgQ4J8LpTF8x5v+1Vn5UQFejmWT11aw== +parent-require@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/parent-require/-/parent-require-1.0.0.tgz#746a167638083a860b0eef6732cb27ed46c32977" + integrity sha1-dGoWdjgIOoYLDu9nMssn7UbDKXc= + +parse-asn1@^5.0.0, parse-asn1@^5.1.5: + version "5.1.5" + resolved "https://registry.yarnpkg.com/parse-asn1/-/parse-asn1-5.1.5.tgz#003271343da58dc94cace494faef3d2147ecea0e" + integrity sha512-jkMYn1dcJqF6d5CpU689bq7w/b5ALS9ROVSpQDPrZsqqesUJii9qutvoT5ltGedNXMO2e16YUWIghG9KxaViTQ== dependencies: asn1.js "^4.0.0" browserify-aes "^1.0.0" @@ -7453,6 +7816,18 @@ parse-url@^5.0.0: parse-path "^4.0.0" protocols "^1.4.0" +parse5-htmlparser2-tree-adapter@^5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-5.1.1.tgz#e8c743d4e92194d5293ecde2b08be31e67461cbc" + integrity sha512-CF+TKjXqoqyDwHqBhFQ+3l5t83xYi6fVT1tQNg+Ye0JRLnTxWvIroCjEp1A0k4lneHNBGnICUf0cfYVYGEazqw== + dependencies: + parse5 "^5.1.1" + +parse5@^5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/parse5/-/parse5-5.1.1.tgz#f68e4e5ba1852ac2cadc00f4555fff6c2abb6178" + integrity sha512-ugq4DFI0Ptb+WWjAdOK16+u/nHfiIrcE+sh8kZMaM0WllQKLI9rOUq6c2b7cwPkXdzfQESqvoqK6ug7U/Yyzug== + parseurl@~1.3.3: version "1.3.3" resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4" @@ -7495,7 +7870,7 @@ path-is-absolute@^1.0.0: resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18= -path-is-inside@^1.0.1, path-is-inside@^1.0.2: +path-is-inside@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/path-is-inside/-/path-is-inside-1.0.2.tgz#365417dede44430d1c11af61027facf074bdfc53" integrity sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM= @@ -7505,6 +7880,11 @@ path-key@^2.0.0, path-key@^2.0.1: resolved "https://registry.yarnpkg.com/path-key/-/path-key-2.0.1.tgz#411cadb574c5a140d3a4b1910d40d80cc9f40b40" integrity sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A= +path-key@^3.1.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" + integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== + path-parse@^1.0.6: version "1.0.6" resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.6.tgz#d62dbb5679405d72c4737ec58600e9ddcf06d24c" @@ -7516,9 +7896,9 @@ path-to-regexp@0.1.7: integrity sha1-32BBeABfUi8V60SQ5yR6G/qmf4w= path-to-regexp@^1.7.0: - version "1.7.0" - resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-1.7.0.tgz#59fde0f435badacba103a84e9d3bc64e96b9937d" - integrity sha1-Wf3g9DW62suhA6hOnTvGTpa5k30= + version "1.8.0" + resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-1.8.0.tgz#887b3ba9d84393e87a0a0b9f4cb756198b53548a" + integrity sha512-n43JRhlUKUAlibEJhPeir1ncUID16QnEjNpwzNdO3Lm4ywrBpBZ5oLD0I6br9evr1Y9JTqwRtAh7JLoOzAQdVA== dependencies: isarray "0.0.1" @@ -7545,10 +7925,10 @@ path-type@^3.0.0: dependencies: pify "^3.0.0" -pathval@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/pathval/-/pathval-1.1.0.tgz#b942e6d4bde653005ef6b71361def8727d0645e0" - integrity sha1-uULm1L3mUwBe9rcTYd74cn0GReA= +path-type@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b" + integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw== pbkdf2@^3.0.3: version "3.0.17" @@ -7561,30 +7941,30 @@ pbkdf2@^3.0.3: safe-buffer "^5.0.1" sha.js "^2.4.8" -pend@~1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/pend/-/pend-1.2.0.tgz#7a57eb550a6783f9115331fcf4663d5c8e007a50" - integrity sha1-elfrVQpng/kRUzH89GY9XI4AelA= - performance-now@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b" integrity sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns= -pg-connection-string@0.1.3: - version "0.1.3" - resolved "https://registry.yarnpkg.com/pg-connection-string/-/pg-connection-string-0.1.3.tgz#da1847b20940e42ee1492beaf65d49d91b245df7" - integrity sha1-2hhHsglA5C7hSSvq9l1J2RskXfc= +pg-connection-string@^2.2.2: + version "2.2.2" + resolved "https://registry.yarnpkg.com/pg-connection-string/-/pg-connection-string-2.2.2.tgz#fdc2fb2084e655f0b3e6e50c69330f6932df452e" + integrity sha512-+hel4DGuSZCjCZwglAuyi+XlodHnKmrbyTw0hVWlmGN2o4AfJDkDo5obAFzblS5M5PFBMx0uDt5Y1QjlNC+tqg== pg-int8@1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/pg-int8/-/pg-int8-1.0.1.tgz#943bd463bf5b71b4170115f80f8efc9a0c0eb78c" integrity sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw== -pg-pool@^2.0.4: - version "2.0.7" - resolved "https://registry.yarnpkg.com/pg-pool/-/pg-pool-2.0.7.tgz#f14ecab83507941062c313df23f6adcd9fd0ce54" - integrity sha512-UiJyO5B9zZpu32GSlP0tXy8J2NsJ9EFGFfz5v6PSbdz/1hBLX1rNiiy5+mAm5iJJYwfCv4A0EBcQLGWwjbpzZw== +pg-pool@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/pg-pool/-/pg-pool-3.2.0.tgz#edde90f21556f83d16c45f1efd696af4afec6f2f" + integrity sha512-7BLwDNDEfPFjE9vmZLcJPLFwuDAVGZ5lIZo2MeQfwYG7EPGfdNVis/dz6obI/yKqvQIx2sf6QBKXMLB+y/ftgA== + +pg-protocol@^1.2.3: + version "1.2.3" + resolved "https://registry.yarnpkg.com/pg-protocol/-/pg-protocol-1.2.3.tgz#711764fff0584943229eddfe60c0ba0aefabc6ea" + integrity sha512-erHFURS0mPmTbq18cn/zNL3Y4IzNCrU4sgCim0qy7zAPe3Vc0rvK5cImJR6lDvIaz3fJU2R1R9FNOlnUtyF10Q== pg-types@^2.1.0: version "2.2.0" @@ -7597,15 +7977,16 @@ pg-types@^2.1.0: postgres-date "~1.0.4" postgres-interval "^1.1.0" -pg@7.12.1: - version "7.12.1" - resolved "https://registry.yarnpkg.com/pg/-/pg-7.12.1.tgz#880636d46d2efbe0968e64e9fe0eeece8ef72a7e" - integrity sha512-l1UuyfEvoswYfcUe6k+JaxiN+5vkOgYcVSbSuw3FvdLqDbaoa2RJo1zfJKfPsSYPFVERd4GHvX3s2PjG1asSDA== +pg@8.2.0: + version "8.2.0" + resolved "https://registry.yarnpkg.com/pg/-/pg-8.2.0.tgz#c569ddfb8b700b695835fcd77f1679d74141a20e" + integrity sha512-EQeWKZv7qBTQZQa7EraR61AOi0bpizvlZLvrPdgAGaraX4YI+y40iQnL39XjPMXVnHOOG3jV6kAGtc0WSJn/+A== dependencies: buffer-writer "2.0.0" packet-reader "1.0.0" - pg-connection-string "0.1.3" - pg-pool "^2.0.4" + pg-connection-string "^2.2.2" + pg-pool "^3.2.0" + pg-protocol "^1.2.3" pg-types "^2.1.0" pgpass "1.x" semver "4.3.2" @@ -7617,12 +7998,12 @@ pgpass@1.x: dependencies: split "^1.0.0" -picomatch@^2.0.5: - version "2.0.7" - resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.0.7.tgz#514169d8c7cd0bdbeecc8a2609e34a7163de69f6" - integrity sha512-oLHIdio3tZ0qH76NybpeneBhYVj0QFTfXEFTc/B3zKQspYfYYkWYgFsmzo+4kvId/bQRcNkVeguI3y+CD22BtA== +picomatch@^2.0.4, picomatch@^2.0.5, picomatch@^2.2.1: + version "2.2.2" + resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.2.2.tgz#21f333e9b6b8eaff02468f5146ea406d345f4dad" + integrity sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg== -pify@^2.0.0, pify@^2.2.0, pify@^2.3.0: +pify@^2.0.0, pify@^2.3.0: version "2.3.0" resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c" integrity sha1-7RQaasBDqEnqWISY59yosVMw6Qw= @@ -7663,7 +8044,7 @@ pkg-dir@^3.0.0: dependencies: find-up "^3.0.0" -pkg-dir@^4.2.0: +pkg-dir@^4.1.0, pkg-dir@^4.2.0: version "4.2.0" resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-4.2.0.tgz#f099133df7ede422e81d1d8448270eeb3e4261f3" integrity sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ== @@ -7698,9 +8079,9 @@ postgres-bytea@~1.0.0: integrity sha1-AntTPAqokOJtFy1Hz5zOzFIazTU= postgres-date@~1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/postgres-date/-/postgres-date-1.0.4.tgz#1c2728d62ef1bff49abdd35c1f86d4bdf118a728" - integrity sha512-bESRvKVuTrjoBluEcpv2346+6kgB7UlnqWZsnbnCccTNq/pqfj1j6oBaN5+b/NrDXepYUT/HKadqv3iS9lJuVA== + version "1.0.5" + resolved "https://registry.yarnpkg.com/postgres-date/-/postgres-date-1.0.5.tgz#710b27de5f27d550f6e80b5d34f7ba189213c2ee" + integrity sha512-pdau6GRPERdAYUQwkBnGKxEfPyhVZXG/JiS44iZWiNdSOWE09N2lUgN6yshuq6fVSon4Pm0VMXd1srUUkLe9iA== postgres-interval@^1.1.0: version "1.2.0" @@ -7709,15 +8090,41 @@ postgres-interval@^1.1.0: dependencies: xtend "^4.0.0" +prebuild-install@^5.3.3: + version "5.3.3" + resolved "https://registry.yarnpkg.com/prebuild-install/-/prebuild-install-5.3.3.tgz#ef4052baac60d465f5ba6bf003c9c1de79b9da8e" + integrity sha512-GV+nsUXuPW2p8Zy7SarF/2W/oiK8bFQgJcncoJ0d7kRpekEA0ftChjfEaF9/Y+QJEc/wFR7RAEa8lYByuUIe2g== + dependencies: + detect-libc "^1.0.3" + expand-template "^2.0.3" + github-from-package "0.0.0" + minimist "^1.2.0" + mkdirp "^0.5.1" + napi-build-utils "^1.0.1" + node-abi "^2.7.0" + noop-logger "^0.1.1" + npmlog "^4.0.1" + pump "^3.0.0" + rc "^1.2.7" + simple-get "^3.0.3" + tar-fs "^2.0.0" + tunnel-agent "^0.6.0" + which-pm-runs "^1.0.0" + +prelude-ls@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396" + integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g== + prelude-ls@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54" integrity sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ= -prepend-http@^1.0.1: - version "1.0.4" - resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-1.0.4.tgz#d4f4562b0ce3696e41ac52d0e002e57a635dc6dc" - integrity sha1-1PRWKwzjaW5BrFLQ4ALlemNdxtw= +prepend-http@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-2.0.0.tgz#e92434bfa5ea8c19f41cdfd401d741a3c819d897" + integrity sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc= prettier-linter-helpers@^1.0.0: version "1.0.0" @@ -7726,16 +8133,23 @@ prettier-linter-helpers@^1.0.0: dependencies: fast-diff "^1.1.2" -prettier@1.18.2: - version "1.18.2" - resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.18.2.tgz#6823e7c5900017b4bd3acf46fe9ac4b4d7bda9ea" - integrity sha512-OeHeMc0JhFE9idD4ZdtNibzY0+TPHSpSSb9h8FqtP+YnoZZ1sl8Vc9b1sasjfymH3SonAF4QcA2+mzHPhMvIiw== +prettier@2.0.5: + version "2.0.5" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.0.5.tgz#d6d56282455243f2f92cc1716692c08aa31522d4" + integrity sha512-7PtVymN48hGcO4fGjybyBSIWDsLU4H4XlvOHfq91pz9kkGlonzwTfYkaIEwiRg/dAJF9YlbsduBAgtYLi+8cFg== process-nextick-args@~2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag== +process-on-spawn@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/process-on-spawn/-/process-on-spawn-1.0.0.tgz#95b05a23073d30a17acfdc92a440efd2baefdc93" + integrity sha512-1WsPDsUSMmZH5LeMLegqkPDrsGgsWwk1Exipy2hvB0o/F0ASzbpIctSCcZIK1ykJvtTJULEH+20WOFjMvGnCTg== + dependencies: + fromentries "^1.2.0" + process@^0.11.10: version "0.11.10" resolved "https://registry.yarnpkg.com/process/-/process-0.11.10.tgz#7332300e840161bda3e69a1d1d91a7d4bc16f182" @@ -7766,7 +8180,7 @@ promzard@^0.3.0: dependencies: read "1" -prop-types@^15.6.2, prop-types@^15.7.2: +prop-types@^15.7.2: version "15.7.2" resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.7.2.tgz#52c41e75b8c87e72b9d9360e0206b99dcbffa6c5" integrity sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ== @@ -7793,27 +8207,27 @@ protoduck@^5.0.1: genfun "^5.0.0" proxy-addr@~2.0.5: - version "2.0.5" - resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.5.tgz#34cbd64a2d81f4b1fd21e76f9f06c8a45299ee34" - integrity sha512-t/7RxHXPH6cJtP0pRG6smSr9QJidhB+3kXu0KgXnbGYMgzEnUxRQ4/LDdfOwZEMyIh3/xHb8PX3t+lfL9z+YVQ== + version "2.0.6" + resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.6.tgz#fdc2336505447d3f2f2c638ed272caf614bbb2bf" + integrity sha512-dh/frvCBVmSsDYzw6n926jv974gddhkFPfiN8hPOi30Wax25QZyZEGveluCgliBnqmuM+UJmBErbAUFIoDbjOw== dependencies: forwarded "~0.1.2" - ipaddr.js "1.9.0" + ipaddr.js "1.9.1" prr@~1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/prr/-/prr-1.0.1.tgz#d3fc114ba06995a45ec6893f484ceb1d78f5f476" integrity sha1-0/wRS6BplaRexok/SEzrHXj19HY= -pseudomap@^1.0.2: +pseudomap@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/pseudomap/-/pseudomap-1.0.2.tgz#f052a28da70e618917ef0a8ac34c1ae5a68286b3" integrity sha1-8FKijacOYYkX7wqKw0wa5aaChrM= -psl@^1.1.24, psl@^1.1.28: - version "1.2.0" - resolved "https://registry.yarnpkg.com/psl/-/psl-1.2.0.tgz#df12b5b1b3a30f51c329eacbdef98f3a6e136dc6" - integrity sha512-GEn74ZffufCmkDDLNcl3uuyF/aSD6exEyh1v/ZSdAomB82t6G9hzJVRx0jBmLDW+VfZqks3aScmMw9DszwUalA== +psl@^1.1.28: + version "1.8.0" + resolved "https://registry.yarnpkg.com/psl/-/psl-1.8.0.tgz#9326f8bcfb013adcc005fdff056acce020e51c24" + integrity sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ== pstree.remy@^1.1.7: version "1.1.7" @@ -7862,7 +8276,7 @@ punycode@1.3.2: resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.3.2.tgz#9653a036fb7c1ee42342f2325cceefea3926c48d" integrity sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0= -punycode@^1.2.4, punycode@^1.4.1: +punycode@^1.2.4: version "1.4.1" resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e" integrity sha1-wNWmOycYgArY4esPpSachN1BhF4= @@ -7872,21 +8286,28 @@ punycode@^2.1.0, punycode@^2.1.1: resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A== -pushover-notifications@1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/pushover-notifications/-/pushover-notifications-1.2.0.tgz#d5007668a0a963973d3886d7ea265c204d415b7a" - integrity sha512-Da2XgHDDq9ZU4idbIx5Y9N4kCsHVgeeHViHK2wxdtdkdP58OvrsKCqpLZnr5nS+I4/PphjTORGSVzwMV2UaPLg== +pupa@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/pupa/-/pupa-2.0.1.tgz#dbdc9ff48ffbea4a26a069b6f9f7abb051008726" + integrity sha512-hEJH0s8PXLY/cdXh66tNEQGndDrIKNqNC5xmrysZy3i5C3oEoLna7YAOad+7u125+zH1HNXUmGEkrhb3c2VriA== + dependencies: + escape-goat "^2.0.0" q@^1.5.1: version "1.5.1" resolved "https://registry.yarnpkg.com/q/-/q-1.5.1.tgz#7e32f75b41381291d04611f1bf14109ac00651d7" integrity sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc= -qs@6.7.0, qs@^6.5.1: +qs@6.7.0: version "6.7.0" resolved "https://registry.yarnpkg.com/qs/-/qs-6.7.0.tgz#41dc1a015e3d581f1621776be31afb2876a9b1bc" integrity sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ== +qs@^6.5.1: + version "6.9.4" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.9.4.tgz#9090b290d1f91728d3c22e54843ca44aea5ab687" + integrity sha512-A1kFqHekCTM7cz0udomYUoYNWjBebHm/5wzU/XqrBRBNWectVH0QIiN+NEcZ0Dte5hvzHwbr8+XQmguPhJ6WdQ== + qs@~6.5.2: version "6.5.2" resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.2.tgz#cb3ae806e8740444584ef154ce8ee98d403f3e36" @@ -7907,10 +8328,10 @@ quick-lru@^1.0.0: resolved "https://registry.yarnpkg.com/quick-lru/-/quick-lru-1.1.0.tgz#4360b17c61136ad38078397ff11416e186dcfbb8" integrity sha1-Q2CxfGETatOAeDl/8RQW4Ybc+7g= -ramda@0.24.1: - version "0.24.1" - resolved "https://registry.yarnpkg.com/ramda/-/ramda-0.24.1.tgz#c3b7755197f35b8dc3502228262c4c91ddb6b857" - integrity sha1-w7d1UZfzW43DUCIoJixMkd22uFc= +quick-lru@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/quick-lru/-/quick-lru-4.0.1.tgz#5b8878f113a58217848c6482026c73e1ba57727f" + integrity sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g== randombytes@^2.0.0, randombytes@^2.0.1, randombytes@^2.0.5: version "2.1.0" @@ -7942,7 +8363,7 @@ raw-body@2.4.0: iconv-lite "0.4.24" unpipe "1.0.0" -rc@^1.0.1, rc@^1.1.6, rc@^1.2.7: +rc@^1.2.7, rc@^1.2.8: version "1.2.8" resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.8.tgz#cd924bf5200a075b83c188cd6b9e211b7fc0d3ed" integrity sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw== @@ -7952,46 +8373,27 @@ rc@^1.0.1, rc@^1.1.6, rc@^1.2.7: minimist "^1.2.0" strip-json-comments "~2.0.1" -react-dom@^16.8.5: - version "16.10.2" - resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-16.10.2.tgz#4840bce5409176bc3a1f2bd8cb10b92db452fda6" - integrity sha512-kWGDcH3ItJK4+6Pl9DZB16BXYAZyrYQItU4OMy0jAkv5aNqc+mAKb4TpFtAteI6TJZu+9ZlNhaeNQSVQDHJzkw== - dependencies: - loose-envify "^1.1.0" - object-assign "^4.1.1" - prop-types "^15.6.2" - scheduler "^0.16.2" - react-is@^16.8.1: - version "16.8.6" - resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.8.6.tgz#5bbc1e2d29141c9fbdfed456343fe2bc430a6a16" - integrity sha512-aUk3bHfZ2bRSVFFbbeVS4i+lNPZr3/WM5jT2J5omUVV1zzcs1nAaf3l51ctA5FFvCRbhrH0bdAsRRQddFJZPtA== - -react@^16.8.5: - version "16.10.2" - resolved "https://registry.yarnpkg.com/react/-/react-16.10.2.tgz#a5ede5cdd5c536f745173c8da47bda64797a4cf0" - integrity sha512-MFVIq0DpIhrHFyqLU0S3+4dIcBhhOvBE8bJ/5kHPVOVaGdo0KuiQzpcjCPsf585WvhypqtrMILyoE2th6dT+Lw== - dependencies: - loose-envify "^1.1.0" - object-assign "^4.1.1" - prop-types "^15.6.2" + version "16.13.1" + resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4" + integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ== read-cmd-shim@^1.0.1: - version "1.0.4" - resolved "https://registry.yarnpkg.com/read-cmd-shim/-/read-cmd-shim-1.0.4.tgz#b4a53d43376211b45243f0072b6e603a8e37640d" - integrity sha512-Pqpl3qJ/QdOIjRYA0q5DND/gLvGOfpIz/fYVDGYpOXfW/lFrIttmLsBnd6IkyK10+JHU9zhsaudfvrQTBB9YFQ== + version "1.0.5" + resolved "https://registry.yarnpkg.com/read-cmd-shim/-/read-cmd-shim-1.0.5.tgz#87e43eba50098ba5a32d0ceb583ab8e43b961c16" + integrity sha512-v5yCqQ/7okKoZZkBQUAfTsQ3sVJtXdNfbPnI5cceppoxEVLYA3k+VtV2omkeo8MS94JCy4fSiUwlRBAwCVRPUA== dependencies: graceful-fs "^4.1.2" "read-package-json@1 || 2", read-package-json@^2.0.0, read-package-json@^2.0.13: - version "2.1.0" - resolved "https://registry.yarnpkg.com/read-package-json/-/read-package-json-2.1.0.tgz#e3d42e6c35ea5ae820d9a03ab0c7291217fc51d5" - integrity sha512-KLhu8M1ZZNkMcrq1+0UJbR8Dii8KZUqB0Sha4mOx/bknfKI/fyrQVrG/YIt2UOtG667sD8+ee4EXMM91W9dC+A== + version "2.1.1" + resolved "https://registry.yarnpkg.com/read-package-json/-/read-package-json-2.1.1.tgz#16aa66c59e7d4dad6288f179dd9295fd59bb98f1" + integrity sha512-dAiqGtVc/q5doFz6096CcnXhpYk0ZN8dEKVkGLU0CsASt8SrgF6SF7OTKAYubfvFhWaqofl+Y8HK19GR8jwW+A== dependencies: glob "^7.1.1" json-parse-better-errors "^1.0.1" normalize-package-data "^2.0.0" - slash "^1.0.0" + npm-normalize-package-bin "^1.0.0" optionalDependencies: graceful-fs "^4.1.2" @@ -8028,13 +8430,14 @@ read-pkg-up@^3.0.0: find-up "^2.0.0" read-pkg "^3.0.0" -read-pkg-up@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-4.0.0.tgz#1b221c6088ba7799601c808f91161c66e58f8978" - integrity sha512-6etQSH7nJGsK0RbG/2TeDzZFa8shjQ1um+SwQQ5cwKy0dhSXdOncEhb1CPpvQG4h7FyOV6EB6YlV0yJvZQNAkA== +read-pkg-up@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-7.0.1.tgz#f3a6135758459733ae2b95638056e1854e7ef507" + integrity sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg== dependencies: - find-up "^3.0.0" - read-pkg "^3.0.0" + find-up "^4.1.0" + read-pkg "^5.2.0" + type-fest "^0.8.1" read-pkg@^1.0.0: version "1.1.0" @@ -8081,9 +8484,9 @@ read@1, read@~1.0.1: mute-stream "~0.0.4" "readable-stream@1 || 2", readable-stream@^2.0.0, readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.0.6, readable-stream@^2.1.5, readable-stream@^2.2.2, readable-stream@^2.3.3, readable-stream@^2.3.5, readable-stream@^2.3.6, readable-stream@~2.3.6: - version "2.3.6" - resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.6.tgz#b11c27d88b8ff1fbe070643cf94b0c79ae1b0aaf" - integrity sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw== + version "2.3.7" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.7.tgz#1eca1cf711aef814c04f62252a36a62f6cb23b57" + integrity sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw== dependencies: core-util-is "~1.0.0" inherits "~2.0.3" @@ -8093,10 +8496,20 @@ read@1, read@~1.0.1: string_decoder "~1.1.1" util-deprecate "~1.0.1" -"readable-stream@2 || 3", readable-stream@^3.0.2: - version "3.4.0" - resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.4.0.tgz#a51c26754658e0a3c21dbf59163bd45ba6f447fc" - integrity sha512-jItXPLmrSR8jmTRmRWJXCnGJsfy85mB3Wd/uINMXA65yrnFo0cPClFIUWzo2najVNSl+mx7/4W8ttlLWJe99pQ== +readable-stream@1.1.x: + version "1.1.14" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.1.14.tgz#7cf4c54ef648e3813084c636dd2079e166c081d9" + integrity sha1-fPTFTvZI44EwhMY23SB54WbAgdk= + dependencies: + core-util-is "~1.0.0" + inherits "~2.0.1" + isarray "0.0.1" + string_decoder "~0.10.x" + +"readable-stream@2 || 3", readable-stream@^3.0.2, readable-stream@^3.1.1, readable-stream@^3.4.0, readable-stream@^3.6.0: + version "3.6.0" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198" + integrity sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA== dependencies: inherits "^2.0.3" string_decoder "^1.1.1" @@ -8121,6 +8534,20 @@ readdirp@^2.2.1: micromatch "^3.1.10" readable-stream "^2.0.2" +readdirp@~3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.2.0.tgz#c30c33352b12c96dfb4b895421a49fd5a9593839" + integrity sha512-crk4Qu3pmXwgxdSgGhgA/eXiJAPQiX4GMOZZMXnqKxHX7TaoL+3gQVo/WeuAiogr07DpnfjIMpXXa+PAIvwPGQ== + dependencies: + picomatch "^2.0.4" + +readdirp@~3.4.0: + version "3.4.0" + resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.4.0.tgz#9fdccdf9e9155805449221ac645e8303ab5b9ada" + integrity sha512-0xe001vZBnJEK+uKcj8qOhyAKPzIT+gStxWr3LCB0DwcXR5NZJ3IaC+yGnHCYzB/S7ov3m3EEbZI2zeNvX+hGQ== + dependencies: + picomatch "^2.2.1" + redent@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/redent/-/redent-1.0.0.tgz#cf916ab1fd5f1f16dfb20822dd6ec7f730c2afde" @@ -8137,24 +8564,50 @@ redent@^2.0.0: indent-string "^3.0.0" strip-indent "^2.0.0" -redis-commands@^1.2.0: +redent@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/redent/-/redent-3.0.0.tgz#e557b7998316bb53c9f1f56fa626352c6963059f" + integrity sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg== + dependencies: + indent-string "^4.0.0" + strip-indent "^3.0.0" + +redis-commands@^1.5.0: version "1.5.0" resolved "https://registry.yarnpkg.com/redis-commands/-/redis-commands-1.5.0.tgz#80d2e20698fe688f227127ff9e5164a7dd17e785" integrity sha512-6KxamqpZ468MeQC3bkWmCB1fp56XL64D4Kf0zJSwDZbVLLm7KFkoIcHrgRvQ+sk8dnhySs7+yBg94yIkAK7aJg== -redis-parser@^2.6.0: - version "2.6.0" - resolved "https://registry.yarnpkg.com/redis-parser/-/redis-parser-2.6.0.tgz#52ed09dacac108f1a631c07e9b69941e7a19504b" - integrity sha1-Uu0J2srBCPGmMcB+m2mUHnoZUEs= +redis-errors@^1.0.0, redis-errors@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/redis-errors/-/redis-errors-1.2.0.tgz#eb62d2adb15e4eaf4610c04afe1529384250abad" + integrity sha1-62LSrbFeTq9GEMBK/hUpOEJQq60= + +redis-parser@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/redis-parser/-/redis-parser-3.0.0.tgz#b66d828cdcafe6b4b8a428a7def4c6bcac31c8b4" + integrity sha1-tm2CjNyv5rS4pCin3vTGvKwxyLQ= + dependencies: + redis-errors "^1.0.0" -redis@2.8.0: - version "2.8.0" - resolved "https://registry.yarnpkg.com/redis/-/redis-2.8.0.tgz#202288e3f58c49f6079d97af7a10e1303ae14b02" - integrity sha512-M1OkonEQwtRmZv4tEWF2VgpG0JWJ8Fv1PhlgT5+B+uNq2cA3Rt1Yt/ryoR+vQNOQcIEgdCdfH0jr3bDpihAw1A== +redis@3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/redis/-/redis-3.0.2.tgz#bd47067b8a4a3e6a2e556e57f71cc82c7360150a" + integrity sha512-PNhLCrjU6vKVuMOyFu7oSP296mwBkcE6lrAjruBYG5LgdSqtRBoVQIylrMyVZD/lkF24RSNNatzvYag6HRBHjQ== dependencies: - double-ended-queue "^2.1.0-0" - redis-commands "^1.2.0" - redis-parser "^2.6.0" + denque "^1.4.1" + redis-commands "^1.5.0" + redis-errors "^1.2.0" + redis-parser "^3.0.0" + +reflect-metadata@^0.1.13: + version "0.1.13" + resolved "https://registry.yarnpkg.com/reflect-metadata/-/reflect-metadata-0.1.13.tgz#67ae3ca57c972a2aa1642b10fe363fe32d49dc08" + integrity sha512-Ts1Y/anZELhSsjMcU605fU9RE4Oi3p5ORujwbIKXfWa+0Zxs510Qrmrce5/Jowq3cHSZSJqBjypxmHarc+vEWg== + +regenerator-runtime@^0.13.4: + version "0.13.5" + resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.5.tgz#d878a1d094b4306d10b9096484b33ebd55e26697" + integrity sha512-ZS5w8CpKFinUzOwW3c83oPeVXoNsrLsaCoLtJvAClH135j/R77RuymhiSErhm2lKcwSCIpmvIWSbDkIfAqKQlA== regex-not@^1.0.0, regex-not@^1.0.2: version "1.0.2" @@ -8164,25 +8617,37 @@ regex-not@^1.0.0, regex-not@^1.0.2: extend-shallow "^3.0.2" safe-regex "^1.1.0" +regexp.prototype.flags@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.3.0.tgz#7aba89b3c13a64509dabcf3ca8d9fbb9bdf5cb75" + integrity sha512-2+Q0C5g951OlYlJz6yu5/M33IcsESLlLfsyIaLJaG4FA2r4yP8MvVMJUUP/fVBkSpbbbZlS5gynbEWLipiiXiQ== + dependencies: + define-properties "^1.1.3" + es-abstract "^1.17.0-next.1" + regexpp@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-2.0.1.tgz#8d19d31cf632482b589049f8281f93dbcba4d07f" integrity sha512-lv0M6+TkDVniA3aD1Eg0DVpfU/booSu7Eev3TDO/mZKHBfVjgCGTV4t4buppESEYDtkArYFOxTJWv6S5C+iaNw== -registry-auth-token@^3.0.1: - version "3.4.0" - resolved "https://registry.yarnpkg.com/registry-auth-token/-/registry-auth-token-3.4.0.tgz#d7446815433f5d5ed6431cd5dca21048f66b397e" - integrity sha512-4LM6Fw8eBQdwMYcES4yTnn2TqIasbXuwDx3um+QRs7S55aMKCBKBxvPXl2RiUjHwuJLTyYfxSpmfSAjQpcuP+A== +regexpp@^3.0.0, regexpp@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-3.1.0.tgz#206d0ad0a5648cffbdb8ae46438f3dc51c9f78e2" + integrity sha512-ZOIzd8yVsQQA7j8GCSlPGXwg5PfmA1mrq0JP4nGhh54LaKN3xdai/vHUDu74pKwV8OxseMS65u2NImosQcSD0Q== + +registry-auth-token@^4.0.0: + version "4.1.1" + resolved "https://registry.yarnpkg.com/registry-auth-token/-/registry-auth-token-4.1.1.tgz#40a33be1e82539460f94328b0f7f0f84c16d9479" + integrity sha512-9bKS7nTl9+/A1s7tnPeGrUpRcVY+LUh7bfFgzpndALdPfXQBfQV77rQVtqgUV3ti4vc/Ik81Ex8UJDWDQ12zQA== dependencies: - rc "^1.1.6" - safe-buffer "^5.0.1" + rc "^1.2.8" -registry-url@^3.0.3: - version "3.1.0" - resolved "https://registry.yarnpkg.com/registry-url/-/registry-url-3.1.0.tgz#3d4ef870f73dde1d77f0cf9a381432444e174942" - integrity sha1-PU74cPc93h138M+aOBQyRE4XSUI= +registry-url@^5.0.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/registry-url/-/registry-url-5.1.0.tgz#e98334b50d5434b81136b44ec638d9c2009c5009" + integrity sha512-8acYXXTI0AkQv6RAOjE3vOaIXZkT9wo4LOFbBKYQEEnnMNBpKqdUrI6S4NT0KPIo/WVvJ5tE/X5LF/TQUf0ekw== dependencies: - rc "^1.0.1" + rc "^1.2.8" release-zalgo@^1.0.0: version "1.0.0" @@ -8213,34 +8678,27 @@ repeating@^2.0.0: dependencies: is-finite "^1.0.0" -request-progress@3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/request-progress/-/request-progress-3.0.0.tgz#4ca754081c7fec63f505e4faa825aa06cd669dbe" - integrity sha1-TKdUCBx/7GP1BeT6qCWqBs1mnb4= - dependencies: - throttleit "^1.0.0" - -request-promise-core@1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/request-promise-core/-/request-promise-core-1.1.2.tgz#339f6aababcafdb31c799ff158700336301d3346" - integrity sha512-UHYyq1MO8GsefGEt7EprS8UrXsm1TxEvFUX1IMTuSLU2Rh7fTIdFtl8xD7JiEYiWU2dl+NYAjCTksTehQUxPag== +request-promise-core@1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/request-promise-core/-/request-promise-core-1.1.3.tgz#e9a3c081b51380dfea677336061fea879a829ee9" + integrity sha512-QIs2+ArIGQVp5ZYbWD5ZLCY29D5CfWizP8eWnm8FoGD1TX61veauETVQbrV60662V0oFBkrDOuaBI8XgtuyYAQ== dependencies: - lodash "^4.17.11" + lodash "^4.17.15" -request-promise@4.2.4: - version "4.2.4" - resolved "https://registry.yarnpkg.com/request-promise/-/request-promise-4.2.4.tgz#1c5ed0d71441e38ad58c7ce4ea4ea5b06d54b310" - integrity sha512-8wgMrvE546PzbR5WbYxUQogUnUDfM0S7QIFZMID+J73vdFARkFy+HElj4T+MWYhpXwlLp0EQ8Zoj8xUA0he4Vg== +request-promise@4.2.5: + version "4.2.5" + resolved "https://registry.yarnpkg.com/request-promise/-/request-promise-4.2.5.tgz#186222c59ae512f3497dfe4d75a9c8461bd0053c" + integrity sha512-ZgnepCykFdmpq86fKGwqntyTiUrHycALuGggpyCZwMvGaZWgxW6yagT0FHkgo5LzYvOaCNvxYwWYIjevSH1EDg== dependencies: bluebird "^3.5.0" - request-promise-core "1.1.2" + request-promise-core "1.1.3" stealthy-require "^1.1.1" tough-cookie "^2.3.3" -request@2.88.0, request@^2.69.0, request@^2.72.0, request@^2.74.0, request@^2.87.0: - version "2.88.0" - resolved "https://registry.yarnpkg.com/request/-/request-2.88.0.tgz#9c2fca4f7d35b592efe57c7f0a55e81052124fef" - integrity sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg== +request@2.88.2, request@^2.88.0: + version "2.88.2" + resolved "https://registry.yarnpkg.com/request/-/request-2.88.2.tgz#d73c918731cb5a87da047e207234146f664d12b3" + integrity sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw== dependencies: aws-sign2 "~0.7.0" aws4 "^1.8.0" @@ -8249,7 +8707,7 @@ request@2.88.0, request@^2.69.0, request@^2.72.0, request@^2.74.0, request@^2.87 extend "~3.0.2" forever-agent "~0.6.1" form-data "~2.3.2" - har-validator "~5.1.0" + har-validator "~5.1.3" http-signature "~1.2.0" is-typedarray "~1.0.0" isstream "~0.1.2" @@ -8259,7 +8717,7 @@ request@2.88.0, request@^2.69.0, request@^2.72.0, request@^2.74.0, request@^2.87 performance-now "^2.1.0" qs "~6.5.2" safe-buffer "^5.1.2" - tough-cookie "~2.4.3" + tough-cookie "~2.5.0" tunnel-agent "^0.6.0" uuid "^3.3.2" @@ -8298,25 +8756,29 @@ resolve-from@^4.0.0: resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6" integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g== +resolve-from@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-5.0.0.tgz#c35225843df8f776df21c57557bc087e9dfdfc69" + integrity sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw== + resolve-url@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a" integrity sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo= -resolve@^1.10.0, resolve@^1.10.1, resolve@^1.11.0, resolve@^1.5.0: - version "1.11.1" - resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.11.1.tgz#ea10d8110376982fef578df8fc30b9ac30a07a3e" - integrity sha512-vIpgF6wfuJOZI7KKKSP+HmiKggadPQAdsp5HiC1mvqnfp0gF1vdwgBWZIdrVft9pgqoMFQN+R7BSWZiBxx+BBw== +resolve@^1.10.0, resolve@^1.12.0, resolve@^1.13.1, resolve@^1.15.1, resolve@^1.3.2: + version "1.17.0" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.17.0.tgz#b25941b54968231cc2d1bb76a79cb7f2c0bf8444" + integrity sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w== dependencies: path-parse "^1.0.6" -restore-cursor@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-1.0.1.tgz#34661f46886327fed2991479152252df92daa541" - integrity sha1-NGYfRohjJ/7SmRR5FSJS35LapUE= +responselike@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/responselike/-/responselike-1.0.2.tgz#918720ef3b631c5642be068f15ade5a46f4ba1e7" + integrity sha1-kYcg7ztjHFZCvgaPFa3lpG9Loec= dependencies: - exit-hook "^1.0.0" - onetime "^1.0.0" + lowercase-keys "^1.0.0" restore-cursor@^2.0.0: version "2.0.0" @@ -8326,32 +8788,47 @@ restore-cursor@^2.0.0: onetime "^2.0.0" signal-exit "^3.0.2" +restore-cursor@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-3.1.0.tgz#39f67c54b3a7a58cea5236d95cf0034239631f7e" + integrity sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA== + dependencies: + onetime "^5.1.0" + signal-exit "^3.0.2" + ret@~0.1.10: version "0.1.15" resolved "https://registry.yarnpkg.com/ret/-/ret-0.1.15.tgz#b8a4825d5bdb1fc3f6f53c2bc33f81388681c7bc" integrity sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg== +retry@0.12.0: + version "0.12.0" + resolved "https://registry.yarnpkg.com/retry/-/retry-0.12.0.tgz#1b42a6266a21f07421d1b0b54b7dc167b01c013b" + integrity sha1-G0KmJmoh8HQh0bC1S33BZ7AcATs= + retry@^0.10.0: version "0.10.1" resolved "https://registry.yarnpkg.com/retry/-/retry-0.10.1.tgz#e76388d217992c252750241d3d3956fed98d8ff4" integrity sha1-52OI0heZLCUnUCQdPTlW/tmNj/Q= -retry@^0.12.0: - version "0.12.0" - resolved "https://registry.yarnpkg.com/retry/-/retry-0.12.0.tgz#1b42a6266a21f07421d1b0b54b7dc167b01c013b" - integrity sha1-G0KmJmoh8HQh0bC1S33BZ7AcATs= +rimraf@2.6.3: + version "2.6.3" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.3.tgz#b2d104fe0d8fb27cf9e0a1cda8262dd3833c6cab" + integrity sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA== + dependencies: + glob "^7.1.3" -rimraf@2: +rimraf@^2.5.4, rimraf@^2.6.2, rimraf@^2.6.3: version "2.7.1" resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.7.1.tgz#35797f13a7fdadc566142c29d4f07ccad483e3ec" integrity sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w== dependencies: glob "^7.1.3" -rimraf@2.6.3, rimraf@^2.5.4, rimraf@^2.6.1, rimraf@^2.6.2, rimraf@^2.6.3: - version "2.6.3" - resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.3.tgz#b2d104fe0d8fb27cf9e0a1cda8262dd3833c6cab" - integrity sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA== +rimraf@^3.0.0: + version "3.0.2" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a" + integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA== dependencies: glob "^7.1.3" @@ -8363,17 +8840,10 @@ ripemd160@^2.0.0, ripemd160@^2.0.1: hash-base "^3.0.0" inherits "^2.0.1" -run-async@^2.2.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.3.0.tgz#0371ab4ae0bdd720d4166d7dfda64ff7a445a6c0" - integrity sha1-A3GrSuC91yDUFm19/aZP96RFpsA= - dependencies: - is-promise "^2.1.0" - -run-node@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/run-node/-/run-node-1.0.0.tgz#46b50b946a2aa2d4947ae1d886e9856fd9cabe5e" - integrity sha512-kc120TBlQ3mih1LSzdAJXo4xn/GWS2ec0l3S+syHDXP9uRr0JAT8Qd3mdMuyjqCzeZktgP3try92cEgf9Nks8A== +run-async@^2.2.0, run-async@^2.4.0: + version "2.4.1" + resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.4.1.tgz#8440eccf99ea3e70bd409d49aab88e10c189a455" + integrity sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ== run-parallel@^1.1.4: version "1.1.9" @@ -8387,17 +8857,10 @@ run-queue@^1.0.0, run-queue@^1.0.3: dependencies: aproba "^1.1.1" -rxjs@^5.0.0-beta.11: - version "5.5.12" - resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-5.5.12.tgz#6fa61b8a77c3d793dbaf270bee2f43f652d741cc" - integrity sha512-xx2itnL5sBbqeeiVgNPVuQQ1nC8Jp2WfNJhXWHmElW9YmrpS9UVnNzhP3EH3HFqexO5Tlp8GhYY+WEcqcVMvGw== - dependencies: - symbol-observable "1.0.1" - -rxjs@^6.4.0: - version "6.5.2" - resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.5.2.tgz#2e35ce815cd46d84d02a209fb4e5921e051dbec7" - integrity sha512-HUb7j3kvb7p7eCUHE3FqjoDsC1xfZQ4AHFWfTKSpZ+sAhhz5X1WX0ZuUqWbzB2QhSLp3DoLUG+hMdEDKqWo2Zg== +rxjs@^6.4.0, rxjs@^6.5.3: + version "6.5.5" + resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.5.5.tgz#c5c884e3094c8cfee31bf27eb87e54ccfc87f9ec" + integrity sha512-WfQI+1gohdf0Dai/Bbmk5L5ItH5tYqm3ki2c5GdWhKjalzjg93N3avFjVStyZZz+A2Em+ZxKH5bNghw9UeylGQ== dependencies: tslib "^1.9.0" @@ -8407,9 +8870,9 @@ safe-buffer@5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1: integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.1, safe-buffer@^5.1.2, safe-buffer@^5.2.0, safe-buffer@~5.2.0: - version "5.2.0" - resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.0.tgz#b74daec49b1148f88c64b68d49b1e815c1f2f519" - integrity sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg== + version "5.2.1" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" + integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== safe-regex@^1.1.0: version "1.1.0" @@ -8423,19 +8886,16 @@ safe-regex@^1.1.0: resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== -sax@>=0.6.0, sax@^1.2.4: +sax@1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.1.tgz#7b8e656190b228e81a66aea748480d828cd2d37a" + integrity sha1-e45lYZCyKOgaZq6nSEgNgozS03o= + +sax@>=0.6.0: version "1.2.4" resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9" integrity sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw== -scheduler@^0.16.2: - version "0.16.2" - resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.16.2.tgz#f74cd9d33eff6fc554edfb79864868e4819132c1" - integrity sha512-BqYVWqwz6s1wZMhjFvLfVR5WXP7ZY32M/wYPo04CcuPM7XZEbV2TBNW7Z0UkguPTl0dWMA59VbNXxK6q+pHItg== - dependencies: - loose-envify "^1.1.0" - object-assign "^4.1.1" - schema-utils@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-1.0.0.tgz#0b79a93204d7b600d4b2850d1f66c2a34951c770" @@ -8450,19 +8910,19 @@ semver-compare@^1.0.0: resolved "https://registry.yarnpkg.com/semver-compare/-/semver-compare-1.0.0.tgz#0dee216a1c941ab37e9efb1788f6afc5ff5537fc" integrity sha1-De4hahyUGrN+nvsXiPavxf9VN/w= -semver-diff@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/semver-diff/-/semver-diff-2.1.0.tgz#4bbb8437c8d37e4b0cf1a68fd726ec6d645d6d36" - integrity sha1-S7uEN8jTfksM8aaP1ybsbWRdbTY= +semver-diff@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/semver-diff/-/semver-diff-3.1.1.tgz#05f77ce59f325e00e2706afd67bb506ddb1ca32b" + integrity sha512-GX0Ix/CJcHyB8c4ykpHGIAvLyOwOobtM/8d+TQkAd81/bEjgPHrfba41Vpesr7jX/t8Uh+R3EX9eAS5be+jQYg== dependencies: - semver "^5.0.3" + semver "^6.3.0" -"semver@2 || 3 || 4 || 5", semver@^5.0.3, semver@^5.1.0, semver@^5.3.0, semver@^5.5.0, semver@^5.5.1, semver@^5.6.0, semver@^5.7.0: - version "5.7.0" - resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.0.tgz#790a7cf6fea5459bac96110b29b60412dc8ff96b" - integrity sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA== +semver-regex@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/semver-regex/-/semver-regex-2.0.0.tgz#a93c2c5844539a770233379107b38c7b4ac9d338" + integrity sha512-mUdIBBvdn0PLOeP3TEkMH7HHeUP3GjsXCwKarjv/kGmUFOYg1VqEemKhoQpWMu6X2I8kHeuVdGibLGkVK+/5Qw== -"semver@2.x || 3.x || 4 || 5", semver@^5.4.1, semver@^5.7.1: +"semver@2 || 3 || 4 || 5", "semver@2.x || 3.x || 4 || 5", semver@^5.4.1, semver@^5.5.0, semver@^5.5.1, semver@^5.6.0, semver@^5.7.0, semver@^5.7.1: version "5.7.1" resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== @@ -8472,15 +8932,15 @@ semver@4.3.2: resolved "https://registry.yarnpkg.com/semver/-/semver-4.3.2.tgz#c7a07158a80bedd052355b770d82d6640f803be7" integrity sha1-x6BxWKgL7dBSNVt3DYLWZA+AO+c= -semver@^6.0.0, semver@^6.1.2, semver@^6.2.0, semver@^6.3.0: +semver@^6.0.0, semver@^6.2.0, semver@^6.3.0: version "6.3.0" resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== -semver@~5.3.0: - version "5.3.0" - resolved "https://registry.yarnpkg.com/semver/-/semver-5.3.0.tgz#9b2ce5d3de02d17c6012ad326aa6b4d0cf54f94f" - integrity sha1-myzl094C0XxgEq0yaqa00M9U+U8= +semver@^7.1.3, semver@^7.2.1, semver@^7.3.2: + version "7.3.2" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.2.tgz#604962b052b81ed0786aae84389ffba70ffd3938" + integrity sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ== send@0.17.1: version "0.17.1" @@ -8501,10 +8961,10 @@ send@0.17.1: range-parser "~1.2.1" statuses "~1.5.0" -serialize-javascript@^1.7.0: - version "1.7.0" - resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-1.7.0.tgz#d6e0dfb2a3832a8c94468e6eb1db97e55a192a65" - integrity sha512-ke8UG8ulpFOxO8f8gRYabHQe/ZntKlcig2Mp+8+URDP1D8vJZ0KUt7LYo07q25Z/+JVSgpr/cui9PIp5H6/+nA== +serialize-javascript@^2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-2.1.2.tgz#ecec53b0e0317bdc95ef76ab7074b7384785fa61" + integrity sha512-rs9OggEUF0V4jUSecXazOYsLfu7OGK2qIn3c7IPBiffz32XniEp/TX9Xmc9LQfK2nQ2QKHvZ2oygKUGU0lG4jQ== serve-static@1.14.1: version "1.14.1" @@ -8541,7 +9001,7 @@ setprototypeof@1.1.1: resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.1.1.tgz#7e95acb24aa92f5885e0abef5ba131330d4ae683" integrity sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw== -sha.js@^2.4.0, sha.js@^2.4.8: +sha.js@^2.4.0, sha.js@^2.4.11, sha.js@^2.4.8: version "2.4.11" resolved "https://registry.yarnpkg.com/sha.js/-/sha.js-2.4.11.tgz#37a5cf0b81ecbc6943de109ba2960d1b26584ae7" integrity sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ== @@ -8549,6 +9009,28 @@ sha.js@^2.4.0, sha.js@^2.4.8: inherits "^2.0.1" safe-buffer "^5.0.1" +shallow-clone@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/shallow-clone/-/shallow-clone-3.0.1.tgz#8f2981ad92531f55035b01fb230769a40e02efa3" + integrity sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA== + dependencies: + kind-of "^6.0.2" + +sharp@0.25.2: + version "0.25.2" + resolved "https://registry.yarnpkg.com/sharp/-/sharp-0.25.2.tgz#f9003d73be50e9265e98f79f04fe53d8c66a3967" + integrity sha512-l1GN0kFNtJr3U9i9pt7a+vo2Ij0xv4tTKDIPx8W6G9WELhPwrMyZZJKAAQNBSI785XB4uZfS5Wpz8C9jWV4AFQ== + dependencies: + color "^3.1.2" + detect-libc "^1.0.3" + node-addon-api "^2.0.0" + npmlog "^4.1.2" + prebuild-install "^5.3.3" + semver "^7.1.3" + simple-get "^3.1.0" + tar "^6.0.1" + tunnel-agent "^0.6.0" + shebang-command@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-1.2.0.tgz#44aac65b695b03398968c39f363fee5deafdf1ea" @@ -8556,11 +9038,23 @@ shebang-command@^1.2.0: dependencies: shebang-regex "^1.0.0" +shebang-command@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea" + integrity sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA== + dependencies: + shebang-regex "^3.0.0" + shebang-regex@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-1.0.0.tgz#da42f49740c0b42db2ca9728571cb190c98efea3" integrity sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM= +shebang-regex@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" + integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== + should-equal@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/should-equal/-/should-equal-2.0.0.tgz#6072cf83047360867e68e98b09d71143d04ee0c3" @@ -8594,7 +9088,7 @@ should-util@^1.0.0: resolved "https://registry.yarnpkg.com/should-util/-/should-util-1.0.1.tgz#fb0d71338f532a3a149213639e2d32cbea8bcb28" integrity sha512-oXF8tfxx5cDk8r2kYqlkUJzZpDBqVY/II2WhvU0n9Y3XYvAYRmeaf1PvvIvTgPnv4KJ+ES5M0PyDq5Jp+Ygy2g== -should@*, should@13.2.3, should@^13.2.3: +should@13.2.3, should@^13.2.3: version "13.2.3" resolved "https://registry.yarnpkg.com/should/-/should-13.2.3.tgz#96d8e5acf3e97b49d89b51feaa5ae8d07ef58f10" integrity sha512-ggLesLtu2xp+ZxI+ysJTmNjh2U0TsC+rQ/pfED9bUZZ4DKefP27D+7YJVVTvKsmjLpIi9jAa7itwDGkDDmt1GQ== @@ -8605,15 +9099,62 @@ should@*, should@13.2.3, should@^13.2.3: should-type-adaptors "^1.0.1" should-util "^1.0.0" +side-channel@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.2.tgz#df5d1abadb4e4bf4af1cd8852bf132d2f7876947" + integrity sha512-7rL9YlPHg7Ancea1S96Pa8/QWb4BtXL/TZvS6B8XFetGBeuhAsfmUspK6DokBeZ64+Kj9TCNRD/30pVz1BvQNA== + dependencies: + es-abstract "^1.17.0-next.1" + object-inspect "^1.7.0" + signal-exit@^3.0.0, signal-exit@^3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.2.tgz#b5fdc08f1287ea1178628e415e25132b73646c6d" - integrity sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0= + version "3.0.3" + resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.3.tgz#a1410c2edd8f077b08b4e253c8eacfcaf057461c" + integrity sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA== + +simple-concat@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/simple-concat/-/simple-concat-1.0.0.tgz#7344cbb8b6e26fb27d66b2fc86f9f6d5997521c6" + integrity sha1-c0TLuLbib7J9ZrL8hvn21Zl1IcY= + +simple-get@^3.0.3, simple-get@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/simple-get/-/simple-get-3.1.0.tgz#b45be062435e50d159540b576202ceec40b9c6b3" + integrity sha512-bCR6cP+aTdScaQCnQKbPKtJOKDp/hj9EDLJo3Nw4y1QksqaovlW/bnptB6/c1e+qmNIDHRK+oXFDdEqBT8WzUA== + dependencies: + decompress-response "^4.2.0" + once "^1.3.1" + simple-concat "^1.0.0" + +simple-swizzle@^0.2.2: + version "0.2.2" + resolved "https://registry.yarnpkg.com/simple-swizzle/-/simple-swizzle-0.2.2.tgz#a4da6b635ffcccca33f70d17cb92592de95e557a" + integrity sha1-pNprY1/8zMoz9w0Xy5JZLeleVXo= + dependencies: + is-arrayish "^0.3.1" + +sinon-express-mock@2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/sinon-express-mock/-/sinon-express-mock-2.2.1.tgz#245646f06710efd98134dbe15b3fd153ebb0e67e" + integrity sha512-z1wqaPMwEnfn0SpigFhVYVS/ObX1tkqyRzRdccX99FgQaLkxGSo4684unr3NCqWeYZ1zchxPw7oFIDfzg1cAjg== sinon-test@^2.4.0: - version "2.4.0" - resolved "https://registry.yarnpkg.com/sinon-test/-/sinon-test-2.4.0.tgz#dffb7caaac75107823c30c9152aeebd44e61adb1" - integrity sha512-oQnO02I7JDbtrSKN8Qs3upobCQRythJCBn3DzPmv4m/SoPvhZJDVqHDFkRZ1lZhN2GkBqOR3m7WT79190z9kEg== + version "2.4.3" + resolved "https://registry.yarnpkg.com/sinon-test/-/sinon-test-2.4.3.tgz#1353565ee2e8b5f6bb0d7d47f0b69c1fa470ffd7" + integrity sha512-si0GRSeYtEFj67t/nyR5BKTWhFTerFJ7Xw88EIhUT5G99bpJ0HOxZiHuJ1DFqS6S7i0RqU23H9GaVxAVf/hxow== + +sinon@9.0.2: + version "9.0.2" + resolved "https://registry.yarnpkg.com/sinon/-/sinon-9.0.2.tgz#b9017e24633f4b1c98dfb6e784a5f0509f5fd85d" + integrity sha512-0uF8Q/QHkizNUmbK3LRFqx5cpTttEVXudywY9Uwzy8bTfZUhljZ7ARzSxnRHWYWtVTeh4Cw+tTb3iU21FQVO9A== + dependencies: + "@sinonjs/commons" "^1.7.2" + "@sinonjs/fake-timers" "^6.0.1" + "@sinonjs/formatio" "^5.0.1" + "@sinonjs/samsam" "^5.0.3" + diff "^4.0.2" + nise "^4.0.1" + supports-color "^7.1.0" sinon@^7.5.0: version "7.5.0" @@ -8628,11 +9169,6 @@ sinon@^7.5.0: nise "^1.5.2" supports-color "^5.5.0" -slash@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/slash/-/slash-1.0.0.tgz#c41f2f6c39fc16d1cd17ad4b5d896114ae470d55" - integrity sha1-xB8vbDn8FtHNF61LXYlhFK5HDVU= - slash@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/slash/-/slash-2.0.0.tgz#de552851a1759df3a8f206535442f5ec4ddeab44" @@ -8643,11 +9179,6 @@ slash@^3.0.0: resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634" integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q== -slice-ansi@0.0.4: - version "0.0.4" - resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-0.0.4.tgz#edbf8903f66f7ce2f8eafd6ceed65e264c831b35" - integrity sha1-7b+JA/ZvfOL46v1s7tZeJkyDGzU= - slice-ansi@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-2.1.0.tgz#cacd7693461a637a5788d92a7dd4fba068e81636" @@ -8662,10 +9193,10 @@ slide@^1.1.6: resolved "https://registry.yarnpkg.com/slide/-/slide-1.1.6.tgz#56eb027d65b4d2dce6cb2e2d32c4d4afc9e1d707" integrity sha1-VusCfWW00tzmyy4tMsTUr8nh1wc= -smart-buffer@4.0.2: - version "4.0.2" - resolved "https://registry.yarnpkg.com/smart-buffer/-/smart-buffer-4.0.2.tgz#5207858c3815cc69110703c6b94e46c15634395d" - integrity sha512-JDhEpTKzXusOqXZ0BUIdH+CjFdO/CR3tLlf5CN34IypI+xMmXW1uB16OOY8z3cICbJlDAVJzNbwBhNO0wt9OAw== +smart-buffer@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/smart-buffer/-/smart-buffer-4.1.0.tgz#91605c25d91652f4661ea69ccf45f1b331ca21ba" + integrity sha512-iVICrxOzCynf/SNaBQCw34eM9jROU/s5rzIhpOvzhzuYHfJR/DhZfDkXiZSgKXfgv26HT3Yni3AV/DGw0cGnnw== snapdragon-node@^2.0.1: version "2.1.1" @@ -8706,12 +9237,12 @@ socks-proxy-agent@^4.0.0: socks "~2.3.2" socks@~2.3.2: - version "2.3.2" - resolved "https://registry.yarnpkg.com/socks/-/socks-2.3.2.tgz#ade388e9e6d87fdb11649c15746c578922a5883e" - integrity sha512-pCpjxQgOByDHLlNqlnh/mNSAxIUkyBBuwwhTcV+enZGbDaClPvHdvm6uvOwZfFJkam7cGhBNbb4JxiP8UZkRvQ== + version "2.3.3" + resolved "https://registry.yarnpkg.com/socks/-/socks-2.3.3.tgz#01129f0a5d534d2b897712ed8aceab7ee65d78e3" + integrity sha512-o5t52PCNtVdiOvzMry7wU4aOqYWL0PeCXRWBEiJow4/i/wr+wpsJQ9awEu1EonLIqsfGd5qSgDdxEOvCdmBEpA== dependencies: - ip "^1.1.5" - smart-buffer "4.0.2" + ip "1.1.5" + smart-buffer "^4.1.0" sort-keys@^2.0.0: version "2.0.0" @@ -8726,28 +9257,20 @@ source-list-map@^2.0.0: integrity sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw== source-map-resolve@^0.5.0: - version "0.5.2" - resolved "https://registry.yarnpkg.com/source-map-resolve/-/source-map-resolve-0.5.2.tgz#72e2cc34095543e43b2c62b2c4c10d4a9054f259" - integrity sha512-MjqsvNwyz1s0k81Goz/9vRBe9SZdB09Bdw+/zYyO+3CuPk6fouTaxscHkgtE8jKvf01kVfl8riHzERQ/kefaSA== + version "0.5.3" + resolved "https://registry.yarnpkg.com/source-map-resolve/-/source-map-resolve-0.5.3.tgz#190866bece7553e1f8f267a2ee82c606b5509a1a" + integrity sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw== dependencies: - atob "^2.1.1" + atob "^2.1.2" decode-uri-component "^0.2.0" resolve-url "^0.2.1" source-map-url "^0.4.0" urix "^0.1.0" -source-map-support@0.5.13: - version "0.5.13" - resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.13.tgz#31b24a9c2e73c2de85066c0feb7d44767ed52932" - integrity sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w== - dependencies: - buffer-from "^1.0.0" - source-map "^0.6.0" - -source-map-support@^0.5.6, source-map-support@~0.5.12: - version "0.5.12" - resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.12.tgz#b4f3b10d51857a5af0138d3ce8003b201613d599" - integrity sha512-4h2Pbvyy15EE02G+JOZpUCmqWJuqrs+sEkzewTm++BPi7Hvn/HwcqLAcNxYAyI0x13CpPPn+kMjl+hplXMHITQ== +source-map-support@0.5.19, source-map-support@^0.5.17, source-map-support@^0.5.6, source-map-support@~0.5.12: + version "0.5.19" + resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.19.tgz#a98b62f86dcaf4f67399648c085291ab9e8fed61" + integrity sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw== dependencies: buffer-from "^1.0.0" source-map "^0.6.0" @@ -8767,17 +9290,17 @@ source-map@^0.6.0, source-map@^0.6.1, source-map@~0.6.1: resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== -spawn-wrap@^1.4.2: - version "1.4.2" - resolved "https://registry.yarnpkg.com/spawn-wrap/-/spawn-wrap-1.4.2.tgz#cff58e73a8224617b6561abdc32586ea0c82248c" - integrity sha512-vMwR3OmmDhnxCVxM8M+xO/FtIp6Ju/mNaDfCMMW7FDcLRTPFWUswec4LXJHTJE2hwTI9O0YBfygu4DalFl7Ylg== +spawn-wrap@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/spawn-wrap/-/spawn-wrap-2.0.0.tgz#103685b8b8f9b79771318827aa78650a610d457e" + integrity sha512-EeajNjfN9zMnULLwhZZQU3GWBoFNkbngTUPfaawT4RkMiviTxcX0qfhVbGey39mfctfDHkWtuecgQ8NJcyQWHg== dependencies: - foreground-child "^1.5.6" - mkdirp "^0.5.0" - os-homedir "^1.0.1" - rimraf "^2.6.2" + foreground-child "^2.0.0" + is-windows "^1.0.2" + make-dir "^3.0.0" + rimraf "^3.0.0" signal-exit "^3.0.2" - which "^1.3.0" + which "^2.0.1" spdx-correct@^3.0.0: version "3.1.0" @@ -8788,14 +9311,14 @@ spdx-correct@^3.0.0: spdx-license-ids "^3.0.0" spdx-exceptions@^2.1.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/spdx-exceptions/-/spdx-exceptions-2.2.0.tgz#2ea450aee74f2a89bfb94519c07fcd6f41322977" - integrity sha512-2XQACfElKi9SlVb1CYadKDXvoajPgBVPn/gOQLrTvHdElaVhr7ZEbqJaRnJLVNeaI4cMEAgVCeBMKF6MWRDCRA== + version "2.3.0" + resolved "https://registry.yarnpkg.com/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz#3f28ce1a77a00372683eade4a433183527a2163d" + integrity sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A== spdx-expression-parse@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz#99e119b7a5da00e05491c9fa338b7904823b41d0" - integrity sha512-Yg6D3XpRD4kkOmTpdgbUiEJFKghJH03fiC1OPll5h/0sO6neh2jqRDVHOQ4o/LMea0tgCkbMgea5ip/e+MkWyg== + version "3.0.1" + resolved "https://registry.yarnpkg.com/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz#cf70f50482eefdc98e3ce0a6833e4a53ceeba679" + integrity sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q== dependencies: spdx-exceptions "^2.1.0" spdx-license-ids "^3.0.0" @@ -8899,14 +9422,14 @@ stream-http@^2.7.2: xtend "^4.0.0" stream-shift@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/stream-shift/-/stream-shift-1.0.0.tgz#d5c752825e5367e786f78e18e445ea223a155952" - integrity sha1-1cdSgl5TZ+eG944Y5EXqIjoVWVI= + version "1.0.1" + resolved "https://registry.yarnpkg.com/stream-shift/-/stream-shift-1.0.1.tgz#d7088281559ab2778424279b0877da3c392d5a3d" + integrity sha512-AiisoFqQ0vbGcZgQPY1cdP2I76glaVA/RauYR4G4thNFgkTqr90yXTo4LYX60Jl+sIlPNHHdGSwo01AvbKUSVQ== -stream-to-observable@^0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/stream-to-observable/-/stream-to-observable-0.1.0.tgz#45bf1d9f2d7dc09bed81f1c307c430e68b84cffe" - integrity sha1-Rb8dny19wJvtgfHDB8Qw5ouEz/4= +streamsearch@0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/streamsearch/-/streamsearch-0.1.2.tgz#808b9d0e56fc273d809ba57338e929919a1a9f1a" + integrity sha1-gIudDlb8Jz2Am6VzOOkpkZoanxo= string-width@^1.0.1: version "1.0.2" @@ -8917,37 +9440,90 @@ string-width@^1.0.1: is-fullwidth-code-point "^1.0.0" strip-ansi "^3.0.0" -"string-width@^1.0.2 || 2", string-width@^2.0.0, string-width@^2.1.0, string-width@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e" - integrity sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw== +"string-width@^1.0.2 || 2", string-width@^2.1.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e" + integrity sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw== + dependencies: + is-fullwidth-code-point "^2.0.0" + strip-ansi "^4.0.0" + +string-width@^3.0.0, string-width@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-3.1.0.tgz#22767be21b62af1081574306f69ac51b62203961" + integrity sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w== + dependencies: + emoji-regex "^7.0.1" + is-fullwidth-code-point "^2.0.0" + strip-ansi "^5.1.0" + +string-width@^4.0.0, string-width@^4.1.0, string-width@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.0.tgz#952182c46cc7b2c313d1596e623992bd163b72b5" + integrity sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg== + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.0" + +string.prototype.matchall@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/string.prototype.matchall/-/string.prototype.matchall-4.0.2.tgz#48bb510326fb9fdeb6a33ceaa81a6ea04ef7648e" + integrity sha512-N/jp6O5fMf9os0JU3E72Qhf590RSRZU/ungsL/qJUYVTNv7hTG0P/dbPjxINVN9jpscu3nzYwKESU3P3RY5tOg== + dependencies: + define-properties "^1.1.3" + es-abstract "^1.17.0" + has-symbols "^1.0.1" + internal-slot "^1.0.2" + regexp.prototype.flags "^1.3.0" + side-channel "^1.0.2" + +string.prototype.trimend@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.1.tgz#85812a6b847ac002270f5808146064c995fb6913" + integrity sha512-LRPxFUaTtpqYsTeNKaFOw3R4bxIzWOnbQ837QfBylo8jIxtcbK/A/sMV7Q+OAV/vWo+7s25pOE10KYSjaSO06g== + dependencies: + define-properties "^1.1.3" + es-abstract "^1.17.5" + +string.prototype.trimleft@^2.1.1: + version "2.1.2" + resolved "https://registry.yarnpkg.com/string.prototype.trimleft/-/string.prototype.trimleft-2.1.2.tgz#4408aa2e5d6ddd0c9a80739b087fbc067c03b3cc" + integrity sha512-gCA0tza1JBvqr3bfAIFJGqfdRTyPae82+KTnm3coDXkZN9wnuW3HjGgN386D7hfv5CHQYCI022/rJPVlqXyHSw== dependencies: - is-fullwidth-code-point "^2.0.0" - strip-ansi "^4.0.0" + define-properties "^1.1.3" + es-abstract "^1.17.5" + string.prototype.trimstart "^1.0.0" -string-width@^3.0.0, string-width@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-3.1.0.tgz#22767be21b62af1081574306f69ac51b62203961" - integrity sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w== +string.prototype.trimright@^2.1.1: + version "2.1.2" + resolved "https://registry.yarnpkg.com/string.prototype.trimright/-/string.prototype.trimright-2.1.2.tgz#c76f1cef30f21bbad8afeb8db1511496cfb0f2a3" + integrity sha512-ZNRQ7sY3KroTaYjRS6EbNiiHrOkjihL9aQE/8gfQ4DtAC/aEBRHFJa44OmoWxGGqXuJlfKkZW4WcXErGr+9ZFg== dependencies: - emoji-regex "^7.0.1" - is-fullwidth-code-point "^2.0.0" - strip-ansi "^5.1.0" + define-properties "^1.1.3" + es-abstract "^1.17.5" + string.prototype.trimend "^1.0.0" -string_decoder@^1.0.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.2.0.tgz#fe86e738b19544afe70469243b2a1ee9240eae8d" - integrity sha512-6YqyX6ZWEYguAxgZzHGL7SsCeGx3V2TtOTqZz1xSTSWnqsbWwbptafNyvf/ACquZUXV3DANr5BDIwNYe1mN42w== +string.prototype.trimstart@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.1.tgz#14af6d9f34b053f7cfc89b72f8f2ee14b9039a54" + integrity sha512-XxZn+QpvrBI1FOcg6dIpxUPgWCPuNXvMD72aaRaUQv1eD4e/Qy8i/hFTe0BUmD60p/QA6bh1avmuPTfNjqVWRw== dependencies: - safe-buffer "~5.1.0" + define-properties "^1.1.3" + es-abstract "^1.17.5" -string_decoder@^1.1.1: +string_decoder@^1.0.0, string_decoder@^1.1.1: version "1.3.0" resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e" integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA== dependencies: safe-buffer "~5.2.0" +string_decoder@~0.10.x: + version "0.10.31" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-0.10.31.tgz#62e203bc41766c6c28c9fc84301dab1c5310fa94" + integrity sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ= + string_decoder@~1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8" @@ -8976,6 +9552,13 @@ strip-ansi@^5.0.0, strip-ansi@^5.1.0, strip-ansi@^5.2.0: dependencies: ansi-regex "^4.1.0" +strip-ansi@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.0.tgz#0b1571dd7669ccd4f3e06e14ef1eed26225ae532" + integrity sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w== + dependencies: + ansi-regex "^5.0.0" + strip-bom@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-2.0.0.tgz#6219a85616520491f35788bdbf1447a99c7e6b0e" @@ -8988,6 +9571,11 @@ strip-bom@^3.0.0: resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3" integrity sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM= +strip-bom@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-4.0.0.tgz#9c3505c1db45bcedca3d9cf7a16f5c5aa3901878" + integrity sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w== + strip-eof@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/strip-eof/-/strip-eof-1.0.0.tgz#bb43ff5598a6eb05d89b59fcd129c983313606bf" @@ -9005,15 +9593,22 @@ strip-indent@^2.0.0: resolved "https://registry.yarnpkg.com/strip-indent/-/strip-indent-2.0.0.tgz#5ef8db295d01e6ed6cbf7aab96998d7822527b68" integrity sha1-XvjbKV0B5u1sv3qrlpmNeCJSe2g= +strip-indent@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/strip-indent/-/strip-indent-3.0.0.tgz#c32e1cee940b6b3432c771bc2c54bcce73cd3001" + integrity sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ== + dependencies: + min-indent "^1.0.0" + strip-json-comments@2.0.1, strip-json-comments@^2.0.1, strip-json-comments@~2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" integrity sha1-PFMZQukIwml8DsNEhYwobHygpgo= -strip-json-comments@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.0.1.tgz#85713975a91fb87bf1b305cca77395e40d2a64a7" - integrity sha512-VTyMAUfdm047mwKl+u79WIdrZxtFtn+nBxHeb844XBQ9uMNTuTHdx2hc5RiAJYqwTj3wc/xe5HLSdJSkJ+WfZw== +strip-json-comments@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.0.tgz#7638d31422129ecf4457440009fba03f9f9ac180" + integrity sha512-e6/d0eBu7gHtdCqFt0xJr642LdToM5/cN4Qb9DbHjVx1CP5RyeM+zH7pbecEmDv/lBqb0QH+6Uqq75rxFPkM0w== strong-log-transformer@^2.0.0: version "2.1.0" @@ -9048,13 +9643,6 @@ supertest@4.0.2, supertest@^4.0.2: methods "^1.1.2" superagent "^3.8.3" -supports-color@5.5.0, supports-color@^5.3.0, supports-color@^5.5.0: - version "5.5.0" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" - integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow== - dependencies: - has-flag "^3.0.0" - supports-color@6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-6.0.0.tgz#76cfe742cf1f41bb9b1c29ad03068c05b4c0e40a" @@ -9062,7 +9650,7 @@ supports-color@6.0.0: dependencies: has-flag "^3.0.0" -supports-color@6.1.0, supports-color@^6.1.0: +supports-color@6.1.0: version "6.1.0" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-6.1.0.tgz#0764abc69c63d5ac842dd4867e8d025e880df8f3" integrity sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ== @@ -9074,15 +9662,24 @@ supports-color@^2.0.0: resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7" integrity sha1-U10EXOa2Nj+kARcIRimZXp3zJMc= -symbol-observable@1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-1.0.1.tgz#8340fc4702c3122df5d22288f88283f513d3fdd4" - integrity sha1-g0D8RwLDEi310iKI+IKD9RPT/dQ= +supports-color@^5.3.0, supports-color@^5.5.0: + version "5.5.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" + integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow== + dependencies: + has-flag "^3.0.0" + +supports-color@^7.1.0: + version "7.1.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.1.0.tgz#68e32591df73e25ad1c4b49108a2ec507962bfd1" + integrity sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g== + dependencies: + has-flag "^4.0.0" table@^5.2.3: - version "5.4.4" - resolved "https://registry.yarnpkg.com/table/-/table-5.4.4.tgz#6e0f88fdae3692793d1077fd172a4667afe986a6" - integrity sha512-IIfEAUx5QlODLblLrGTTLJA7Tk0iLSGBvgY8essPRVNGHAzThujww1YqHLs6h3HfTg55h++RzLHH5Xw/rfv+mg== + version "5.4.6" + resolved "https://registry.yarnpkg.com/table/-/table-5.4.6.tgz#1292d19500ce3f86053b05f0e8e7e4a3bb21079e" + integrity sha512-wmEc8m4fjnob4gt5riFRtTu/6+4rSe12TpAELNSqHMfF3IqnA+CH37USM6/YR3qRZv7e56kAEAtd6nKZaxe0Ug== dependencies: ajv "^6.10.2" lodash "^4.17.14" @@ -9094,18 +9691,26 @@ tapable@^1.0.0, tapable@^1.1.3: resolved "https://registry.yarnpkg.com/tapable/-/tapable-1.1.3.tgz#a1fccc06b58db61fd7a45da2da44f5f3a3e67ba2" integrity sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA== -tar@^4: - version "4.4.10" - resolved "https://registry.yarnpkg.com/tar/-/tar-4.4.10.tgz#946b2810b9a5e0b26140cf78bea6b0b0d689eba1" - integrity sha512-g2SVs5QIxvo6OLp0GudTqEf05maawKUxXru104iaayWA09551tFCTI8f1Asb4lPfkBr91k07iL4c11XO3/b0tA== +tar-fs@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/tar-fs/-/tar-fs-2.1.0.tgz#d1cdd121ab465ee0eb9ccde2d35049d3f3daf0d5" + integrity sha512-9uW5iDvrIMCVpvasdFHW0wJPez0K4JnMZtsuIeDI7HyMGJNxmDZDOCQROr7lXyS+iL/QMpj07qcjGYTSdRFXUg== dependencies: chownr "^1.1.1" - fs-minipass "^1.2.5" - minipass "^2.3.5" - minizlib "^1.2.1" - mkdirp "^0.5.0" - safe-buffer "^5.1.2" - yallist "^3.0.3" + mkdirp-classic "^0.5.2" + pump "^3.0.0" + tar-stream "^2.0.0" + +tar-stream@^2.0.0: + version "2.1.2" + resolved "https://registry.yarnpkg.com/tar-stream/-/tar-stream-2.1.2.tgz#6d5ef1a7e5783a95ff70b69b97455a5968dc1325" + integrity sha512-UaF6FoJ32WqALZGOIAApXx+OdxhekNMChu6axLJR85zMMjXKWFGjbIRe+J6P4UnRGg9rAwWvbTT0oI7hD/Un7Q== + dependencies: + bl "^4.0.1" + end-of-stream "^1.4.1" + fs-constants "^1.0.0" + inherits "^2.0.3" + readable-stream "^3.1.1" tar@^4.4.10, tar@^4.4.12, tar@^4.4.8: version "4.4.13" @@ -9120,6 +9725,18 @@ tar@^4.4.10, tar@^4.4.12, tar@^4.4.8: safe-buffer "^5.1.2" yallist "^3.0.3" +tar@^6.0.1: + version "6.0.2" + resolved "https://registry.yarnpkg.com/tar/-/tar-6.0.2.tgz#5df17813468a6264ff14f766886c622b84ae2f39" + integrity sha512-Glo3jkRtPcvpDlAs/0+hozav78yoXKFr+c4wgw62NNMO3oo4AaJdCo21Uu7lcwr55h39W2XD1LMERc64wtbItg== + dependencies: + chownr "^2.0.0" + fs-minipass "^2.0.0" + minipass "^3.0.0" + minizlib "^2.1.0" + mkdirp "^1.0.3" + yallist "^4.0.0" + tcomb-validation@^3.3.0: version "3.4.1" resolved "https://registry.yarnpkg.com/tcomb-validation/-/tcomb-validation-3.4.1.tgz#a7696ec176ce56a081d9e019f8b732a5a8894b65" @@ -9149,51 +9766,48 @@ temp-write@^3.4.0: temp-dir "^1.0.0" uuid "^3.0.1" -term-size@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/term-size/-/term-size-1.2.0.tgz#458b83887f288fc56d6fffbfad262e26638efa69" - integrity sha1-RYuDiH8oj8Vtb/+/rSYuJmOO+mk= - dependencies: - execa "^0.7.0" +term-size@^2.1.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/term-size/-/term-size-2.2.0.tgz#1f16adedfe9bdc18800e1776821734086fcc6753" + integrity sha512-a6sumDlzyHVJWb8+YofY4TW112G6p2FCPEAFk+59gIYHv3XHRhm9ltVQ9kli4hNWeQBwSpe8cRN25x0ROunMOw== -terser-webpack-plugin@^1.4.1: - version "1.4.1" - resolved "https://registry.yarnpkg.com/terser-webpack-plugin/-/terser-webpack-plugin-1.4.1.tgz#61b18e40eaee5be97e771cdbb10ed1280888c2b4" - integrity sha512-ZXmmfiwtCLfz8WKZyYUuuHf3dMYEjg8NrjHMb0JqHVHVOSkzp3cW2/XG1fP3tRhqEqSzMwzzRQGtAPbs4Cncxg== +terser-webpack-plugin@^1.4.3: + version "1.4.3" + resolved "https://registry.yarnpkg.com/terser-webpack-plugin/-/terser-webpack-plugin-1.4.3.tgz#5ecaf2dbdc5fb99745fd06791f46fc9ddb1c9a7c" + integrity sha512-QMxecFz/gHQwteWwSo5nTc6UaICqN1bMedC5sMtUc7y3Ha3Q8y6ZO0iCR8pq4RJC8Hjf0FEPEHZqcMB/+DFCrA== dependencies: cacache "^12.0.2" find-cache-dir "^2.1.0" is-wsl "^1.1.0" schema-utils "^1.0.0" - serialize-javascript "^1.7.0" + serialize-javascript "^2.1.2" source-map "^0.6.1" terser "^4.1.2" webpack-sources "^1.4.0" worker-farm "^1.7.0" terser@^4.1.2: - version "4.3.1" - resolved "https://registry.yarnpkg.com/terser/-/terser-4.3.1.tgz#09820bcb3398299c4b48d9a86aefc65127d0ed65" - integrity sha512-pnzH6dnFEsR2aa2SJaKb1uSCl3QmIsJ8dEkj0Fky+2AwMMcC9doMqLOQIH6wVTEKaVfKVvLSk5qxPBEZT9mywg== + version "4.6.13" + resolved "https://registry.yarnpkg.com/terser/-/terser-4.6.13.tgz#e879a7364a5e0db52ba4891ecde007422c56a916" + integrity sha512-wMvqukYgVpQlymbnNbabVZbtM6PN63AzqexpwJL8tbh/mRT9LE5o+ruVduAGL7D6Fpjl+Q+06U5I9Ul82odAhw== dependencies: commander "^2.20.0" source-map "~0.6.1" source-map-support "~0.5.12" -test-exclude@^5.2.3: - version "5.2.3" - resolved "https://registry.yarnpkg.com/test-exclude/-/test-exclude-5.2.3.tgz#c3d3e1e311eb7ee405e092dac10aefd09091eac0" - integrity sha512-M+oxtseCFO3EDtAaGH7iiej3CBkzXqFMbzqYAACdzKui4eZA+pq3tZEwChvOdNfa7xxy8BfbmgJSIr43cC/+2g== +test-exclude@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/test-exclude/-/test-exclude-6.0.0.tgz#04a8698661d805ea6fa293b6cb9e63ac044ef15e" + integrity sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w== dependencies: - glob "^7.1.3" + "@istanbuljs/schema" "^0.1.2" + glob "^7.1.4" minimatch "^3.0.4" - read-pkg-up "^4.0.0" - require-main-filename "^2.0.0" -text-extensions@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/text-extensions/-/text-extensions-2.0.0.tgz#43eabd1b495482fae4a2bf65e5f56c29f69220f6" - integrity sha512-F91ZqLgvi1E0PdvmxMgp+gcf6q8fMH7mhdwWfzXnl1k+GbpQDmi8l7DzLC5JTASKbwpY3TfxajAUzAXcv2NmsQ== +text-extensions@^1.0.0: + version "1.9.0" + resolved "https://registry.yarnpkg.com/text-extensions/-/text-extensions-1.9.0.tgz#1853e45fee39c945ce6f6c36b2d659b5aabc2a26" + integrity sha512-wiBrwC1EhBelW12Zy26JeOUkQ5mRu+5o8rpsJk5+2t+Y5vE7e842qtZDQ2g1NpX/29HdyFeJ4nSIhI47ENSxlQ== text-table@^0.2.0: version "0.2.0" @@ -9214,11 +9828,6 @@ thenify-all@^1.0.0: dependencies: any-promise "^1.0.0" -throttleit@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/throttleit/-/throttleit-1.0.0.tgz#9e785836daf46743145a5984b6268d828528ac6c" - integrity sha1-nnhYNtr0Z0MUWlmEtiaNgoUorGw= - through2@^2.0.0, through2@^2.0.2: version "2.0.5" resolved "https://registry.yarnpkg.com/through2/-/through2-2.0.5.tgz#01c1e39eb31d07cb7d03a96a70823260b23132cd" @@ -9239,25 +9848,13 @@ through@2, "through@>=2.2.7 <3", through@^2.3.4, through@^2.3.6: resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" integrity sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU= -timed-out@^4.0.0: - version "4.0.1" - resolved "https://registry.yarnpkg.com/timed-out/-/timed-out-4.0.1.tgz#f32eacac5a175bea25d7fab565ab3ed8741ef56f" - integrity sha1-8y6srFoXW+ol1/q1Zas+2HQe9W8= - timers-browserify@^2.0.4: - version "2.0.10" - resolved "https://registry.yarnpkg.com/timers-browserify/-/timers-browserify-2.0.10.tgz#1d28e3d2aadf1d5a5996c4e9f95601cd053480ae" - integrity sha512-YvC1SV1XdOUaL6gx5CoGroT3Gu49pK9+TZ38ErPldOWW4j49GI1HKs9DV+KGq/w6y+LZ72W1c8cKz2vzY+qpzg== + version "2.0.11" + resolved "https://registry.yarnpkg.com/timers-browserify/-/timers-browserify-2.0.11.tgz#800b1f3eee272e5bc53ee465a04d0e804c31211f" + integrity sha512-60aV6sgJ5YEbzUdn9c8kYGIqOubPoUdqQCul3SBAsRCZ40s6Y5cMcrW4dt3/k/EsbLVJNl9n6Vz3fTc+k2GeKQ== dependencies: setimmediate "^1.0.4" -tmp@0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.1.0.tgz#ee434a4e22543082e294ba6201dcc6eafefa2877" - integrity sha512-J7Z2K08jbGcdA1kkQpJSqLF6T0tdQqpR2pnSUXsIchbPdTI9v3e85cLW0d6WDhwuAleOV71j2xWs8qMPfK7nKw== - dependencies: - rimraf "^2.6.3" - tmp@^0.0.33: version "0.0.33" resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9" @@ -9282,6 +9879,11 @@ to-object-path@^0.3.0: dependencies: kind-of "^3.0.2" +to-readable-stream@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/to-readable-stream/-/to-readable-stream-1.0.0.tgz#ce0aa0c2f3df6adf852efb404a783e77c0475771" + integrity sha512-Iq25XBt6zD5npPhlLVXGFN3/gyR2/qODcKNNyTMd4vbm39HUaOiAM4PMq0eMVC/Tkxz+Zjdsc55g9yyz+Yq00Q== + to-regex-range@^2.1.0: version "2.1.1" resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-2.1.1.tgz#7c80c17b9dfebe599e27367e0d4dd5590141db38" @@ -9319,7 +9921,7 @@ touch@^3.1.0: dependencies: nopt "~1.0.10" -tough-cookie@^2.3.3: +tough-cookie@^2.3.3, tough-cookie@~2.5.0: version "2.5.0" resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.5.0.tgz#cd9fb2a0aa1d5a12b473bd9fb96fa3dcff65ade2" integrity sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g== @@ -9327,14 +9929,6 @@ tough-cookie@^2.3.3: psl "^1.1.28" punycode "^2.1.1" -tough-cookie@~2.4.3: - version "2.4.3" - resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.4.3.tgz#53f36da3f47783b0925afa06ff9f3b165280f781" - integrity sha512-Q5srk/4vDM54WJsJio3XNn6K2sCG+CQ8G5Wz6bZhRZoAe/+TxjWB/GlFAnYEbkYVlON9FMk/fE3h2RLpPXo4lQ== - dependencies: - psl "^1.1.24" - punycode "^1.4.1" - tr46@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/tr46/-/tr46-1.0.1.tgz#a8b13fd6bfd2489519674ccde55ba3693b706d09" @@ -9352,25 +9946,20 @@ trim-newlines@^2.0.0: resolved "https://registry.yarnpkg.com/trim-newlines/-/trim-newlines-2.0.0.tgz#b403d0b91be50c331dfc4b82eeceb22c3de16d20" integrity sha1-tAPQuRvlDDMd/EuC7s6yLD3hbSA= +trim-newlines@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/trim-newlines/-/trim-newlines-3.0.0.tgz#79726304a6a898aa8373427298d54c2ee8b1cb30" + integrity sha512-C4+gOpvmxaSMKuEf9Qc134F1ZuOHVXKRbtEflf4NTtuuJDEIJ9p5PXsalL8SkeRw+qit1Mo+yuvMPAKwWg/1hA== + trim-off-newlines@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/trim-off-newlines/-/trim-off-newlines-1.0.1.tgz#9f9ba9d9efa8764c387698bcbfeb2c848f11adb3" integrity sha1-n5up2e+odkw4dpi8v+sshI8RrbM= -trim-right@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/trim-right/-/trim-right-1.0.1.tgz#cb2e1203067e0c8de1f614094b9fe45704ea6003" - integrity sha1-yy4SAwZ+DI3h9hQJS5/kVwTqYAM= - -tryer@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/tryer/-/tryer-1.0.1.tgz#f2c85406800b9b0f74c9f7465b81eaad241252f8" - integrity sha512-c3zayb8/kWWpycWYg87P71E1S1ZL6b6IJxfb5fvsUgsf0S2MVGaDhDXXjDMpdCpfWXqptc+4mXwmiy1ypXqRAA== - -ts-loader@6.2.1: - version "6.2.1" - resolved "https://registry.yarnpkg.com/ts-loader/-/ts-loader-6.2.1.tgz#67939d5772e8a8c6bdaf6277ca023a4812da02ef" - integrity sha512-Dd9FekWuABGgjE1g0TlQJ+4dFUfYGbYcs52/HQObE0ZmUNjQlmLAS7xXsSzy23AMaMwipsx5sNHvoEpT2CZq1g== +ts-loader@7.0.4: + version "7.0.4" + resolved "https://registry.yarnpkg.com/ts-loader/-/ts-loader-7.0.4.tgz#5d9b95227de5afb91fdd9668f8920eb193cfe0cc" + integrity sha512-5du6OQHl+4ZjO4crEyoYUyWSrmmo7bAO+inkaILZ68mvahqrfoa4nn0DRmpQ4ruT4l+cuJCgF0xD7SBIyLeeow== dependencies: chalk "^2.3.0" enhanced-resolve "^4.0.0" @@ -9378,15 +9967,30 @@ ts-loader@6.2.1: micromatch "^4.0.0" semver "^6.0.0" -ts-mocha@6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/ts-mocha/-/ts-mocha-6.0.0.tgz#40b8c5462ffce6f5dcee5ff729655b2958f26e50" - integrity sha512-ZCtJK8WXxHNbFNjvUKQIXZby/+ybQQkaBcM/3QhBQUfwjpdGFE9F6iWsHhF5ifQNFV/lWiOODi2VMD5AyPcQyg== +ts-mocha@7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/ts-mocha/-/ts-mocha-7.0.0.tgz#f1549b48b46f53d7ae1dccbb26313c7879acb190" + integrity sha512-7WfkQw1W6JZXG5m4E1w2e945uWzBoZqmnOHvpMu0v+zvyKLdUQeTtRMfcQsVEKsUnYL6nTyH4okRt2PZucmFXQ== dependencies: ts-node "7.0.1" optionalDependencies: tsconfig-paths "^3.5.0" +ts-nats@1.2.14-2: + version "1.2.14-2" + resolved "https://registry.yarnpkg.com/ts-nats/-/ts-nats-1.2.14-2.tgz#4edfa5a99cb983b727b4e03d3280e9f851929b37" + integrity sha512-b0nPczfd8Z4+LQ5C7xWW391qiPsWNidhZRB9yrumQ0hfRHAos4NVwodPIZd78e/so3zBW5LRifejaAJE/UzNXA== + dependencies: + nuid "^1.1.2" + ts-nkeys "^1.0.16" + +ts-nkeys@^1.0.16: + version "1.0.16" + resolved "https://registry.yarnpkg.com/ts-nkeys/-/ts-nkeys-1.0.16.tgz#b0c6e7c4f16f976c7e7ddb6982fc789a2f971248" + integrity sha512-1qrhAlavbm36wtW+7NtKOgxpzl+70NTF8xlz9mEhiA5zHMlMxjj3sEVKWm3pGZhHXE0Q3ykjrj+OSRVaYw+Dqg== + dependencies: + tweetnacl "^1.0.3" + ts-node@7.0.1: version "7.0.1" resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-7.0.1.tgz#9562dc2d1e6d248d24bc55f773e3f614337d9baf" @@ -9401,18 +10005,18 @@ ts-node@7.0.1: source-map-support "^0.5.6" yn "^2.0.0" -ts-node@8.4.1: - version "8.4.1" - resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-8.4.1.tgz#270b0dba16e8723c9fa4f9b4775d3810fd994b4f" - integrity sha512-5LpRN+mTiCs7lI5EtbXmF/HfMeCjzt7DH9CZwtkr6SywStrNQC723wG+aOWFiLNn7zT3kD/RnFqi3ZUfr4l5Qw== +ts-node@8.10.1: + version "8.10.1" + resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-8.10.1.tgz#77da0366ff8afbe733596361d2df9a60fc9c9bd3" + integrity sha512-bdNz1L4ekHiJul6SHtZWs1ujEKERJnHs4HxN7rjTyyVOFf3HaJ6sLqe6aPG62XTzAB/63pKRh5jTSWL0D7bsvw== dependencies: arg "^4.1.0" diff "^4.0.1" make-error "^1.1.1" - source-map-support "^0.5.6" - yn "^3.0.0" + source-map-support "^0.5.17" + yn "3.1.1" -tsconfig-paths@3.9.0: +tsconfig-paths@3.9.0, tsconfig-paths@^3.5.0: version "3.9.0" resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-3.9.0.tgz#098547a6c4448807e8fcb8eae081064ee9a3c90b" integrity sha512-dRcuzokWhajtZWkQsDVKbWyY+jgcLC5sqJhg2PSgf4ZkH2aHPvaOY8YWGhmjb68b5qqTfasSsDO9k7RUiEmZAw== @@ -9422,21 +10026,10 @@ tsconfig-paths@3.9.0: minimist "^1.2.0" strip-bom "^3.0.0" -tsconfig-paths@^3.5.0: - version "3.8.0" - resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-3.8.0.tgz#4e34202d5b41958f269cf56b01ed95b853d59f72" - integrity sha512-zZEYFo4sjORK8W58ENkRn9s+HmQFkkwydDG7My5s/fnfr2YYCaiyXe/HBUcIgU8epEKOXwiahOO+KZYjiXlWyQ== - dependencies: - "@types/json5" "^0.0.29" - deepmerge "^2.0.1" - json5 "^1.0.1" - minimist "^1.2.0" - strip-bom "^3.0.0" - tslib@^1.8.1, tslib@^1.9.0: - version "1.10.0" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.10.0.tgz#c3c19f95973fb0a62973fb09d90d961ee43e5c8a" - integrity sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ== + version "1.13.0" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.13.0.tgz#c881e13cc7015894ed914862d276436fa9a47043" + integrity sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q== tsutils@^3.17.1: version "3.17.1" @@ -9462,6 +10055,18 @@ tweetnacl@^0.14.3, tweetnacl@~0.14.0: resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64" integrity sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q= +tweetnacl@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-1.0.3.tgz#ac0af71680458d8a6378d0d0d050ab1407d35596" + integrity sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw== + +type-check@^0.4.0, type-check@~0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.4.0.tgz#07b8203bfa7056c0657050e3ccd2c37730bab8f1" + integrity sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew== + dependencies: + prelude-ls "^1.2.1" + type-check@~0.3.2: version "0.3.2" resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.3.2.tgz#5884cab512cf1d355e3fb784f30804b2b520db72" @@ -9469,11 +10074,21 @@ type-check@~0.3.2: dependencies: prelude-ls "~1.1.2" -type-detect@4.0.8, type-detect@^4.0.0, type-detect@^4.0.5: +type-detect@4.0.8, type-detect@^4.0.8: version "4.0.8" resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-4.0.8.tgz#7646fb5f18871cfbb7749e69bd39a6388eb7450c" integrity sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g== +type-fest@^0.11.0: + version "0.11.0" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.11.0.tgz#97abf0872310fed88a5c466b25681576145e33f1" + integrity sha512-OdjXJxnCN1AvyLSzeKIgXTXxV+99ZuXl3Hpo9XpJAv9MBcHrrJOQ5kV7ypXOuQie+AmWG25hLbiKdwYTifzcfQ== + +type-fest@^0.13.1: + version "0.13.1" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.13.1.tgz#0172cb5bce80b0bd542ea348db50c7e21834d934" + integrity sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg== + type-fest@^0.3.0: version "0.3.1" resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.3.1.tgz#63d00d204e059474fe5e1b7c011112bbd1dc29e1" @@ -9484,7 +10099,12 @@ type-fest@^0.6.0: resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.6.0.tgz#8d2a2370d3df886eb5c90ada1c5bf6188acf838b" integrity sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg== -type-is@~1.6.17, type-is@~1.6.18: +type-fest@^0.8.0, type-fest@^0.8.1: + version "0.8.1" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.8.1.tgz#09e249ebde851d3b1e48d27c105444667f17b83d" + integrity sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA== + +type-is@^1.6.4, type-is@~1.6.17, type-is@~1.6.18: version "1.6.18" resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.18.tgz#4e552cd05df09467dcbc4ef739de89f2cf37c131" integrity sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g== @@ -9492,23 +10112,50 @@ type-is@~1.6.17, type-is@~1.6.18: media-typer "0.3.0" mime-types "~2.1.24" +typedarray-to-buffer@^3.1.5: + version "3.1.5" + resolved "https://registry.yarnpkg.com/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz#a97ee7a9ff42691b9f783ff1bc5112fe3fca9080" + integrity sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q== + dependencies: + is-typedarray "^1.0.0" + typedarray@^0.0.6: version "0.0.6" resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c= -typescript@3.6.4: - version "3.6.4" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.6.4.tgz#b18752bb3792bc1a0281335f7f6ebf1bbfc5b91d" - integrity sha512-unoCll1+l+YK4i4F8f22TaNVPRHcD9PA3yCuZ8g5e0qGqlVlJ/8FSateOLLSagn+Yg5+ZwuPkL8LFUc0Jcvksg== +typeorm@0.2.24: + version "0.2.24" + resolved "https://registry.yarnpkg.com/typeorm/-/typeorm-0.2.24.tgz#cd0fbd907326873a96c98e290fca49c589f0ffa8" + integrity sha512-L9tQv6nNLRyh+gex/qc8/CyLs8u0kXKqk1OjYGF13k/KOg6N2oibwkuGgv0FuoTGYx2ta2NmqvuMUAMrHIY5ew== + dependencies: + app-root-path "^3.0.0" + buffer "^5.1.0" + chalk "^2.4.2" + cli-highlight "^2.0.0" + debug "^4.1.1" + dotenv "^6.2.0" + glob "^7.1.2" + js-yaml "^3.13.1" + mkdirp "^0.5.1" + reflect-metadata "^0.1.13" + sha.js "^2.4.11" + tslib "^1.9.0" + xml2js "^0.4.17" + yargonaut "^1.1.2" + yargs "^13.2.1" + +typescript@3.8.3: + version "3.8.3" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.8.3.tgz#409eb8544ea0335711205869ec458ab109ee1061" + integrity sha512-MYlEfn5VrLNsgudQTVJeNaQFUAI7DkhnOjdpAp4T+ku1TfQClewlbSuTVHiA+8skNBgaf02TL/kLOvig4y3G8w== uglify-js@^3.1.4: - version "3.6.0" - resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.6.0.tgz#704681345c53a8b2079fb6cec294b05ead242ff5" - integrity sha512-W+jrUHJr3DXKhrsS7NUVxn3zqMOFn0hL/Ei6v0anCIMoKC93TjcflTagwIHLW7SfMFfiQuktQyFVCFHGUE0+yg== + version "3.9.3" + resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.9.3.tgz#4a285d1658b8a2ebaef9e51366b3a0f7acd79ec2" + integrity sha512-r5ImcL6QyzQGVimQoov3aL2ZScywrOgBXGndbWrdehKoSvGe/RmiE5Jpw/v+GvxODt6l2tpBXwA7n+qZVlHBMA== dependencies: - commander "~2.20.0" - source-map "~0.6.1" + commander "~2.20.3" uid-number@0.0.6: version "0.0.6" @@ -9521,9 +10168,9 @@ umask@^1.1.0: integrity sha1-8pzr8B31F5ErtY/5xOUP3o4zMg0= undefsafe@^2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/undefsafe/-/undefsafe-2.0.2.tgz#225f6b9e0337663e0d8e7cfd686fc2836ccace76" - integrity sha1-Il9rngM3Zj4Njnz9aG/Cg2zKznY= + version "2.0.3" + resolved "https://registry.yarnpkg.com/undefsafe/-/undefsafe-2.0.3.tgz#6b166e7094ad46313b2202da7ecc2cd7cc6e7aae" + integrity sha512-nrXZwwXrD/T/JXeygJqdCO6NZZ1L66HrxM/Z7mIq2oPanoN0F1nLx3lwJMu6AwJY69hdixaFQOuoYsMjE5/C2A== dependencies: debug "^2.2.0" @@ -9551,12 +10198,12 @@ unique-slug@^2.0.0: dependencies: imurmurhash "^0.1.4" -unique-string@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/unique-string/-/unique-string-1.0.0.tgz#9e1057cca851abb93398f8b33ae187b99caec11a" - integrity sha1-nhBXzKhRq7kzmPizOuGHuZyuwRo= +unique-string@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/unique-string/-/unique-string-2.0.0.tgz#39c6451f81afb2749de2b233e3f7c5e8843bd89d" + integrity sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg== dependencies: - crypto-random-string "^1.0.0" + crypto-random-string "^2.0.0" unit.js@2.1.1: version "2.1.1" @@ -9573,9 +10220,16 @@ unit.js@2.1.1: supertest "^4.0.2" universal-user-agent@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/universal-user-agent/-/universal-user-agent-4.0.0.tgz#27da2ec87e32769619f68a14996465ea1cb9df16" - integrity sha512-eM8knLpev67iBDizr/YtqkJsF3GK8gzDc6st/WKzrTuPtcsOKW/0IdL4cnMBsU69pOx0otavLWBDGTwg+dB0aA== + version "4.0.1" + resolved "https://registry.yarnpkg.com/universal-user-agent/-/universal-user-agent-4.0.1.tgz#fd8d6cb773a679a709e967ef8288a31fcc03e557" + integrity sha512-LnST3ebHwVL2aNe4mejI9IQh2HfZ1RLo8Io2HugSif8ekzD1TlWpHpColOB/eh8JHMLkGH3Akqf040I+4ylNxg== + dependencies: + os-name "^3.1.0" + +universal-user-agent@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/universal-user-agent/-/universal-user-agent-5.0.0.tgz#a3182aa758069bf0e79952570ca757de3579c1d9" + integrity sha512-B5TPtzZleXyPrUMKCpEHFmVhMN6EhmJYjG5PQna9s7mXeSqGTLap4OpqLl5FCEFUI3UBmllkETwKf/db66Y54Q== dependencies: os-name "^3.1.0" @@ -9597,38 +10251,31 @@ unset-value@^1.0.0: has-value "^0.3.1" isobject "^3.0.0" -untildify@3.0.3: - version "3.0.3" - resolved "https://registry.yarnpkg.com/untildify/-/untildify-3.0.3.tgz#1e7b42b140bcfd922b22e70ca1265bfe3634c7c9" - integrity sha512-iSk/J8efr8uPT/Z4eSUywnqyrQU7DSdMfdqK4iWEaUVVmcP5JcnpRqmVMwcwcnmI1ATFNgC5V90u09tBynNFKA== - -unzip-response@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/unzip-response/-/unzip-response-2.0.1.tgz#d2f0f737d16b0615e72a6935ed04214572d56f97" - integrity sha1-0vD3N9FrBhXnKmk17QQhRXLVb5c= - -upath@^1.1.1: - version "1.1.2" - resolved "https://registry.yarnpkg.com/upath/-/upath-1.1.2.tgz#3db658600edaeeccbe6db5e684d67ee8c2acd068" - integrity sha512-kXpym8nmDmlCBr7nKdIx8P2jNBa+pBpIUFRnKJ4dr8htyYGJFokkr2ZvERRtUN+9SY+JqXouNgUPtv6JQva/2Q== +upath@^1.1.1, upath@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/upath/-/upath-1.2.0.tgz#8f66dbcd55a883acdae4408af8b035a5044c1894" + integrity sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg== -update-notifier@^2.5.0: - version "2.5.0" - resolved "https://registry.yarnpkg.com/update-notifier/-/update-notifier-2.5.0.tgz#d0744593e13f161e406acb1d9408b72cad08aff6" - integrity sha512-gwMdhgJHGuj/+wHJJs9e6PcCszpxR1b236igrOkUofGhqJuG+amlIKwApH1IW1WWl7ovZxsX49lMBWLxSdm5Dw== +update-notifier@^4.0.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/update-notifier/-/update-notifier-4.1.0.tgz#4866b98c3bc5b5473c020b1250583628f9a328f3" + integrity sha512-w3doE1qtI0/ZmgeoDoARmI5fjDoT93IfKgEGqm26dGUOh8oNpaSTsGNdYRN/SjOuo10jcJGwkEL3mroKzktkew== dependencies: - boxen "^1.2.1" - chalk "^2.0.1" - configstore "^3.0.0" + boxen "^4.2.0" + chalk "^3.0.0" + configstore "^5.0.1" + has-yarn "^2.1.0" import-lazy "^2.1.0" - is-ci "^1.0.10" - is-installed-globally "^0.1.0" - is-npm "^1.0.0" - latest-version "^3.0.0" - semver-diff "^2.0.0" - xdg-basedir "^3.0.0" - -uri-js@4.2.2, uri-js@^4.2.2: + is-ci "^2.0.0" + is-installed-globally "^0.3.1" + is-npm "^4.0.0" + is-yarn-global "^0.3.0" + latest-version "^5.0.0" + pupa "^2.0.1" + semver-diff "^3.1.1" + xdg-basedir "^4.0.0" + +uri-js@^4.2.2: version "4.2.2" resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.2.2.tgz#94c540e1ff772956e2299507c010aea6c8838eb0" integrity sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ== @@ -9640,14 +10287,22 @@ urix@^0.1.0: resolved "https://registry.yarnpkg.com/urix/-/urix-0.1.0.tgz#da937f7a62e21fec1fd18d49b35c2935067a6c72" integrity sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI= -url-parse-lax@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/url-parse-lax/-/url-parse-lax-1.0.0.tgz#7af8f303645e9bd79a272e7a14ac68bc0609da73" - integrity sha1-evjzA2Rem9eaJy56FKxovAYJ2nM= +url-parse-lax@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/url-parse-lax/-/url-parse-lax-3.0.0.tgz#16b5cafc07dbe3676c1b1999177823d6503acb0c" + integrity sha1-FrXK/Afb42dsGxmZF3gj1lA6yww= + dependencies: + prepend-http "^2.0.0" + +url@0.10.3: + version "0.10.3" + resolved "https://registry.yarnpkg.com/url/-/url-0.10.3.tgz#021e4d9c7705f21bbf37d03ceb58767402774c64" + integrity sha1-Ah5NnHcF8hu/N9A861h2dAJ3TGQ= dependencies: - prepend-http "^1.0.1" + punycode "1.3.2" + querystring "0.2.0" -url@0.11.0, url@^0.11.0: +url@^0.11.0: version "0.11.0" resolved "https://registry.yarnpkg.com/url/-/url-0.11.0.tgz#3838e97cfc60521eb73c525a8e55bfdd9e2e28f1" integrity sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE= @@ -9691,21 +10346,36 @@ utils-merge@1.0.1: resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713" integrity sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM= -uuid@3.3.2, uuid@^3.3.2: +uuid@3.3.2: version "3.3.2" resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.3.2.tgz#1b4af4955eb3077c501c23872fc6513811587131" integrity sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA== -uuid@3.3.3, uuid@^3.0.1: - version "3.3.3" - resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.3.3.tgz#4568f0216e78760ee1dbf3a4d2cf53e224112866" - integrity sha512-pW0No1RGHgzlpHJO1nsVrHKpOEIxkGg1xB+v0ZmdNH5OAeAwzAVrCnI2/6Mtx+Uys6iaylxa+D3g4j63IKKjSQ== +uuid@8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.0.0.tgz#bc6ccf91b5ff0ac07bbcdbf1c7c4e150db4dbb6c" + integrity sha512-jOXGuXZAWdsTH7eZLtyXMqUb9EcWMGZNbL9YcGBJl4MH4nrxHmZJhEHvyLFrkxo+28uLb/NYRcStH48fnD0Vzw== + +uuid@^3.0.1, uuid@^3.3.2, uuid@^3.3.3: + version "3.4.0" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee" + integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A== + +uuid@^7.0.3: + version "7.0.3" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-7.0.3.tgz#c5c9f2c8cf25dc0a372c4df1441c41f5bd0c680b" + integrity sha512-DPSke0pXhTZgoF/d+WSt2QaKMCFSfx7QegxEWT+JOuHF5aWrKEn0G+ztjuJg/gG8/ItK+rbPCD/yNv8yyih6Cg== -v8-compile-cache@2.0.3, v8-compile-cache@^2.0.3: +v8-compile-cache@2.0.3: version "2.0.3" resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.0.3.tgz#00f7494d2ae2b688cfe2899df6ed2c54bef91dbe" integrity sha512-CNmdbwQMBjwr9Gsmohvm0pbL954tJrNzf6gWL3K+QMQf00PF7ERGrEiLgjuU3mKreLC2MeGhUsNV9ybTbLgd3w== +v8-compile-cache@^2.0.3: + version "2.1.0" + resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.1.0.tgz#e14de37b31a6d194f5690d67efc4e7f6fc6ab30e" + integrity sha512-usZBT3PW+LOjM25wbqIlZwPeJV+3OSz3M1k1Ws8snlW39dZyYL9lOGC5FgPVHfk0jKmjiDV8Z0mIbVQPiwFs7g== + validate-npm-package-license@^3.0.1, validate-npm-package-license@^3.0.3: version "3.0.4" resolved "https://registry.yarnpkg.com/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz#fc91f6b9c7ba15c857f4cb2c5defeec39d4f410a" @@ -9741,16 +10411,16 @@ verror@1.10.0: extsprintf "^1.2.0" vm-browserify@^1.0.1: - version "1.1.0" - resolved "https://registry.yarnpkg.com/vm-browserify/-/vm-browserify-1.1.0.tgz#bd76d6a23323e2ca8ffa12028dc04559c75f9019" - integrity sha512-iq+S7vZJE60yejDYM0ek6zg308+UZsdtPExWP9VZoCFCz1zkJoXFnAX7aZfd/ZwrkidzdUZL0C/ryW+JwAiIGw== + version "1.1.2" + resolved "https://registry.yarnpkg.com/vm-browserify/-/vm-browserify-1.1.2.tgz#78641c488b8e6ca91a75f511e7a3b32a86e5dda0" + integrity sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ== -watchpack@^1.6.0: - version "1.6.0" - resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-1.6.0.tgz#4bc12c2ebe8aa277a71f1d3f14d685c7b446cd00" - integrity sha512-i6dHe3EyLjMmDlU1/bGQpEw25XSjkJULPuAVKCbNRefQVq48yXKUpwg538F7AZTf9kyr57zj++pQFltUa5H7yA== +watchpack@^1.6.1: + version "1.6.1" + resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-1.6.1.tgz#280da0a8718592174010c078c7585a74cd8cd0e2" + integrity sha512-+IF9hfUFOrYOOaKyfaI7h7dquUIOgyEMoQMLA7OP5FxegKA2+XdXThAZ9TU2kucfhDH7rfMHs1oPYziVGWRnZA== dependencies: - chokidar "^2.0.2" + chokidar "^2.1.8" graceful-fs "^4.1.2" neo-async "^2.5.0" @@ -9766,29 +10436,10 @@ webidl-conversions@^4.0.2: resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-4.0.2.tgz#a855980b1f0b6b359ba1d5d9fb39ae941faa63ad" integrity sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg== -webpack-bundle-analyzer@3.6.0: - version "3.6.0" - resolved "https://registry.yarnpkg.com/webpack-bundle-analyzer/-/webpack-bundle-analyzer-3.6.0.tgz#39b3a8f829ca044682bc6f9e011c95deb554aefd" - integrity sha512-orUfvVYEfBMDXgEKAKVvab5iQ2wXneIEorGNsyuOyVYpjYrI7CUOhhXNDd3huMwQ3vNNWWlGP+hzflMFYNzi2g== - dependencies: - acorn "^6.0.7" - acorn-walk "^6.1.1" - bfj "^6.1.1" - chalk "^2.4.1" - commander "^2.18.0" - ejs "^2.6.1" - express "^4.16.3" - filesize "^3.6.1" - gzip-size "^5.0.0" - lodash "^4.17.15" - mkdirp "^0.5.1" - opener "^1.5.1" - ws "^6.0.0" - -webpack-cli@3.3.9: - version "3.3.9" - resolved "https://registry.yarnpkg.com/webpack-cli/-/webpack-cli-3.3.9.tgz#79c27e71f94b7fe324d594ab64a8e396b9daa91a" - integrity sha512-xwnSxWl8nZtBl/AFJCOn9pG7s5CYUYdZxmmukv+fAHLcBIHM36dImfpQg3WfShZXeArkWlf6QRw24Klcsv8a5A== +webpack-cli@3.3.11: + version "3.3.11" + resolved "https://registry.yarnpkg.com/webpack-cli/-/webpack-cli-3.3.11.tgz#3bf21889bf597b5d82c38f215135a411edfdc631" + integrity sha512-dXlfuml7xvAFwYUPsrtQAA9e4DOe58gnzSxhgrO/ZM/gyXTBowrsYeubyN4mqGhYdpXMFNyQ6emjJS9M7OBd4g== dependencies: chalk "2.4.2" cross-spawn "6.0.5" @@ -9802,15 +10453,14 @@ webpack-cli@3.3.9: v8-compile-cache "2.0.3" yargs "13.2.4" -webpack-sources@^1.1.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-1.3.0.tgz#2a28dcb9f1f45fe960d8f1493252b5ee6530fa85" - integrity sha512-OiVgSrbGu7NEnEvQJJgdSFPl2qWKkWq5lHMhgiToIiN9w34EBnjYzSYs+VbL5KoYiLNtFFa7BZIKxRED3I32pA== +webpack-merge@4.2.2: + version "4.2.2" + resolved "https://registry.yarnpkg.com/webpack-merge/-/webpack-merge-4.2.2.tgz#a27c52ea783d1398afd2087f547d7b9d2f43634d" + integrity sha512-TUE1UGoTX2Cd42j3krGYqObZbOD+xF7u28WB7tfUordytSjbWTIjK/8V0amkBfTYN4/pB/GIDlJZZ657BGG19g== dependencies: - source-list-map "^2.0.0" - source-map "~0.6.1" + lodash "^4.17.15" -webpack-sources@^1.4.0, webpack-sources@^1.4.1: +webpack-sources@^1.1.0, webpack-sources@^1.4.0, webpack-sources@^1.4.1: version "1.4.3" resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-1.4.3.tgz#eedd8ec0b928fbf1cbfe994e22d2d890f330a933" integrity sha512-lgTS3Xhv1lCOKo7SA5TjKXMjpSM4sBjNV5+q2bqesbSPs5FjGmU6jjtBSkX9b4qW87vDIsCIlUPOEhbZrMdjeQ== @@ -9818,16 +10468,16 @@ webpack-sources@^1.4.0, webpack-sources@^1.4.1: source-list-map "^2.0.0" source-map "~0.6.1" -webpack@4.41.2: - version "4.41.2" - resolved "https://registry.yarnpkg.com/webpack/-/webpack-4.41.2.tgz#c34ec76daa3a8468c9b61a50336d8e3303dce74e" - integrity sha512-Zhw69edTGfbz9/8JJoyRQ/pq8FYUoY0diOXqW0T6yhgdhCv6wr0hra5DwwWexNRns2Z2+gsnrNcbe9hbGBgk/A== +webpack@4.43.0: + version "4.43.0" + resolved "https://registry.yarnpkg.com/webpack/-/webpack-4.43.0.tgz#c48547b11d563224c561dad1172c8aa0b8a678e6" + integrity sha512-GW1LjnPipFW2Y78OOab8NJlCflB7EFskMih2AHdvjbpKMeDJqEgSx24cXXXiPS65+WSwVyxtDsJH6jGX2czy+g== dependencies: - "@webassemblyjs/ast" "1.8.5" - "@webassemblyjs/helper-module-context" "1.8.5" - "@webassemblyjs/wasm-edit" "1.8.5" - "@webassemblyjs/wasm-parser" "1.8.5" - acorn "^6.2.1" + "@webassemblyjs/ast" "1.9.0" + "@webassemblyjs/helper-module-context" "1.9.0" + "@webassemblyjs/wasm-edit" "1.9.0" + "@webassemblyjs/wasm-parser" "1.9.0" + acorn "^6.4.1" ajv "^6.10.2" ajv-keywords "^3.4.1" chrome-trace-event "^1.0.2" @@ -9838,19 +10488,19 @@ webpack@4.41.2: loader-utils "^1.2.3" memory-fs "^0.4.1" micromatch "^3.1.10" - mkdirp "^0.5.1" + mkdirp "^0.5.3" neo-async "^2.6.1" node-libs-browser "^2.2.1" schema-utils "^1.0.0" tapable "^1.1.3" - terser-webpack-plugin "^1.4.1" - watchpack "^1.6.0" + terser-webpack-plugin "^1.4.3" + watchpack "^1.6.1" webpack-sources "^1.4.1" whatwg-url@^7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-7.0.0.tgz#fde926fa54a599f3adf82dff25a9f7be02dc6edd" - integrity sha512-37GeVSIJ3kn1JgKyjiYNmSLP1yzbpb29jdmwBSgkD9h40/hyrR/OifpVUndji3tmwGgD8qpw7iQu3RSbCrBpsQ== + version "7.1.0" + resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-7.1.0.tgz#c2c492f1eca612988efd3d2266be1b9fc6170d06" + integrity sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg== dependencies: lodash.sortby "^4.7.0" tr46 "^1.0.1" @@ -9861,13 +10511,25 @@ which-module@^2.0.0: resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a" integrity sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho= -which@1, which@1.3.1, which@^1.2.14, which@^1.2.9, which@^1.3.0, which@^1.3.1: +which-pm-runs@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/which-pm-runs/-/which-pm-runs-1.0.0.tgz#670b3afbc552e0b55df6b7780ca74615f23ad1cb" + integrity sha1-Zws6+8VS4LVd9rd4DKdGFfI60cs= + +which@1.3.1, which@^1.2.14, which@^1.2.9, which@^1.3.1: version "1.3.1" resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ== dependencies: isexe "^2.0.0" +which@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" + integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== + dependencies: + isexe "^2.0.0" + wide-align@1.1.3, wide-align@^1.1.0: version "1.1.3" resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.3.tgz#ae074e6bdc0c14a431e804e624549c633b000457" @@ -9875,26 +10537,26 @@ wide-align@1.1.3, wide-align@^1.1.0: dependencies: string-width "^1.0.2 || 2" -widest-line@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/widest-line/-/widest-line-2.0.1.tgz#7438764730ec7ef4381ce4df82fb98a53142a3fc" - integrity sha512-Ba5m9/Fa4Xt9eb2ELXt77JxVDV8w7qQrH0zS/TWSJdLyAwQjWoOzpzj5lwVftDz6n/EOu3tNACS84v509qwnJA== +widest-line@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/widest-line/-/widest-line-3.1.0.tgz#8292333bbf66cb45ff0de1603b136b7ae1496eca" + integrity sha512-NsmoXalsWVDMGupxZ5R08ka9flZjjiLvHVAWYOKtiKM8ujtZWr9cRffak+uSE48+Ob8ObalXpwyeUiyDD6QFgg== dependencies: - string-width "^2.1.1" + string-width "^4.0.0" windows-release@^3.1.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/windows-release/-/windows-release-3.2.0.tgz#8122dad5afc303d833422380680a79cdfa91785f" - integrity sha512-QTlz2hKLrdqukrsapKsINzqMgOUpQW268eJ0OaOpJN32h272waxR9fkB9VoWRtK7uKHG5EHJcTXQBD8XZVJkFA== + version "3.3.0" + resolved "https://registry.yarnpkg.com/windows-release/-/windows-release-3.3.0.tgz#dce167e9f8be733f21c849ebd4d03fe66b29b9f0" + integrity sha512-2HetyTg1Y+R+rUgrKeUEhAG/ZuOmTrI1NBb3ZyAGQMYmOJjBBPe4MTodghRkmLJZHwkuPi02anbeGP+Zf401LQ== dependencies: execa "^1.0.0" -wordwrap@~0.0.2: - version "0.0.3" - resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-0.0.3.tgz#a3d5da6cd5c0bc0008d37234bbaf1bed63059107" - integrity sha1-o9XabNXAvAAI03I0u68b7WMFkQc= +word-wrap@^1.2.3, word-wrap@~1.2.3: + version "1.2.3" + resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c" + integrity sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ== -wordwrap@~1.0.0: +wordwrap@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-1.0.0.tgz#27584810891456a4171c8d0226441ade90cbcaeb" integrity sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus= @@ -9915,6 +10577,22 @@ wrap-ansi@^5.1.0: string-width "^3.0.0" strip-ansi "^5.0.0" +wrap-ansi@^6.2.0: + version "6.2.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-6.2.0.tgz#e9393ba07102e6c91a3b221478f0257cd2856e53" + integrity sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA== + dependencies: + ansi-styles "^4.0.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + +wrapper-webpack-plugin@2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/wrapper-webpack-plugin/-/wrapper-webpack-plugin-2.1.0.tgz#2b5d80f46af84c9eeb707d08796a115e233adeac" + integrity sha512-e+2FhSYGCxhDq3PcUw5mRhH+8vcYa+9d9AuLChJUZ9ZbUPhQOHZ/O2dnN98iTqeUuvrzSSOv13+x/NhrAm5JEg== + dependencies: + webpack-sources "^1.1.0" + wrappy@1: version "1.0.2" resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" @@ -9929,6 +10607,16 @@ write-file-atomic@^2.0.0, write-file-atomic@^2.3.0, write-file-atomic@^2.4.2: imurmurhash "^0.1.4" signal-exit "^3.0.2" +write-file-atomic@^3.0.0: + version "3.0.3" + resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-3.0.3.tgz#56bd5c5a5c70481cd19c571bd39ab965a5de56e8" + integrity sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q== + dependencies: + imurmurhash "^0.1.4" + is-typedarray "^1.0.0" + signal-exit "^3.0.2" + typedarray-to-buffer "^3.1.5" + write-json-file@^2.2.0: version "2.3.0" resolved "https://registry.yarnpkg.com/write-json-file/-/write-json-file-2.3.0.tgz#2b64c8a33004d54b8698c76d585a77ceb61da32f" @@ -9968,26 +10656,31 @@ write@1.0.3: dependencies: mkdirp "^0.5.1" -ws@^5.2.0: - version "5.2.2" - resolved "https://registry.yarnpkg.com/ws/-/ws-5.2.2.tgz#dffef14866b8e8dc9133582514d1befaf96e980f" - integrity sha512-jaHFD6PFv6UgoIVda6qZllptQsMlDEJkTQcybzzXDYM1XO9Y8em691FGMPmM46WGyLU4z9KMgQN+qrux/nhlHA== +ws-heartbeat@1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/ws-heartbeat/-/ws-heartbeat-1.1.0.tgz#7989331381ba5a432092ee6093fa081199549d8f" + integrity sha512-OM24BT52L58vHd9BkKanurOlAKYZCEDM917GVTqQEAlU/bXkJIHJOqL5GPWEcKd+R5frla26BDSlRKrilhiImA== dependencies: - async-limiter "~1.0.0" + ws "6" -ws@^6.0.0: +ws@6: version "6.2.1" resolved "https://registry.yarnpkg.com/ws/-/ws-6.2.1.tgz#442fdf0a47ed64f59b6a5d8ff130f4748ed524fb" integrity sha512-GIyAXC2cB7LjvpgMt9EKS2ldqr0MTrORaleiOno6TweZ6r3TKtoFQWay/2PceJ3RuBasOHzXNn5Lrw1X0bEjqA== dependencies: async-limiter "~1.0.0" -xdg-basedir@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/xdg-basedir/-/xdg-basedir-3.0.0.tgz#496b2cc109eca8dbacfe2dc72b603c17c5870ad4" - integrity sha1-SWsswQnsqNus/i3HK2A8F8WHCtQ= +ws@7.3.0: + version "7.3.0" + resolved "https://registry.yarnpkg.com/ws/-/ws-7.3.0.tgz#4b2f7f219b3d3737bc1a2fbf145d825b94d38ffd" + integrity sha512-iFtXzngZVXPGgpTlP1rBqsUK82p9tKqsWRPg5L56egiljujJT3vGAYnHANvFxBieXrTFavhzhxW52jnaWV+w2w== + +xdg-basedir@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/xdg-basedir/-/xdg-basedir-4.0.0.tgz#4bc8d9984403696225ef83a1573cbbcb4e79db13" + integrity sha512-PSNhEJDejZYV7h50BohL09Er9VaIefr2LMAf3OEmpCkjOi34eYyQYAXUTjEQtZJTKcF0E2UKTh+osDLsgNim9Q== -xml2js@~0.4.0: +xml2js@0.4.19: version "0.4.19" resolved "https://registry.yarnpkg.com/xml2js/-/xml2js-0.4.19.tgz#686c20f213209e94abf0d1bcf1efaa291c7827a7" integrity sha512-esZnJZJOiJR9wWKMyuvSE1y6Dq5LCuJanqhxslH2bxM6duahNZ+HMpCLhBQGZkbX6xRf8x1Y2eJlgt2q3qo49Q== @@ -9995,11 +10688,31 @@ xml2js@~0.4.0: sax ">=0.6.0" xmlbuilder "~9.0.1" +xml2js@^0.4.17: + version "0.4.23" + resolved "https://registry.yarnpkg.com/xml2js/-/xml2js-0.4.23.tgz#a0c69516752421eb2ac758ee4d4ccf58843eac66" + integrity sha512-ySPiMjM0+pLDftHgXY4By0uswI3SPKLDw/i3UXbnO8M/p28zqexCUoPmQFrYD+/1BzhGJSs2i1ERWKJAtiLrug== + dependencies: + sax ">=0.6.0" + xmlbuilder "~11.0.0" + +xmlbuilder@~11.0.0: + version "11.0.1" + resolved "https://registry.yarnpkg.com/xmlbuilder/-/xmlbuilder-11.0.1.tgz#be9bae1c8a046e76b31127726347d0ad7002beb3" + integrity sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA== + xmlbuilder@~9.0.1: version "9.0.7" resolved "https://registry.yarnpkg.com/xmlbuilder/-/xmlbuilder-9.0.7.tgz#132ee63d2ec5565c557e20f4c22df9aca686b10d" integrity sha1-Ey7mPS7FVlxVfiD0wi35rKaGsQ0= +xregexp@^4.3.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/xregexp/-/xregexp-4.3.0.tgz#7e92e73d9174a99a59743f67a4ce879a04b5ae50" + integrity sha512-7jXDIFXh5yJ/orPn4SXjuVrWWoi4Cr8jfV1eHv9CixKSbU+jY4mxfrBwAuDvupPNKpMUY+FeIqsVw/JLT9+B8g== + dependencies: + "@babel/runtime-corejs3" "^7.8.3" + xtend@^4.0.0, xtend@~4.0.1: version "4.0.2" resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54" @@ -10010,28 +10723,57 @@ y18n@^4.0.0: resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.0.tgz#95ef94f85ecc81d007c264e190a120f0a3c8566b" integrity sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w== -yallist@^2.1.2: +yallist@^2.0.0: version "2.1.2" resolved "https://registry.yarnpkg.com/yallist/-/yallist-2.1.2.tgz#1c11f9218f076089a47dd512f93c6699a6a81d52" integrity sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI= yallist@^3.0.0, yallist@^3.0.2, yallist@^3.0.3: - version "3.0.3" - resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.0.3.tgz#b4b049e314be545e3ce802236d6cd22cd91c3de9" - integrity sha512-S+Zk8DEWE6oKpV+vI3qWkaK+jSbIK86pCwe2IF/xwIpQ8jEuxpw9NyaGjmp9+BoJv5FV2piqCDcoCtStppiq2A== + version "3.1.1" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd" + integrity sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g== + +yallist@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" + integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== + +yaml@^1.7.2: + version "1.9.2" + resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.9.2.tgz#f0cfa865f003ab707663e4f04b3956957ea564ed" + integrity sha512-HPT7cGGI0DuRcsO51qC1j9O16Dh1mZ2bnXwsi0jrSpsLz0WxOLSLXfkABVl6bZO629py3CU+OMJtpNHDLB97kg== + dependencies: + "@babel/runtime" "^7.9.2" -yargs-parser@13.1.1, yargs-parser@^13.0.0, yargs-parser@^13.1.0, yargs-parser@^13.1.1: - version "13.1.1" - resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-13.1.1.tgz#d26058532aa06d365fe091f6a1fc06b2f7e5eca0" - integrity sha512-oVAVsHz6uFrg3XQheFII8ESO2ssAf9luWuAd6Wexsu4F3OtIW0o8IribPXYrD4WC24LWtPrJlGy87y5udK+dxQ== +yargonaut@^1.1.2: + version "1.1.4" + resolved "https://registry.yarnpkg.com/yargonaut/-/yargonaut-1.1.4.tgz#c64f56432c7465271221f53f5cc517890c3d6e0c" + integrity sha512-rHgFmbgXAAzl+1nngqOcwEljqHGG9uUZoPjsdZEs1w5JW9RXYzrSvH/u70C1JE5qFi0qjsdhnUX/dJRpWqitSA== + dependencies: + chalk "^1.1.1" + figlet "^1.1.1" + parent-require "^1.0.0" + +yargs-parser@13.1.2, yargs-parser@^13.1.0, yargs-parser@^13.1.2: + version "13.1.2" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-13.1.2.tgz#130f09702ebaeef2650d54ce6e3e5706f7a4fb38" + integrity sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg== dependencies: camelcase "^5.0.0" decamelize "^1.2.0" -yargs-parser@^15.0.0: - version "15.0.0" - resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-15.0.0.tgz#cdd7a97490ec836195f59f3f4dbe5ea9e8f75f08" - integrity sha512-xLTUnCMc4JhxrPEPUYD5IBR1mWCK/aT6+RJ/K29JY2y1vD+FhtgKK0AXRWvI262q3QSffAQuTouFIKUuHX89wQ== +yargs-parser@^15.0.1: + version "15.0.1" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-15.0.1.tgz#54786af40b820dcb2fb8025b11b4d659d76323b3" + integrity sha512-0OAMV2mAZQrs3FkNpDQcBk1x5HXb8X4twADss4S0Iuk+2dGnLOE/fRHrsYm542GduMveyA77OF4wrNJuanRCWw== + dependencies: + camelcase "^5.0.0" + decamelize "^1.2.0" + +yargs-parser@^18.1.1, yargs-parser@^18.1.3: + version "18.1.3" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-18.1.3.tgz#be68c4975c6b2abf469236b0c870362fab09a7b0" + integrity sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ== dependencies: camelcase "^5.0.0" decamelize "^1.2.0" @@ -10062,10 +10804,10 @@ yargs@13.2.4: y18n "^4.0.0" yargs-parser "^13.1.0" -yargs@13.3.0, yargs@^13.2.2, yargs@^13.3.0: - version "13.3.0" - resolved "https://registry.yarnpkg.com/yargs/-/yargs-13.3.0.tgz#4c657a55e07e5f2cf947f8a366567c04a0dedc83" - integrity sha512-2eehun/8ALW8TLoIl7MVaRUrg+yCnenu8B4kBlRxj3GJGDKU1Og7sMXPNm1BYyM1DOJmTZ4YeN/Nwxv+8XJsUA== +yargs@13.3.2, yargs@^13.2.1, yargs@^13.2.2, yargs@^13.3.0: + version "13.3.2" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-13.3.2.tgz#ad7ffefec1aa59565ac915f82dccb38a9c31a2dd" + integrity sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw== dependencies: cliui "^5.0.0" find-up "^3.0.0" @@ -10076,12 +10818,29 @@ yargs@13.3.0, yargs@^13.2.2, yargs@^13.3.0: string-width "^3.0.0" which-module "^2.0.0" y18n "^4.0.0" - yargs-parser "^13.1.1" + yargs-parser "^13.1.2" + +yargs@15.3.1, yargs@^15.0.0, yargs@^15.0.2: + version "15.3.1" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-15.3.1.tgz#9505b472763963e54afe60148ad27a330818e98b" + integrity sha512-92O1HWEjw27sBfgmXiixJWT5hRBp2eobqXicLtPBIDBhYB+1HpwZlXmbW2luivBJHBzki+7VyCLRtAkScbTBQA== + dependencies: + cliui "^6.0.0" + decamelize "^1.2.0" + find-up "^4.1.0" + get-caller-file "^2.0.1" + require-directory "^2.1.1" + require-main-filename "^2.0.0" + set-blocking "^2.0.0" + string-width "^4.2.0" + which-module "^2.0.0" + y18n "^4.0.0" + yargs-parser "^18.1.1" -yargs@14.2.0, yargs@^14.2.0: - version "14.2.0" - resolved "https://registry.yarnpkg.com/yargs/-/yargs-14.2.0.tgz#f116a9242c4ed8668790b40759b4906c276e76c3" - integrity sha512-/is78VKbKs70bVZH7w4YaZea6xcJWOAwkhbR0CFuZBmYtfTYF0xjGJF43AYd8g2Uii1yJwmS5GR2vBmrc32sbg== +yargs@^14.2.2: + version "14.2.3" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-14.2.3.tgz#1a1c3edced1afb2a2fea33604bc6d1d8d688a414" + integrity sha512-ZbotRWhF+lkjijC/VhmOT9wSgyBQ7+zr13+YLkhfsSiTriYsMzkTUFP18pFhWwBeMa5gUc1MzbhrO6/VB7c9Xg== dependencies: cliui "^5.0.0" decamelize "^1.2.0" @@ -10093,44 +10852,14 @@ yargs@14.2.0, yargs@^14.2.0: string-width "^3.0.0" which-module "^2.0.0" y18n "^4.0.0" - yargs-parser "^15.0.0" - -yauzl@2.10.0: - version "2.10.0" - resolved "https://registry.yarnpkg.com/yauzl/-/yauzl-2.10.0.tgz#c7eb17c93e112cb1086fa6d8e51fb0667b79a5f9" - integrity sha1-x+sXyT4RLLEIb6bY5R+wZnt5pfk= - dependencies: - buffer-crc32 "~0.2.3" - fd-slicer "~1.1.0" + yargs-parser "^15.0.1" -yauzl@2.4.1: - version "2.4.1" - resolved "https://registry.yarnpkg.com/yauzl/-/yauzl-2.4.1.tgz#9528f442dab1b2284e58b4379bb194e22e0c4005" - integrity sha1-lSj0QtqxsihOWLQ3m7GU4i4MQAU= - dependencies: - fd-slicer "~1.0.1" - -yazl@^2.4.3: - version "2.5.1" - resolved "https://registry.yarnpkg.com/yazl/-/yazl-2.5.1.tgz#a3d65d3dd659a5b0937850e8609f22fffa2b5c35" - integrity sha512-phENi2PLiHnHb6QBVot+dJnaAZ0xosj7p3fWl+znIjBDlnMI2PsZCJZ306BPTFOaHf5qdDEI8x5qFrSOBN5vrw== - dependencies: - buffer-crc32 "~0.2.3" +yn@3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/yn/-/yn-3.1.1.tgz#1e87401a09d767c1d5eab26a6e4c185182d2eb50" + integrity sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q== yn@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/yn/-/yn-2.0.0.tgz#e5adabc8acf408f6385fc76495684c88e6af689a" integrity sha1-5a2ryKz0CPY4X8dklWhMiOavaJo= - -yn@^3.0.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/yn/-/yn-3.1.0.tgz#fcbe2db63610361afcc5eb9e0ac91e976d046114" - integrity sha512-kKfnnYkbTfrAdd0xICNFw7Atm8nKpLcLv9AZGEt+kczL/WQVai4e2V6ZN8U/O+iI6WrNuJjNNOyu4zfhl9D3Hg== - -zip-webpack-plugin@3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/zip-webpack-plugin/-/zip-webpack-plugin-3.0.0.tgz#63b3c173f1a87a006915cd7328a3c40b44dc8e32" - integrity sha512-5kNvPv+TUP3JqKWQUXj0vTgXHIRQpYw5YyBUVXQ0pumTAK+a4OZ+eXDHnh44nyr9B1XJQZq9WtSSm5j6NQhjWQ== - dependencies: - webpack-sources "^1.1.0" - yazl "^2.4.3"