From 74cbcdb03dd468da1844523183f2dedb57458085 Mon Sep 17 00:00:00 2001 From: TULCHINSKI LIRAN Date: Mon, 28 Sep 2026 17:58:03 +0300 Subject: [PATCH] feat: scaffold 3d-ops-trigger skeleton (MAPCO-11834) Rename boilerplate identity to 3d-ops-trigger across package.json, openapi, helm chart/values, catalog-info and README. Pin Node>=24 (engines) and wire core deps: @map-colonies/jobnik-sdk, @map-colonies/3d-shared (local file link), zod. --- README.md | 123 +++++++----------------------- catalog-info.yaml | 10 +-- helm/Chart.yaml | 4 +- helm/values.yaml | 6 +- openapi3.yaml | 4 +- package-lock.json | 190 +++++++++++++++++++++++++++++++++++++++++++--- package.json | 12 ++- 7 files changed, 225 insertions(+), 124 deletions(-) diff --git a/README.md b/README.md index aa757fb..4e601b6 100644 --- a/README.md +++ b/README.md @@ -1,124 +1,53 @@ -# Map Colonies typescript service template +# 3D Operations Trigger ---------------------------------- -This is a basic repo template for building new MapColonies web services in Typescript. +Entry point for 3D operations — **ingest, delete, update, publish/unpublish**. It validates incoming requests (light & fast) and triggers the corresponding [Jobnik](https://mapcolonies.github.io/infra-portal/docs/knowledge-base/jobnik) jobs. Successor to the legacy `3d-gateway` (and, in the near future, `store-trigger`). -> [!IMPORTANT] -> To regenerate the types on openapi change run the command `npm run generate:openapi-types`. +> Epic: MAPCO-11833 · Design: [3D Ingestion High level architecture](https://mapcolonies.atlassian.net/wiki/spaces/MAPConflicResolution/pages/3342172161) · See [docs/PRD.md](./docs/PRD.md) -> [!WARNING] -> After creating a new repo based on this template, you should delete the CODEOWNERS file. - - -## Development -When in development you should use the command `npm run start:dev`. The main benefits are that it enables offline mode for the config package, and source map support for NodeJS errors. - -### Template Features: - -- eslint configuration by [@map-colonies/eslint-config](https://github.com/MapColonies/eslint-config) - -- prettier configuration by [@map-colonies/prettier-config](https://github.com/MapColonies/prettier-config) - -- jest - -- .nvmrc - -- Multi stage production-ready Dockerfile - -- commitlint - -- git hooks - -- logging by [@map-colonies/js-logger](https://github.com/MapColonies/js-logger) - -- OpenAPI request validation - -- config load with [node-config](https://www.npmjs.com/package/node-config) - -- Tracing and metrics by [@map-colonies/telemetry](https://github.com/MapColonies/telemetry) - -- github templates - -- bug report - -- feature request - -- pull request - -- github actions - -- on pull_request - -- LGTM +## API -- test +| Endpoint | Method | Summary | +| --- | --- | --- | +| `/record` | POST | Start an ingestion flow (validate, create Jobnik ingestion job) | +| `/record/{id}` | DELETE | Validate deletability, create Jobnik delete job | +| `/record/{id}` | PATCH | Update metadata for a record | +| `/record/status/{id}` | PATCH | Publish / unpublish a record | -- lint +Full OpenAPI spec: [openapi3.yaml](/openapi3.yaml). Regenerate types on spec change with `npm run generate:openapi-types`. -- snyk +## Requirements -## API -Checkout the OpenAPI spec [here](/openapi3.yaml) +- **Node.js ≥ 24** (required by `@map-colonies/jobnik-sdk`). Use the pinned version via `nvm use`. ## Installation -Install deps with npm - ```bash npm install ``` -## Run Locally - -Clone the project - -```bash - -git clone https://link-to-project - -``` - -Go to the project directory - -```bash - -cd my-project - -``` - -Install dependencies +> During development this service consumes `@map-colonies/3d-shared` (and, later, mc-models v2) via local `file:` links until those packages are published to npm. -```bash - -npm install - -``` - -Start the server +## Run Locally ```bash - -npm run start - +npm run start # build + run +npm run start:dev # offline config + source maps ``` ## Running Tests -To run tests, run the following command - ```bash - -npm run test - +npm run test # all +npm run test:unit # unit only +npm run test:integration # integration only ``` -To only run unit tests: -```bash -npm run test:unit -``` +## Development notes -To only run integration tests: -```bash -npm run test:integration -``` +- eslint / prettier via `@map-colonies/eslint-config` and `@map-colonies/prettier-config` +- vitest for tests +- OpenAPI request validation at the middleware layer +- config via [node-config](https://www.npmjs.com/package/node-config) +- tracing & metrics via `@map-colonies/telemetry` diff --git a/catalog-info.yaml b/catalog-info.yaml index 55a51de..b4ab881 100644 --- a/catalog-info.yaml +++ b/catalog-info.yaml @@ -1,17 +1,17 @@ apiVersion: backstage.io/v1alpha1 kind: Component metadata: - name: ts-server-boilerplate - description: A boilerplate github repo for a REST API service in NodeJS for MapColonies + name: 3d-ops-trigger + description: Entry point for 3D operations (ingest, delete, update, publish/unpublish) — validates requests and triggers Jobnik jobs annotations: - github.com/project-slug: MapColonies/ts-server-boilerplate + github.com/project-slug: MapColonies/3d-ops-trigger tags: - nodejs - typescript - expressjs - - boilerplate + - 3d spec: type: service lifecycle: production owner: DevInfra - system: boilerplate + system: 3d diff --git a/helm/Chart.yaml b/helm/Chart.yaml index b1f2c0f..853bc57 100644 --- a/helm/Chart.yaml +++ b/helm/Chart.yaml @@ -1,6 +1,6 @@ apiVersion: v2 -name: ts-server-boilerplate -description: A Helm chart for ts-server-boilerplate service +name: 3d-ops-trigger +description: A Helm chart for the 3d-ops-trigger service type: application version: 1.0.0 appVersion: 1.0.0 diff --git a/helm/values.yaml b/helm/values.yaml index f982729..46736ea 100644 --- a/helm/values.yaml +++ b/helm/values.yaml @@ -7,7 +7,7 @@ global: mclabels: component: backend - partOf: boilerplates + partOf: 3d owner: common prometheus: enabled: true @@ -23,7 +23,7 @@ fullnameOverride: "" configManagement: offlineMode: false - name: 'service-name' + name: '3d-ops-trigger' version: 'latest' serverUrl: 'http://localhost:8080/api' @@ -66,7 +66,7 @@ caPath: '/usr/local/share/ca-certificates' caKey: 'ca.crt' image: - repository: ts-server-boilerplate + repository: 3d-ops-trigger # If commented, appVersion will be taken. See: _helpers.tpl # tag: 'latest' pullPolicy: IfNotPresent diff --git a/openapi3.yaml b/openapi3.yaml index 55ee8ee..710048e 100644 --- a/openapi3.yaml +++ b/openapi3.yaml @@ -1,7 +1,7 @@ openapi: 3.0.1 info: - title: service-name - description: basic template for map colonies service + title: 3d-ops-trigger + description: Entry point for 3D operations — validates requests and triggers Jobnik jobs version: 1.0.0 license: name: MIT diff --git a/package-lock.json b/package-lock.json index 6ee91ea..0a0f98a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,18 +1,20 @@ { - "name": "service-name", + "name": "3d-ops-trigger", "version": "1.0.0", "lockfileVersion": 3, "requires": true, "packages": { "": { - "name": "service-name", + "name": "3d-ops-trigger", "version": "1.0.0", "license": "ISC", "dependencies": { "@godaddy/terminus": "^4.12.1", + "@map-colonies/3d-shared": "file:../../3d-general/3d-shared", "@map-colonies/config": "^4.0.1", "@map-colonies/error-express-handler": "^4.0.0", "@map-colonies/express-access-log-middleware": "^5.0.0", + "@map-colonies/jobnik-sdk": "^1.0.1", "@map-colonies/js-logger": "^5.0.0", "@map-colonies/openapi-express-viewer": "^5.0.0", "@map-colonies/prometheus": "^1.0.0", @@ -27,7 +29,8 @@ "http-status-codes": "^2.3.0", "prom-client": "^15.1.3", "reflect-metadata": "^0.2.2", - "tsyringe": "^4.8.0" + "tsyringe": "^4.8.0", + "zod": "^3.24.1" }, "devDependencies": { "@commitlint/cli": "^21.2.2", @@ -60,6 +63,48 @@ "tsc-alias": "^1.9.1", "typescript": "^5.9.3", "vitest": "^4.1.10" + }, + "engines": { + "node": ">=24.0.0" + } + }, + "../../3d-general/3d-shared": { + "name": "@map-colonies/3d-shared", + "version": "1.0.0", + "license": "ISC", + "dependencies": { + "@map-colonies/types": "^1.4.0", + "geojson": "^0.5.0", + "mime-types": "^2.1.35", + "zod": "^3.24.1" + }, + "devDependencies": { + "@commitlint/cli": "^19.6.1", + "@commitlint/config-conventional": "^19.6.0", + "@map-colonies/eslint-config": "^4.0.0", + "@map-colonies/prettier-config": "^0.0.1", + "@swc/core": "^1.7.26", + "@swc/jest": "^0.2.36", + "@types/geojson": "^7946.0.14", + "@types/jest": "^29.4.0", + "@types/node": "^20.0.0", + "@typescript-eslint/eslint-plugin": "^6.0.0", + "@typescript-eslint/parser": "^6.0.0", + "commitlint": "^19.6.1", + "eslint": "^8.36.0", + "husky": "^9.1.7", + "jest": "^29.0.0", + "jest-html-reporters": "^3.1.4", + "prettier": "^3.4.2", + "pretty-quick": "^4.0.0", + "rimraf": "^5.0.0", + "standard-version": "^9.5.0", + "ts-jest": "^29.0.0", + "typedoc": "^0.27.6", + "typescript": "^5.0.0" + }, + "engines": { + "node": ">=24.0.0" } }, "node_modules/@apideck/better-ajv-errors": { @@ -99,7 +144,6 @@ "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.7.tgz", "integrity": "sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==", - "dev": true, "license": "MIT", "dependencies": { "@babel/helper-validator-identifier": "^7.29.7", @@ -114,7 +158,6 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true, "license": "MIT" }, "node_modules/@babel/helper-string-parser": { @@ -131,7 +174,6 @@ "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.29.7.tgz", "integrity": "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==", - "dev": true, "license": "MIT", "engines": { "node": ">=6.9.0" @@ -157,7 +199,6 @@ "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.29.7.tgz", "integrity": "sha512-Nq8OhGWiZIZGV6hLHoyAKLLcJihP/xFeBMGJoUrxTX2psI8dCifzLhZISFb+VWS3wFMRDmCGw5R+dOySCqPLhw==", - "dev": true, "license": "MIT", "engines": { "node": ">=6.9.0" @@ -888,7 +929,6 @@ "version": "2.0.4", "resolved": "https://registry.npmjs.org/@humanwhocodes/momoa/-/momoa-2.0.4.tgz", "integrity": "sha512-RE815I4arJFtt+FVeU1Tgp9/Xvecacji8w/V6XtXsWWH/wz/eNkNbhb+ny/+PlVZjV0rxQpRSQKNKE3lcktHEA==", - "dev": true, "license": "Apache-2.0", "engines": { "node": ">=10.10.0" @@ -969,6 +1009,10 @@ "integrity": "sha512-4JQNk+3mVzK3xh2rqd6RB4J46qUR19azEHBneZyTZM+c456qOrbbM/5xcR8huNCCcbVt7+UmizG6GuUvPvKUYg==", "license": "MIT" }, + "node_modules/@map-colonies/3d-shared": { + "resolved": "../../3d-general/3d-shared", + "link": true + }, "node_modules/@map-colonies/commitlint-config": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/@map-colonies/commitlint-config/-/commitlint-config-2.0.1.tgz", @@ -1134,6 +1178,81 @@ "node": ">=24" } }, + "node_modules/@map-colonies/jobnik-sdk": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@map-colonies/jobnik-sdk/-/jobnik-sdk-1.0.1.tgz", + "integrity": "sha512-ieV91q/+F+5QWaKjW3rmvNTfENw/1mLOM/LlMqb6khYs7ODQMCQcXapEYM3VIifYMc51E46XezHNXOxQKBUtfA==", + "license": "ISC", + "dependencies": { + "@map-colonies/read-pkg": "^2.0.0", + "@map-colonies/semantic-conventions": "^1.0.0", + "@readme/openapi-parser": "^4.0.1", + "date-fns": "^4.1.0", + "http-status-codes": "^2.3.0", + "openapi-fetch": "^0.14.0", + "opossum": "^9.0.0", + "undici": "^7.19.1", + "yaml": "^2.8.1" + }, + "engines": { + "node": ">=24" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.9.0", + "prom-client": "^15.0.0" + } + }, + "node_modules/@map-colonies/jobnik-sdk/node_modules/@apidevtools/json-schema-ref-parser": { + "version": "13.0.5", + "resolved": "https://registry.npmjs.org/@apidevtools/json-schema-ref-parser/-/json-schema-ref-parser-13.0.5.tgz", + "integrity": "sha512-xfh4xVJD62gG6spIc7lwxoWT+l16nZu1ELyU8FkjaP/oD2yP09EvLAU6KhtudN9aML2Khhs9pY6Slr7KGTES3w==", + "license": "MIT", + "dependencies": { + "@types/json-schema": "^7.0.15", + "js-yaml": "^4.1.0" + }, + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://github.com/sponsors/philsturgeon" + } + }, + "node_modules/@map-colonies/jobnik-sdk/node_modules/@readme/openapi-parser": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/@readme/openapi-parser/-/openapi-parser-4.1.2.tgz", + "integrity": "sha512-lAFH88r/CHs5VZDUocEda0OSMSQsr6801sziIjOKyVA+0hSFN+BPuelPF5XvkMROHecnPd+XEJN1iNQqCgER/g==", + "license": "MIT", + "dependencies": { + "@apidevtools/json-schema-ref-parser": "^13.0.5", + "@readme/better-ajv-errors": "^2.3.2", + "@readme/openapi-schemas": "^3.1.0", + "@types/json-schema": "^7.0.15", + "ajv": "^8.12.0", + "ajv-draft-04": "^1.0.0" + }, + "engines": { + "node": ">=20" + }, + "peerDependencies": { + "openapi-types": ">=7" + } + }, + "node_modules/@map-colonies/jobnik-sdk/node_modules/yaml": { + "version": "2.9.1", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.9.1.tgz", + "integrity": "sha512-3NxN8+78OdzbT7C/WjGsyfPAtJaN3FNDsWxv7Y7mcDsT/oOmgW8BpyQQFFBnvZE3j9Y2Sdz1ULFLezL7Eb2yFw==", + "license": "ISC", + "bin": { + "yaml": "bin.mjs" + }, + "engines": { + "node": ">= 14.6" + }, + "funding": { + "url": "https://github.com/sponsors/eemeli" + } + }, "node_modules/@map-colonies/js-logger": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/@map-colonies/js-logger/-/js-logger-5.0.0.tgz", @@ -1281,6 +1400,14 @@ "integrity": "sha512-XUQ2/gvC6d6/nGMKIGVyZo/kemGH5uGOy4xhkYCVLNV/CJsHvJXmhiov7QpqVpb58x+/2WVVUThAStj9ogipug==", "license": "MIT" }, + "node_modules/@map-colonies/semantic-conventions": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@map-colonies/semantic-conventions/-/semantic-conventions-1.0.0.tgz", + "integrity": "sha512-QcR+hHVcH3GyHtOXjmsWZXWWR6GG4Kx8+xWdkjuLlBAEG6sLX6hQObLJ1H5YhLZiw3HKCA39hjLwXyl+6ExUNg==", + "engines": { + "node": ">=24" + } + }, "node_modules/@map-colonies/tracing": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/@map-colonies/tracing/-/tracing-1.0.0.tgz", @@ -3543,7 +3670,6 @@ "version": "2.4.0", "resolved": "https://registry.npmjs.org/@readme/better-ajv-errors/-/better-ajv-errors-2.4.0.tgz", "integrity": "sha512-9WODaOAKSl/mU+MYNZ2aHCrkoRSvmQ+1YkLj589OEqqjOAhbn8j7Z+ilYoiTu/he6X63/clsxxAB4qny9/dDzg==", - "dev": true, "license": "Apache-2.0", "dependencies": { "@babel/code-frame": "^7.22.5", @@ -3611,7 +3737,6 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/@readme/openapi-schemas/-/openapi-schemas-3.1.0.tgz", "integrity": "sha512-9FC/6ho8uFa8fV50+FPy/ngWN53jaUu4GRXlAjcxIRrzhltJnpKkBG2Tp0IDraFJeWrOpk84RJ9EMEEYzaI1Bw==", - "dev": true, "license": "MIT", "engines": { "node": ">=18" @@ -6415,6 +6540,16 @@ "node": ">= 8" } }, + "node_modules/date-fns": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-4.4.0.tgz", + "integrity": "sha512-+1UMbeh68lH1SegH83CGWwpb6OHHbpSgr3+s5Eww5M4CAgswBpoWS0AjTOfEJ33HiYKz1hdj/KTFprzXHmq/6w==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/kossnocorp" + } + }, "node_modules/dateformat": { "version": "4.6.3", "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-4.6.3.tgz", @@ -9481,6 +9616,15 @@ "@jsdevtools/ono": "7.1.3" } }, + "node_modules/openapi-fetch": { + "version": "0.14.1", + "resolved": "https://registry.npmjs.org/openapi-fetch/-/openapi-fetch-0.14.1.tgz", + "integrity": "sha512-l7RarRHxlEZYjMLd/PR0slfMVse2/vvIAGm75/F7J6MlQ8/b9uUQmUF2kCPrQhJqMXSxmYWObVgeYXbFYzZR+A==", + "license": "MIT", + "dependencies": { + "openapi-typescript-helpers": "^0.0.15" + } + }, "node_modules/openapi-response-validator": { "version": "9.3.1", "resolved": "https://registry.npmjs.org/openapi-response-validator/-/openapi-response-validator-9.3.1.tgz", @@ -9541,7 +9685,6 @@ "version": "12.1.3", "resolved": "https://registry.npmjs.org/openapi-types/-/openapi-types-12.1.3.tgz", "integrity": "sha512-N4YtSYJqghVu4iek2ZUvcN/0aqH1kRDuNqzcycDxhOUpg7GdvLa2F3DgS6yBNhInhv2r/6I0Flkn7CqL8+nIcw==", - "dev": true, "license": "MIT" }, "node_modules/openapi-typescript": { @@ -9566,6 +9709,12 @@ "typescript": "^5.x" } }, + "node_modules/openapi-typescript-helpers": { + "version": "0.0.15", + "resolved": "https://registry.npmjs.org/openapi-typescript-helpers/-/openapi-typescript-helpers-0.0.15.tgz", + "integrity": "sha512-opyTPaunsklCBpTK8JGef6mfPhLSnyy5a0IN9vKtx3+4aExf+KxEqYwIy3hqkedXIB97u357uLMJsOnm3GVjsw==", + "license": "MIT" + }, "node_modules/openapi-typescript/node_modules/parse-json": { "version": "8.3.0", "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-8.3.0.tgz", @@ -9643,6 +9792,15 @@ "@types/node": "*" } }, + "node_modules/opossum": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/opossum/-/opossum-9.0.0.tgz", + "integrity": "sha512-K76U0QkxOfUZamneQuzz+AP0fyfTJcCplZ2oZL93nxeupuJbN4s6uFNbmVCt4eWqqGqRnnowdFuBicJ1fLMVxw==", + "license": "Apache-2.0", + "engines": { + "node": "^24 || ^22 || ^20" + } + }, "node_modules/optionator": { "version": "0.9.4", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", @@ -10115,7 +10273,6 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", - "dev": true, "license": "ISC" }, "node_modules/picomatch": { @@ -12450,6 +12607,15 @@ "funding": { "url": "https://github.com/sponsors/sindresorhus" } + }, + "node_modules/zod": { + "version": "3.25.76", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", + "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } } } } diff --git a/package.json b/package.json index f080ea9..5791b80 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { - "name": "service-name", + "name": "3d-ops-trigger", "version": "1.0.0", - "description": "This is template for map colonies typescript service", + "description": "Entry point for 3D operations (ingest, delete, update, publish/unpublish) — validates requests and triggers Jobnik jobs", "main": "./src/index.ts", "scripts": { "test:unit": "vitest run --coverage.enabled=false --project unit", @@ -30,9 +30,14 @@ }, "author": "MapColonies", "license": "ISC", + "engines": { + "node": ">=24.0.0" + }, "dependencies": { "@godaddy/terminus": "^4.12.1", + "@map-colonies/3d-shared": "file:../../3d-general/3d-shared", "@map-colonies/config": "^4.0.1", + "@map-colonies/jobnik-sdk": "^1.0.1", "@map-colonies/error-express-handler": "^4.0.0", "@map-colonies/express-access-log-middleware": "^5.0.0", "@map-colonies/js-logger": "^5.0.0", @@ -49,7 +54,8 @@ "http-status-codes": "^2.3.0", "prom-client": "^15.1.3", "reflect-metadata": "^0.2.2", - "tsyringe": "^4.8.0" + "tsyringe": "^4.8.0", + "zod": "^3.24.1" }, "devDependencies": { "@commitlint/cli": "^21.2.2",