Skip to content

PM-6363: Salesforce opportunity lookup and Salesforce-aligned SMU options - #45

Merged
jmgasper merged 1 commit into
devfrom
PM-6363
Sep 17, 2026
Merged

jmgasper merged 1 commit into
devfrom
PM-6363

Conversation

@jmgasper

@jmgasper jmgasper commented Sep 17, 2026 •

Copy link
Copy Markdown
Contributor

Implements the projects-api-v6 half of PM-6363.

What this adds

GET /v6/projects/salesforce/opportunities/:opportunityId — a read-only lookup returning the opportunity name, description, Subcontracting End Customer, Reporting SMU, Close Date, stage and a link to the record. The Work app uses it to populate project details from an opportunity; the Sales app uses it for the opportunity description popup.

{
  "id": "006UN00000XamntYAB",
  "name": "EMEA - Amazon Web Services - PS BFSI",
  "description": "Next AWS MVP/POC (Shabam Sponsor)",
  "customer": "Novartis Pharmaceuticals",
  "smu": "AMR1",
  "reportingSmu": "AMR1",
  "closeDate": "2026-07-31",
  "stageName": "Prospecting",
  "url": "https://topcoder.my.salesforce.com/006UN00000XamntYAB"
}

SalesforceClient is modelled on the reports-api-v6 Salesforce client, as requested:

  • OAuth client-credentials flow, credentials and tokens stay in server memory.
  • Trusted-origin allow-list (*.my.salesforce.com, login/test.salesforce.com) checked before credentials or tokens are sent anywhere.
  • Bounded retries with backoff on network errors, 429 and 5xx; one session renewal on 401; sanitized errors only.
  • Record ids are validated against ^006[a-zA-Z0-9]{12}([a-zA-Z0-9]{3})?$ before they reach the SOQL literal, so the id cannot terminate the quoted string.

Access requires a manager-tier role (Work app editors) or a talent-manager role (Sales app viewers).

SMU rename

The ticket asks to rename the SMU options "to follow the same naming pattern in all places". The dev Salesforce org emits APME, EURP, AMR1, AMR2 (and INTERNAL) for Opportunity.Reporting_SMU__c, so the options now use those exact codes and an imported opportunity maps onto a project with no translation. The ticket text lists "AMPE" and "AM2"; those are treated as typos of APME and AMR2, confirmed with the reporter.

Backward compatibility: projects saved as APMEA/Europe/Americas1/Americas2 are accepted and upgraded in place on the next write, so no data migration is needed and existing showcase saves keep working. An unrecognized Reporting SMU (for example INTERNAL) is surfaced as Others with the raw value in smuOther rather than being dropped.

Project.details.salesforceOpportunityId is validated alongside the other shared metadata.

Configuration

Four new optional env vars, documented in the README:

Variable Default
SALESFORCE_API_CONSUMER_KEY -
SALESFORCE_API_CONSUMER_SECRET -
SALESFORCE_LOGIN_URL https://topcoder.my.salesforce.com
SALESFORCE_REST_API_VERSION 65.0

These are separate from the existing JWT-bearer SALESFORCE_CLIENT_* variables used by BillingAccountService; both integrations coexist. Without them the endpoint returns 503 and nothing else changes.

Testing

  • 32 new unit tests (client, service, controller, shared metadata utils). Full suite: 431 passed, same 15 pre-existing failures as origin/dev (JWT and metadata specs that need a database).
  • Verified live against the dev Salesforce org with the provided credentials: an opportunity with a Subcontracting End Customer, one with Reporting_SMU__c = AMR1, one with INTERNAL (mapped to Others), and an unknown id (404).
  • tsc --noEmit and eslint clean.

🤖 Generated with Claude Code

…ions

Adds a read-only Salesforce opportunity endpoint that the Work app uses to
populate project details and the Sales app uses for its opportunity popup:

- GET /v6/projects/salesforce/opportunities/:opportunityId returns the
  opportunity name, description, Subcontracting End Customer, Reporting SMU,
  Close Date, stage and a link to the record.
- SalesforceClient runs SOQL over the OAuth client-credentials flow, mirroring
  the Reports API client: trusted-origin allow-list, bounded retries, in-memory
  token reuse and a single renewal on 401. Record ids are validated against a
  strict pattern before they reach the query.
- Access requires a manager-tier role (Work) or a talent-manager role (Sales).

Renames the SMU options to the Salesforce Reporting SMU codes (APME, EURP,
AMR1, AMR2) so an imported opportunity maps onto a project without translation.
Projects saved with the previous labels are upgraded in place on the next write,
and an unrecognized Reporting SMU is surfaced as Others plus the raw value.
Project.details.salesforceOpportunityId is validated alongside the other shared
metadata.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@jmgasper
jmgasper merged commit 36d97f0 into dev Sep 17, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant