fix(ms-bing-capi): item price should be number not integer - #3859
fix(ms-bing-capi): item price should be number not integer#3859scottlepich-lz wants to merge 6 commits into
Conversation
The `items` field defined per-item `price` with `type: 'integer'`, which is too strict. Microsoft's UET Conversions API accepts DECIMAL item prices in whole currency units — their official docs show the `price` parameter example as 25.1 and every JSON sample uses decimals such as "price": 25.1 and "price": 27.3 (https://learn.microsoft.com/en-us/advertising/guides/uet-conversion-api-integration). With `type: 'integer'`, Segment's mapping-kit rejected valid decimal prices (e.g. 9.99) with "400: Item Price must be an integer but it was a number", dropping the entire event. Changing the type to `number` allows decimal prices through. `quantity` remains an integer. generated-types.ts is unchanged since both integer and number map to the TypeScript type `number`. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
Note
Copilot couldn't run its full agentic review because no GitHub Actions runner was available. Make sure your repository has a runner available to run Copilot's review, or add a copilot-setup-steps.yml file specifying one with the runs-on attribute. See the docs for more details.
Updates the Microsoft Bing CAPI destination schema to allow decimal item prices and adds a regression test to ensure decimal prices pass mapping validation and are forwarded to Bing.
Changes:
- Relaxed
items[].pricevalidation fromintegertonumberin the action field schema. - Added a unit test confirming a decimal item price (
9.99) is accepted and appears in the outgoing request payload.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
| packages/destination-actions/src/destinations/ms-bing-capi/sendEvent/fields.ts | Loosens schema validation for item price to accept decimals. |
| packages/destination-actions/src/destinations/ms-bing-capi/sendEvent/tests/sendEvent.test.ts | Adds regression coverage for decimal item prices being forwarded successfully. |
| .post(`/v1/${settings.UetTag}/events`, (body: any) => { | ||
| const items = body.data[0].customData.items | ||
| expect(items).toHaveLength(1) | ||
| expect(items[0].price).toBe(9.99) |
| .post(`/v1/${settings.UetTag}/events`, (body: any) => { | ||
| const items = body.data[0].customData.items | ||
| expect(items).toHaveLength(1) | ||
| expect(items[0].price).toBe(9.99) |
| .post(`/v1/${settings.UetTag}/events`, (body: any) => { | ||
| const items = body.data[0].customData.items | ||
| expect(items).toHaveLength(1) | ||
| expect(items[0].price).toBe(9.99) |
| price: { | ||
| label: 'Item Price', | ||
| description: 'The price of the item, after discounts.', | ||
| type: 'integer' | ||
| type: 'number' | ||
| }, |
The sendEvent action sends events with `continueOnValidationError: true`, so Microsoft's CAPI accepts an event (HTTP 200, `eventsReceived: 1`) even when it has non-fatal issues, and reports those issues as entries in `error.details[]` flagged with `"isWarning": true`. The batch response handler (`performBatch`) matched `error.details[]` by `index` only and treated ANY matching detail as a hard failure. As a result, a batched event whose only detail was a warning was marked `status: 400` and reported as a failed delivery, even though Microsoft had accepted it. This caused real, silent delivery failures for any batched event that triggered a Microsoft warning. (Single `perform` returns the raw 200, so the bug only manifested in batching.) Fix: only treat a detail as a failure when it is NOT a warning (`detail.index === index && !detail.isWarning`). A warning-only event is now marked SUCCESS (200); an event with a real (non-warning) error at its index is still marked 400. `types.ts` is extended to model the `isWarning` and `errorCode` fields the API actually returns. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The sendEvent `items.price` field type was changed from `integer` to `number` in fields.ts, but metadata.json was not regenerated. Running `yarn generate:metadata-payload` updates the Item Price field type to `number`, which fixes the failing "Assert metadata payloads are up-to-date" CI check. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 5 out of 5 changed files in this pull request and generated no new comments.
Comments suppressed due to low confidence (2)
packages/destination-actions/src/destinations/ms-bing-capi/sendEvent/index.ts:75
- The PR title/summary focuses on changing
items[].pricefrom integer -> number, but this change also modifies batch error semantics by treatingisWarning: truedetails as success. That’s a behavioral change worth explicitly documenting in the PR description (or splitting into a separate PR) so reviewers and release notes capture the expanded scope.
const error = details.find((detail) => detail.index === index && !detail.isWarning)
packages/destination-actions/src/destinations/ms-bing-capi/sendEvent/index.ts:75
- This change drops warning-only details entirely from per-index results. While that avoids incorrectly failing accepted events, it also removes potentially useful diagnostics (e.g., partial field acceptance). Consider surfacing warning details somewhere non-fatal (e.g., attach warnings to the success response payload if supported, or emit debug-level logs/metrics) so operators can detect degraded payload quality without event drops.
const error = details.find((detail) => detail.index === index && !detail.isWarning)
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 5 out of 5 changed files in this pull request and generated no new comments.
Suppressed comments (2)
packages/destination-actions/src/destinations/ms-bing-capi/sendEvent/index.ts:75
- The PR title/description focus on changing
items[].pricefromintegertonumber, but this change also alters batch error handling by ignoringisWarning: truedetails. Please either (a) update the PR title/description to explicitly include the warning-handling behavior change, or (b) split this into a separate PR to keep scope aligned.
const error = details.find((detail) => detail.index === index && !detail.isWarning)
packages/destination-actions/src/destinations/ms-bing-capi/sendEvent/index.ts:75
- Using
!detail.isWarningrelies on truthiness and is a bit opaque givenisWarningis optional. For clarity (and to preserve the current behavior whereundefinedis treated as a real error), consider making the intent explicit with a comparison likedetail.isWarning !== true.
const error = details.find((detail) => detail.index === index && !detail.isWarning)
Summary
The Microsoft Bing CAPI destination's
itemsInputField defines the per-itempriceproperty withtype: 'integer'. This is too strict: Microsoft's own UET Conversions API accepts decimal item prices in whole currency units.Because of this, Segment's mapping-kit rejects perfectly valid decimal prices (e.g.
9.99) with:Since mapping validation fails, the entire event is dropped — not just the price field. Any storefront selling at non-round prices (which is essentially all of them) silently loses conversion events.
Evidence (Microsoft docs)
Microsoft's official UET Conversions API integration guide documents
priceas a decimal:priceexample value as25.1(a decimal)."price": 25.1and"price": 27.3.ecommTotalValueis typednumber($double).So
integeris incorrect; the correct type isnumber.Fix
Change the
items[].pricefield fromtype: 'integer'totype: 'number'.quantityis left asinteger(correct for quantity). Nothing else is changed.generated-types.tsis unchanged, because bothintegerandnumbermap to the TypeScript typenumber. Codegen (generate:types) was run for the destination and produced no diff.Testing
sendEvent/__tests__/sendEvent.test.tsproving a decimal item price (9.99) is now accepted and forwarded to the request payload without a validation error.type: 'integer'with the exact errorItem Price must be an integer but it was a number, and passes withtype: 'number'.jest src/destinations/ms-bing-capi).Follow-up observation (out of scope, not changed here)
While in this file I noticed a separate latent bug: the
itemsfield'sdefaultmapping writes to keyitem_price, but the defined property isprice. Since the field isadditionalProperties: false,item_priceis silently dropped, so the default mapping never sends a price at all. I've deliberately left this out of this PR to keep it focused on the type fix; flagging it as a follow-up.🤖 Generated with Claude Code