feat: add @adobe/spacecat-shared-drs-client package (LLMO-1819)#1398
feat: add @adobe/spacecat-shared-drs-client package (LLMO-1819)#1398
Conversation
Shared HTTP client for the Data Retrieval Service (DRS) API, enabling both spacecat-api-service and spacecat-audit-worker to interact with DRS for prompt generation and brand presence re-analysis. Methods: createFrom(), isConfigured(), submitJob(), submitPromptGenerationJob(), triggerBrandDetection(), getJob() 17 unit tests, 100% coverage. Made-with: Cursor
cdf8056 to
5367f0f
Compare
|
This PR will trigger a minor release when merged. |
…ex (LLMO-1819) - Send `brand` instead of `brand_name` (DRS validates `parameters.brand`) - Default numPrompts to 50 instead of 42 - Guard regex against null apiBaseUrl to resolve CodeQL polynomial regex alert Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
|
There's a parallel effort on #1382 that also introduces a DRS client package. The two PR's have different use cases for DRS but create the same package. Maybe the two PRs can find a common ground? @rarescheseli |
|
Thanks @AndreiAlexandruParaschiv! I was not aware of that. However @rarescheseli seems off today and I need this right away. So I propose that after I merge, @rarescheseli can add all the other functionalities that is needed afterwards. |
danieljchuser
left a comment
There was a problem hiding this comment.
PR Review: @adobe/spacecat-shared-drs-client
Overall: Clean, well-structured new package that follows established monorepo patterns. 17 tests with 100% coverage thresholds. A few issues to address:
Issues
1. MEDIUM — Missing context caching in createFrom()
src/index.js:26-27 — The BrandClient (and other shared clients) cache the newly created instance back onto context so subsequent calls reuse it:
// BrandClient pattern:
const client = new BrandClient({ apiBaseUrl, apiKey }, log);
context.brandClient = client; // <-- caches it
return client;DrsClient checks for a cached instance but never caches a newly created one:
if (context.drsClient) return context.drsClient;
return new DrsClient({ apiBaseUrl, apiKey }, log); // not cachedThis means every call to createFrom() creates a new instance unless the caller pre-populates context.drsClient manually. Suggest adding context.drsClient = client; before returning, consistent with BrandClient.
2. LOW — CodeQL polynomial regex alert still active
src/index.js:31 — The second commit says it "guards regex against null apiBaseUrl to resolve CodeQL polynomial regex alert", but both CodeQL bot comments are still present. The regex /\/+$/ is technically fine (not truly polynomial), but CodeQL flags it. A non-regex alternative would silence it:
let url = apiBaseUrl || '';
while (url.endsWith('/')) url = url.slice(0, -1);
this.apiBaseUrl = url || undefined;Or simply mark it as a false positive in CodeQL if the team prefers.
3. LOW — model: 'gpt-5-nano' hardcoded
src/index.js:117 — The model name is hardcoded in submitPromptGenerationJob. If DRS supports different models, this should probably be a parameter (with a default). If it's intentionally fixed, a comment explaining why would help future readers.
4. LOW — TypeScript declaration inconsistency
src/index.d.ts:44 — triggerBrandDetection returns Promise<unknown> while other methods return typed results (Promise<DrsJobResult>, Promise<Record<string, unknown>>). Consider Promise<Record<string, unknown>> or a dedicated interface for consistency.
5. NITS
package.json— Missingbugsandhomepagefields that other packages (e.g. brand-client) include. Very minor but keeps things consistent.src/index.js:82—submitJobaccessesresult.job_idunconditionally in the log. If the response doesn't containjob_id(e.g., unexpected response shape), this would logundefined. A guard or optional chain would be safer.
Cross-Repository Impact Assessment
| Specialist | Impact | Notes |
|---|---|---|
| API (api-service) | LOW — Additive | New dependency to adopt. Dependent PR exists. No breaking changes to existing contracts. |
| AUDIT (audit-worker) | LOW — Additive | New dependency to adopt. Dependent PR exists. No breaking changes. |
| INFRA (infrastructure) | NONE | No new queues, buckets, or IAM changes needed. DRS is an external HTTP service. |
| All others | NONE | Pure addition, no impact on existing packages or contracts. |
Positive Notes
- Follows existing monorepo conventions well (
.nycrc.json,.releaserc.cjs,.mocha-multi.json, test setup) - Good use of
tracingFetchfromspacecat-shared-utilsfor observability - Clean separation: generic
submitJobfor extensibility + specializedsubmitPromptGenerationJob - 100% coverage thresholds enforced
- Non-JSON response handling is a nice touch (204 responses)
Verdict
Approve with minor suggestions — The missing context caching (#1) is the most impactful item. The rest are low/nit-level. No breaking changes to any existing cross-repo contracts.
1. Cache newly created client on context (consistent with BrandClient) 2. Replace regex with while loop for trailing slash removal (CodeQL) 3. Add comment explaining hardcoded model name 4. Fix TypeScript return type for triggerBrandDetection 5. Add bugs/homepage to package.json, guard result.job_id access Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
|
🎉 This PR is included in version @adobe/spacecat-shared-drs-client-v1.0.0 🎉 The release is available on: Your semantic-release bot 📦🚀 |
…819) (#1892) ## Summary - Replace local `src/support/drs-client.js` with `@adobe/spacecat-shared-drs-client` - Update `llmo-onboarding.js` to use `DrsClient.createFrom(context)` (class pattern) - Delete local client and its tests (now covered by the shared package) ## Dependencies - **Blocked by**: [spacecat-shared#1398](adobe/spacecat-shared#1398) — shared package must be published first - Add `@adobe/spacecat-shared-drs-client` to `package.json` after it's published ## Test plan - [x] Updated esmock mocks for class-based `createFrom()` pattern - [ ] CI will fail on `import/no-unresolved` until shared package is published - [ ] E2E: verify LLMO onboarding still triggers DRS prompt generation Made with [Cursor](https://cursor.com) --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…2061) ## Summary - Remove dead `triggerMystiqueCategorization` code (DRS config writer already populates categories via LLMO-1819) - Replace `triggerGeoBrandPresence` / `triggerGeoBrandPresenceRefresh` calls in `llmo-customer-analysis` with `DrsClient.triggerBrandDetection()` for config-change flows - Remove `isAdobe` guard — DRS now handles all sites uniformly (Adobe and external) - Keep old Mystique trigger functions for backward compatibility (manual Slack triggers) ## What changed in llmo-customer-analysis | Trigger | Before | After | |---|---|---| | First-time onboarding (categorization) | Mystique API call | Removed (DRS already writes categories) | | No config version (first setup) | `triggerGeoBrandPresence` → Mystique | `triggerBrandDetection` → DRS | | Topics/categories/entities change | `triggerGeoBrandPresence` → Mystique (non-Adobe only) | `triggerBrandDetection` → DRS (all sites) | | Brands/competitors change | `triggerGeoBrandPresenceRefresh` → Mystique (non-Adobe only) | `triggerBrandDetection` → DRS (all sites) | ## Dependencies - **Blocked by**: [spacecat-shared#1398](adobe/spacecat-shared#1398) — shared DRS client package - Add `@adobe/spacecat-shared-drs-client` to `package.json` after it's published - **Infrastructure**: `DRS_API_URL` and `DRS_API_KEY` env vars must be added to audit-worker deployment (spacecat-infrastructure) ## Test plan - [ ] CI will fail on `import/no-unresolved` until shared package is published - [ ] Update unit tests for `llmo-customer-analysis` handler - [ ] E2E: verify config changes trigger DRS brand detection instead of Mystique Made with [Cursor](https://cursor.com) --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Summary
@adobe/spacecat-shared-drs-clientproviding an HTTP client for the Data Retrieval Service (DRS) APIspacecat-api-serviceandspacecat-audit-workerto interact with DRS for prompt generation and brand presence re-analysiscreateFrom(),isConfigured(),submitJob(),submitPromptGenerationJob(),triggerBrandDetection(),getJob()Context
As part of the migration from Mystique to DRS for brand presence execution, both api-service (onboarding) and audit-worker (config-change triggers) need to call DRS. The client was previously a local file in api-service only.
Dependent PRs
drs-client.jsto this shared packageTest plan
Made with Cursor