README
promoted-ts-client
A Typescript Client to contact Promoted APIs. This is primarily intended to be used when logging Request
s and Insertion
s on a Node.js server.
Client logging libraries:
- promoted-ts-client - for logging
Request
s andInsertion
s from your server. - promoted-snowplow-logger - for logging events from a browser.
- ios-metrics-sdk - for iOS logging.
Creating a PromotedClient
We recommend creating a PromotedClient in a separate file so it can be reused.
PromotedClient avoids having direct dependencies so customer's have more options for customization and can keep dependencies smaller.
promotedClient.js
import { logOnError, newPromotedClient, throwOnError } from 'promoted-ts-client';
import { v5 as uuid } from 'uuid';
import axios from 'axios';
// Client can choose their preferred RPC client.
const axiosApiClient = <Req, Res>(url: string, apiKey: string, timeout: number) => (request: Req): Promise<Res> =>
axios.post(
url,
request,
{
headers: {
"x-api-key": apiKey,
},
timeout,
});
// These values will vary depending on dev vs prod.
const deliveryApi = 'https://....com/...';
const deliveryApiKey = 'AbCSomeRLongString1';
const deliveryTimeoutMillis = 250;
const metricsApi = 'https://....com/...';
const metricsApiKey = 'AbCSomeRLongString2';
const metricsTimeoutMillis = 3000;
// NextJS example. For errors, if inDev then throw else log.
const throwError =
process?.env?.NODE_ENV !== 'production' ||
(typeof location !== "undefined" && location?.hostname === "localhost");
export const promotedClient = newPromotedClient({
// TODO - Customize handleError for your server.
// When developing using Node.js, throwOnError will give a scary unhandled promise warning.
handleError: throwError ? throwOnError : logOnError;
deliveryClient: axiosApiClient(deliveryApi, deliveryApiKey, deliveryTimeoutMillis),
metricsClient: axiosApiClient(metricsApi, metricsApiKey, metricsTimeoutMillis),
uuid,
deliveryTimeoutMillis,
metricsTimeoutMillis,
});
Calling our Delivery API
Let's say the previous code looks like this:
static async getProducts(req: any, res: Response) {
const products = ...; // Logic to get products from DB, apply filtering, etc.
sendSuccessToClient(res, { products });
}
We would modify to something like this:
static async getProducts(req: any, res: Response) {
const products = ...;
const response = await promotedClient.deliver({
request: {
userInfo: {
logUserId: req.logUserId,
},
useCase: 'FEED',
sessionId: req.sessionId,
viewId: req.viewId,
},
fullInsertions: products.map(product => ({
// Must be filled in.
contentId: product.id,
// You can set custom properties here.
properties: {
struct: {
product,
},
},
})),
});
// Change the response Product list to use the values in the returned Insertions.
sendSuccessToClient(res, {
products: response.insertion.map(insertion => insertion.properties.struct.product),
});
await response.log();
}
There are other optional options.
Argument | Type | Default Value | Description |
---|---|---|---|
onlyLog |
boolean |
false |
Can be used to conditionally disable deliver per request |
toCompactDeliveryInsertion |
Insertion => Insertion |
Returns the argument | Can be used to strip out fields being passed into Delivery API |
toCompactMetricsInsertion |
Insertion => Insertion |
Returns the argument | Can be used to strip out fields being passed into Metrics API |
Logging only
There are two ways of doing this with PromotedClient
:
- You can use
deliver
but add aonlyLog: true
property. - You can use
prepareForLogging
method call instead. TheprepareForLogging
signature is similar todeliver
and should be integrated the same way.
Pagination
When calling
deliver
, we expect that you will pass an unpaged (complete) list of insertions, and the SDK assumes this to be the case. To help you catch this scenario, the SDK will call handleError in the pre-paged case if performChecks is turned on.When calling
prepareForLogging
with shadow traffic turned on, we also expect an unpaged list of insertions, since in this case we are simulating delivery.When calling
prepareForLogging
otherwise, you may choose to pass "pre-paged" or "unpaged" insertions based on theinsertionPageType
field on theMetricsRequest
.- When
insertionPageType
is "unpaged", theRequest.paging.offset
andRequest.paging.size
parameters are used to log a "window" of insertions. - When
insertionPageType
is "pre-paged", the SDK will not handle pagination of the insertions that are part of the resulting lot request.
- When
Position
- Do not set the insertion
position
field in client code. The SDK and Delivery API will set it whendeliver
is called. - When logging only via
prepareForLogging
, theposition
field is not set by the SDK on the log request insertions.
The prepareForLogging
call assumes the client has already handled pagination. It needs a Request.paging.offset
to be passed in for the number of items deep that the page is.
Improving this library
Tech used
Uses
- TypeScript support
- React support
- ESLint (with React and Prettier)
- Unit tests (Jest and Testing Library)
- Minified output with Terser
- Bundle size validation with size-limit
- Flexible builds with Rollup
- CHANGELOG.md template
Scripts
- Run most commands:
npm run finish
- Build the project:
npm run build
- Validate output bundle size with
npm run size
- Validate output bundle size with
- Lint the project:
npm run lint
- Run unit tests:
npm test
ornpm test
When developing locally
If you want to test local changes in an actual deployment, use npm link
.
- Make sure your npm uses the same version as the client directory.
- Run
npm run updatelink
. - Go to client directory and run
npm link promoted-ts-client
.
When you update promoted-ts-client
, run npm run updatelink
.
When you want to undo, run npm run unlink
in this directory and npm unlink promoted-ts-client
in the client directory.
Deploy
Based on the anticipated semantic-version, update the SERVER_VERSION
constant in client.ts
.
We use a GitHub action that runs semantic-release to determine how to update versions. Just do a normal code review and this should work. Depending on the message prefixes (e.g. feat:
, fix:
, clean:
, docs:
), it'll update the version appropriately.
Resources
The base of this repository is a combination of the following repos: