From 3745d7a414b6306f44702481f2b9679f789524d5 Mon Sep 17 00:00:00 2001 From: Dave Date: Tue, 10 Feb 2026 12:48:48 -0500 Subject: [PATCH] feature/IO-3556-Chattr-Integration --- .ebignore | 3 +- docker-compose-cluster.yml | 51 ++------------- docker-compose.yml | 50 ++------------ hasura/metadata/tables.yaml | 2 + .../down.sql | 4 ++ .../up.sql | 2 + localstack/init/10-bootstrap.sh | 65 +++++++++++++++++++ server.js | 1 + server/chatter/createLocation.js | 22 +++++++ server/graphql-client/queries.js | 11 +++- server/routes/chatterRoutes.js | 12 ++++ 11 files changed, 131 insertions(+), 92 deletions(-) create mode 100644 hasura/migrations/1770737662785_alter_table_public_bodyshops_add_column_chatter_company_id/down.sql create mode 100644 hasura/migrations/1770737662785_alter_table_public_bodyshops_add_column_chatter_company_id/up.sql create mode 100644 localstack/init/10-bootstrap.sh create mode 100644 server/chatter/createLocation.js create mode 100644 server/routes/chatterRoutes.js diff --git a/.ebignore b/.ebignore index f407b0596..4043a0f46 100644 --- a/.ebignore +++ b/.ebignore @@ -13,4 +13,5 @@ .env.development.local .env.test.local .env.production.local -bodyshop_translations.babel \ No newline at end of file +.env.localstack.docker +bodyshop_translations.babel diff --git a/docker-compose-cluster.yml b/docker-compose-cluster.yml index bbce31dd4..6988d4d35 100644 --- a/docker-compose-cluster.yml +++ b/docker-compose-cluster.yml @@ -38,8 +38,6 @@ services: condition: service_healthy localstack: condition: service_healthy - aws-cli: - condition: service_completed_successfully ports: - "4001:4000" # Different external port for local access volumes: @@ -65,8 +63,6 @@ services: condition: service_healthy localstack: condition: service_healthy - aws-cli: - condition: service_completed_successfully ports: - "4002:4000" # Different external port for local access volumes: @@ -92,8 +88,6 @@ services: condition: service_healthy localstack: condition: service_healthy - aws-cli: - condition: service_completed_successfully ports: - "4003:4000" # Different external port for local access volumes: @@ -156,23 +150,18 @@ services: # LocalStack localstack: - image: localstack/localstack + image: localstack/localstack:4.13.1 container_name: localstack hostname: localstack networks: - redis-cluster-net restart: unless-stopped volumes: + - ./certs:/tmp/certs:ro # only if your script reads /tmp/certs/... + - ./localstack/init:/etc/localstack/init/ready.d:ro - /var/run/docker.sock:/var/run/docker.sock - environment: - - SERVICES=s3,ses,secretsmanager,cloudwatch,logs - - DEBUG=0 - - AWS_ACCESS_KEY_ID=test - - AWS_SECRET_ACCESS_KEY=test - - AWS_DEFAULT_REGION=ca-central-1 - - EXTRA_CORS_ALLOWED_HEADERS=Authorization,Content-Type - - EXTRA_CORS_ALLOWED_ORIGINS=* - - EXTRA_CORS_EXPOSE_HEADERS=Authorization,Content-Type + env_file: + - .env.localstack.docker ports: - "4566:4566" healthcheck: @@ -182,36 +171,6 @@ services: retries: 5 start_period: 20s - # AWS-CLI - aws-cli: - image: amazon/aws-cli - container_name: aws-cli - hostname: aws-cli - networks: - - redis-cluster-net - depends_on: - localstack: - condition: service_healthy - volumes: - - './localstack:/tmp/localstack' - - './certs:/tmp/certs' - environment: - - AWS_ACCESS_KEY_ID=test - - AWS_SECRET_ACCESS_KEY=test - - AWS_DEFAULT_REGION=ca-central-1 - entrypoint: /bin/sh -c - command: > - " - aws --endpoint-url=http://localstack:4566 ses verify-domain-identity --domain imex.online --region ca-central-1 - aws --endpoint-url=http://localstack:4566 ses verify-email-identity --email-address noreply@imex.online --region ca-central-1 - aws --endpoint-url=http://localstack:4566 secretsmanager create-secret --name CHATTER_PRIVATE_KEY --secret-string file:///tmp/certs/io-ftp-test.key - aws --endpoint-url=http://localstack:4566 logs create-log-group --log-group-name development --region ca-central-1 - aws --endpoint-url=http://localstack:4566 s3api create-bucket --bucket imex-large-log --create-bucket-configuration LocationConstraint=ca-central-1 - aws --endpoint-url=http://localstack:4566 s3api create-bucket --bucket imex-carfax-uploads --create-bucket-configuration LocationConstraint=ca-central-1 - aws --endpoint-url=http://localstack:4566 s3api create-bucket --bucket rome-carfax-uploads --create-bucket-configuration LocationConstraint=ca-central-1 - aws --endpoint-url=http://localstack:4566 s3api create-bucket --bucket rps-carfax-uploads --create-bucket-configuration LocationConstraint=ca-central-1 - " - networks: redis-cluster-net: driver: bridge diff --git a/docker-compose.yml b/docker-compose.yml index 0662dd9bd..f2a0f160c 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -68,23 +68,18 @@ services: # LocalStack: Used to emulate AWS services locally, currently setup for SES # Notes: Set the ENV Debug to 1 for additional logging localstack: - image: localstack/localstack + image: localstack/localstack:4.13.1 container_name: localstack hostname: localstack networks: - redis-cluster-net restart: unless-stopped volumes: + - ./certs:/tmp/certs:ro # only if your script reads /tmp/certs/... + - ./localstack/init:/etc/localstack/init/ready.d:ro - /var/run/docker.sock:/var/run/docker.sock - environment: - - SERVICES=s3,ses,secretsmanager,cloudwatch,logs - - DEBUG=0 - - AWS_ACCESS_KEY_ID=test - - AWS_SECRET_ACCESS_KEY=test - - AWS_DEFAULT_REGION=ca-central-1 - - EXTRA_CORS_ALLOWED_HEADERS=Authorization,Content-Type - - EXTRA_CORS_ALLOWED_ORIGINS=* - - EXTRA_CORS_EXPOSE_HEADERS=Authorization,Content-Type + env_file: + - .env.localstack.docker ports: - "4566:4566" healthcheck: @@ -94,38 +89,6 @@ services: retries: 5 start_period: 20s - # AWS-CLI - Used in conjunction with LocalStack to set required permission to send emails - aws-cli: - image: amazon/aws-cli - container_name: aws-cli - hostname: aws-cli - networks: - - redis-cluster-net - depends_on: - localstack: - condition: service_healthy - volumes: - - './localstack:/tmp/localstack' - - './certs:/tmp/certs' - - environment: - - AWS_ACCESS_KEY_ID=test - - AWS_SECRET_ACCESS_KEY=test - - AWS_DEFAULT_REGION=ca-central-1 - entrypoint: /bin/sh -c - command: > - " - aws --endpoint-url=http://localstack:4566 ses verify-domain-identity --domain imex.online --region ca-central-1 - aws --endpoint-url=http://localstack:4566 ses verify-email-identity --email-address noreply@imex.online --region ca-central-1 - aws --endpoint-url=http://localstack:4566 secretsmanager create-secret --name CHATTER_PRIVATE_KEY --secret-string file:///tmp/certs/io-ftp-test.key - aws --endpoint-url=http://localstack:4566 logs create-log-group --log-group-name development --region ca-central-1 - aws --endpoint-url=http://localstack:4566 s3api create-bucket --bucket imex-large-log --create-bucket-configuration LocationConstraint=ca-central-1 - aws --endpoint-url=http://localstack:4566 s3api create-bucket --bucket imex-job-totals --create-bucket-configuration LocationConstraint=ca-central-1 - aws --endpoint-url=http://localstack:4566 s3api create-bucket --bucket parts-estimates --create-bucket-configuration LocationConstraint=ca-central-1 - aws --endpoint-url=http://localstack:4566 s3api create-bucket --bucket imex-carfax-uploads --create-bucket-configuration LocationConstraint=ca-central-1 - aws --endpoint-url=http://localstack:4566 s3api create-bucket --bucket rome-carfax-uploads --create-bucket-configuration LocationConstraint=ca-central-1 - aws --endpoint-url=http://localstack:4566 s3api create-bucket --bucket rps-carfax-uploads --create-bucket-configuration LocationConstraint=ca-central-1 - " # Node App: The Main IMEX API node-app: build: @@ -145,8 +108,7 @@ services: condition: service_healthy localstack: condition: service_healthy - aws-cli: - condition: service_completed_successfully + ports: - "4000:4000" - "9229:9229" diff --git a/hasura/metadata/tables.yaml b/hasura/metadata/tables.yaml index 7cf11c8de..2f29ac26c 100644 --- a/hasura/metadata/tables.yaml +++ b/hasura/metadata/tables.yaml @@ -947,6 +947,7 @@ - carfax_exclude - cdk_configuration - cdk_dealerid + - chatter_company_id - chatterid - city - claimscorpid @@ -1063,6 +1064,7 @@ - bill_allow_post_to_closed - bill_tax_rates - cdk_configuration + - chatter_company_id - city - country - created_at diff --git a/hasura/migrations/1770737662785_alter_table_public_bodyshops_add_column_chatter_company_id/down.sql b/hasura/migrations/1770737662785_alter_table_public_bodyshops_add_column_chatter_company_id/down.sql new file mode 100644 index 000000000..291344aa9 --- /dev/null +++ b/hasura/migrations/1770737662785_alter_table_public_bodyshops_add_column_chatter_company_id/down.sql @@ -0,0 +1,4 @@ +-- Could not auto-generate a down migration. +-- Please write an appropriate down migration for the SQL below: +-- alter table "public"."bodyshops" add column "chatter_company_id" text +-- null; diff --git a/hasura/migrations/1770737662785_alter_table_public_bodyshops_add_column_chatter_company_id/up.sql b/hasura/migrations/1770737662785_alter_table_public_bodyshops_add_column_chatter_company_id/up.sql new file mode 100644 index 000000000..4a6e1e8d4 --- /dev/null +++ b/hasura/migrations/1770737662785_alter_table_public_bodyshops_add_column_chatter_company_id/up.sql @@ -0,0 +1,2 @@ +alter table "public"."bodyshops" add column "chatter_company_id" text + null; diff --git a/localstack/init/10-bootstrap.sh b/localstack/init/10-bootstrap.sh new file mode 100644 index 000000000..ee8183d1e --- /dev/null +++ b/localstack/init/10-bootstrap.sh @@ -0,0 +1,65 @@ +#!/usr/bin/env bash +echo "Running LocalStack bootstrap script: 10-bootstrap.sh" + +set -euo pipefail + +REGION="${AWS_DEFAULT_REGION:-ca-central-1}" + +# awslocal is the LocalStack wrapper so you don't need --endpoint-url +# (it targets the LocalStack gateway automatically) +# Docs: https://docs.localstack.cloud/.../aws-cli/ +ensure_bucket() { + local b="$1" + if ! awslocal s3api head-bucket --bucket "$b" >/dev/null 2>&1; then + awslocal s3api create-bucket \ + --bucket "$b" \ + --create-bucket-configuration LocationConstraint="$REGION" \ + --region "$REGION" >/dev/null + fi +} + +ensure_log_group() { + local lg="$1" + awslocal logs create-log-group --log-group-name "$lg" --region "$REGION" >/dev/null 2>&1 || true +} + +ensure_secret_string() { + local name="$1" + local value="$2" + + if awslocal secretsmanager describe-secret --secret-id "$name" >/dev/null 2>&1; then + awslocal secretsmanager update-secret --secret-id "$name" --secret-string "$value" >/dev/null + else + awslocal secretsmanager create-secret --name "$name" --secret-string "$value" >/dev/null + fi +} + +ensure_secret_file() { + local name="$1" + local filepath="$2" + + if awslocal secretsmanager describe-secret --secret-id "$name" >/dev/null 2>&1; then + awslocal secretsmanager update-secret --secret-id "$name" --secret-string "file://$filepath" >/dev/null + else + awslocal secretsmanager create-secret --name "$name" --secret-string "file://$filepath" >/dev/null + fi +} + +# SES identities (idempotent-ish; ignoring if it already exists) +awslocal ses verify-domain-identity --domain imex.online --region "$REGION" >/dev/null || true +awslocal ses verify-email-identity --email-address noreply@imex.online --region "$REGION" >/dev/null || true + +# Secrets +ensure_secret_file "CHATTER_PRIVATE_KEY" "/tmp/certs/io-ftp-test.key" +ensure_secret_string "CHATTER_COMPANY_KEY_6713" "${CHATTER_COMPANY_KEY_6713:-REPLACE_ME}" + +# Logs +ensure_log_group "development" + +# Buckets +ensure_bucket "imex-job-totals" +ensure_bucket "parts-estimate" +ensure_bucket "imex-large-log" +ensure_bucket "imex-carfax-uploads" +ensure_bucket "rome-carfax-uploads" +ensure_bucket "rps-carfax-uploads" diff --git a/server.js b/server.js index 099ae3562..07901ab6e 100644 --- a/server.js +++ b/server.js @@ -125,6 +125,7 @@ const applyRoutes = ({ app }) => { app.use("/payroll", require("./server/routes/payrollRoutes")); app.use("/sso", require("./server/routes/ssoRoutes")); app.use("/integrations", require("./server/routes/intergrationRoutes")); + app.use("/chatter", require("./server/routes/chatterRoutes")); // Default route for forbidden access app.get("/", (req, res) => { diff --git a/server/chatter/createLocation.js b/server/chatter/createLocation.js new file mode 100644 index 000000000..2bd85f95f --- /dev/null +++ b/server/chatter/createLocation.js @@ -0,0 +1,22 @@ +const DEFAULT_COMPANY_ID = process.env.CHATTER_DEFAULT_COMPANY_ID; + +const createLocation = (req, res) => { + const { logger } = req; + const { bodyshopID } = req.body; + + // No Default company + if (!DEFAULT_COMPANY_ID) { + logger.log("chatter-create-location-no-default-company", "warn", null, null, { bodyshopID }); + return res.json({ success: false }); + } + + // No Bodyshop data available + if (!bodyshopID) { + logger.log("chatter-create-location-invalid-bodyshop", "warn", null, null, { bodyshopID }); + return res.json({ success: false }); + } + + return res.json({ success: true }); +}; + +module.exports = createLocation; diff --git a/server/graphql-client/queries.js b/server/graphql-client/queries.js index 54714f723..e6576b080 100644 --- a/server/graphql-client/queries.js +++ b/server/graphql-client/queries.js @@ -1911,10 +1911,19 @@ exports.GET_AUTOHOUSE_SHOPS = `query GET_AUTOHOUSE_SHOPS { }`; exports.GET_CHATTER_SHOPS = `query GET_CHATTER_SHOPS { - bodyshops(where: {chatterid: {_is_null: false}, _or: {chatterid: {_neq: ""}}}){ + bodyshops( + where: { + chatterid: { _is_null: false, _neq: "" } + _or: [ + { chatter_company_id: { _is_null: true } } + { chatter_company_id: { _eq: "" } } + ] + } + ) { id shopname chatterid + chatter_company_id imexshopid timezone } diff --git a/server/routes/chatterRoutes.js b/server/routes/chatterRoutes.js new file mode 100644 index 000000000..52675bc9b --- /dev/null +++ b/server/routes/chatterRoutes.js @@ -0,0 +1,12 @@ +const express = require("express"); +const createLocation = require("../chatter/createLocation"); +const router = express.Router(); +const validateFirebaseIdTokenMiddleware = require("../middleware/validateFirebaseIdTokenMiddleware"); +const validateAdminMiddleware = require("../middleware/validateAdminMiddleware"); + +router.use(validateFirebaseIdTokenMiddleware); +router.use(validateAdminMiddleware); + +router.post("/create-location", createLocation); + +module.exports = router;