Fix: Shopify subpath imports, enhance docs (#755)
🚀 Publish Trigger.dev Docker / typecheck (push) Failing after 0s
🚀 Publish Trigger.dev Docker / units (push) Failing after 0s
🚀 Publish Trigger.dev Docker / e2e (push) Failing after 0s
🚀 Publish Trigger.dev Docker / publish (push) Has been skipped

* Fix invalid subpath imports

* Remove @trigger.dev/tsup dep

* Update lockfile

* Small github docs fix

* Pull runtime adaptor import out of integration

* Fix io.store docs examples

* Add sendEvents to task library

* Add KV to task library and small fixes

* Suppress duplicate http endpoint warnings

* Changeset

* Add task returns
This commit is contained in:
nicktrn
2023-11-27 16:03:03 +00:00
committed by GitHub
parent 5d28f1a3d6
commit 096151c014
22 changed files with 274 additions and 67 deletions
+6
View File
@@ -0,0 +1,6 @@
---
"@trigger.dev/shopify": patch
"@trigger.dev/sdk": patch
---
Fix `@trigger.dev/shopify` imports, enhance docs, and suppress HTTP Endpoint warnings
@@ -184,6 +184,28 @@ We support the following log levels:
[reference docs](/sdk/io/logger)
## `store`
The store object exposes several namespaced **Key-Value Stores** you can access inside of your Jobs. This is useful for storing small amounts of serializable data for later retrieval:
```ts
// store some data
await io.store.job.set("💾", "disk-A", "Doom 1.2 Demo #3");
// get it again later
const value = await io.store.job.get<string>("read-💾", "disk-A");
```
If you want to access the store from outside a run (e.g. just from your backend) you should use [client.store](/sdk/triggerclient/store) instead.
The following namespaces are at your disposal:
- `store.env` to access and store data within the **Environment**
- `store.job` to access and store data within the **Job**
- `store.run` to access and store data within the **Run**
[reference docs](/sdk/io/store)
## `random`
Use this task to generate a random number that stays stable during run retries/resumes:
@@ -215,6 +237,33 @@ await io.sendEvent("🚚", {
[reference docs](/sdk/io/sendevent)
## `sendEvents`
This task allows you to send multiple events from inside your job run.
If you want to send multiple events from outside a run (e.g. just from your backend) you should use [client.sendEvents()](/sdk/triggerclient/instancemethods/sendevents) instead.
```ts
await io.sendEvents("🚚🚚", [
{
id: "e_12345",
name: "new.user",
payload: {
userId: "u_12345",
},
},
{
id: "e_67890",
name: "new.user",
payload: {
userId: "u_67890",
},
},
]);
```
[reference docs](/sdk/io/sendevents)
## `getEvent`
This task allows you to get an event by ID from inside your job run.
+70 -1
View File
@@ -28,12 +28,60 @@ await io.shopify.rest.<Resource>.<method>("cacheKey", params)
Fetch all resources of a given type.
```ts
await io.shopify.rest.Variant.all("get-all-variants", {,
await io.shopify.rest.Variant.all("get-all-variants", {
autoPaginate: true, // Pagination helper, disabled by default
product_id: 123456 // Optional, resource-specific parameter
})
```
**Returns**
<ResponseField name="data" type="array" required>
An array of Shopify resources.
</ResponseField>
<ResponseField name="pageInfo" type="PageInfo">
The `PageInfo` object. Will be `undefined` if there are no further pages.
<Expandable title="properties">
<ResponseField name="limit" type="string" required>
The maximum number of results shown per page.
</ResponseField>
<ResponseField name="fields" type="string[]">
An array of resource fields to show in the results.
</ResponseField>
<ResponseField name="previousPageUrl" type="string">
The optional URL of the previous page.
</ResponseField>
<ResponseField name="nextPageUrl" type="string">
The optional URL of the next page.
</ResponseField>
<ResponseField name="prevPage" type="PageInfoParams">
The optional `PageInfoParams` object of the previous page.
<Expandable title="properties" defaultOpen>
<ResponseField name="path" type="string" required>
The path of the previous page.
</ResponseField>
<ResponseField name="query" type="object" required>
The query parameters of the previous page.
</ResponseField>
</Expandable>
</ResponseField>
<ResponseField name="nextPage" type="PageInfoParams">
The optional `PageInfoParams` object of the next page.
<Expandable title="properties" defaultOpen>
<ResponseField name="path" type="string" required>
The path of the next page.
</ResponseField>
<ResponseField name="query" type="object" required>
The query parameters of the next page.
</ResponseField>
</Expandable>
</ResponseField>
</Expandable>
</ResponseField>
### `count()`
Fetch the number of resources of a given type.
@@ -44,6 +92,12 @@ await io.shopify.rest.Product.count("count-products", {
})
```
**Returns**
<ResponseField name="count" type="number" required>
The number of resources.
</ResponseField>
### `find()`
Fetch a single resource by its ID.
@@ -54,6 +108,10 @@ await io.shopify.rest.Product.find("find-product", {
})
```
**Returns**
A `Promise` that resolves to the Shopify resource.
### `save()`
Create or update a resource of a given type. The resource will be created if no ID is specified.
@@ -75,6 +133,10 @@ await io.shopify.rest.Product.save("update-product", {
})
```
**Returns**
A `Promise` that resolves to the Shopify resource.
### `delete()`
Delete an existing resource.
@@ -85,6 +147,10 @@ await io.shopify.rest.Product.delete("delete-product", {
})
```
**Returns**
A `Promise` that resolves to `undefined` when the resource has been deleted. Throws an error otherwise.
## Resources
This is a list of REST Resources that can be used directly as Tasks. They all implement the same methods described above. For resources with non-standard methods, you will have to use the raw Shopify API Client instead - please see the end of this page for further instructions.
@@ -186,6 +252,9 @@ client.defineJob({
You can access the [Shopify API Client instance](https://github.com/Shopify/shopify-api-js/blob/ff0900cd383712e6362b6dd3370d8ab11caabd3d/packages/shopify-api/docs/reference/shopifyApi.md) by using the `runTask` method on the integration:
```ts
import "@shopify/shopify-api/adapters/node";
import { Shopify } from "@trigger.dev/shopify";
const shopify = new Shopify({
id: "shopify",
});
+29
View File
@@ -35,6 +35,34 @@ yarn add @trigger.dev/shopify@latest
</CodeGroup>
## Runtime Adapter
It's **required** to import the correct Runtime Adapter for your platform. All examples will use the Node.js adapter, but you can change this to any of the following or even create your own [custom adapter](https://github.com/Shopify/shopify-api-js/blob/6adccd72527dc1fcf387adf09c5522010b6f31f2/packages/shopify-api/docs/guides/runtimes.md).
<Warning>Only import **one** adapter!</Warning>
```ts
// Import the Node.js adapter
import '@shopify/shopify-api/adapters/node';
// Import the CloudFlare Worker adapter
import '@shopify/shopify-api/adapters/cf-worker';
// Import the generic Web API adapter
import '@shopify/shopify-api/adapters/web-api';
```
You can then import and use `@trigger.dev/shopify` like any other integration:
```ts
import "@shopify/shopify-api/adapters/node";
import { Shopify } from "@trigger.dev/shopify";
const shopify = new Shopify({
...
});
```
## Authentication
You can use Personal Access Tokens to authenticate with Shopify and get started with building custom apps.
@@ -48,6 +76,7 @@ The [required scopes](https://shopify.dev/docs/api/usage/access-scopes#authentic
Additionally, you will also have to provide your shop domain.
```ts my-job.ts
import "@shopify/shopify-api/adapters/node";
import { Shopify } from "@trigger.dev/shopify";
//create Shopify client using a token
+6 -1
View File
@@ -323,6 +323,12 @@
"pages": [
"sdk/triggerclient/overview",
"sdk/triggerclient/constructor",
{
"group": "Instance properties",
"pages": [
"sdk/triggerclient/store"
]
},
{
"group": "Instance methods",
"pages": [
@@ -331,7 +337,6 @@
"sdk/triggerclient/instancemethods/getevent",
"sdk/triggerclient/instancemethods/cancel-event",
"sdk/triggerclient/instancemethods/cancel-runs-for-event",
"sdk/triggerclient/store",
"sdk/triggerclient/instancemethods/getruns",
"sdk/triggerclient/instancemethods/getrun",
"sdk/triggerclient/instancemethods/define-job",
+61 -13
View File
@@ -5,7 +5,7 @@ description: "Exposes namespaced **Key-Value Stores** you can access inside of y
---
<Warning>
Only use this for small values - there's a **256KB** per-item size limit.
Only use this for small values - there's a **256KB** size limit per item.
</Warning>
## Namespaces
@@ -14,6 +14,54 @@ description: "Exposes namespaced **Key-Value Stores** you can access inside of y
- `store.job` to access and store data within the **Job**
- `store.run` to access and store data within the **Run**
You will only be able to access Run-scoped data from inside the _same_ Run when using `store.run`.
To share data across Runs you can use `store.job`:
```ts
client.defineJob({
...
run: async (payload, io, ctx) => {
// Run-scoped get - this will always return undefined
await io.store.run.get("run-get", "counter")
// Job-scoped get - this will only be undefined on the first run
const counter = await io.store.job.get<number | undefined>("job-get", "counter")
const currentCount = counter ?? 0
const incrementedCounter = currentCount++
// Run-scoped set - somewhat pointless as we don't access it again in this Run
await io.store.run.set("run-set", "counter", incrementedCounter);
// Job-scoped set
await io.store.job.set("job-set", "counter", incrementedCounter);
}
})
```
And to share data across Jobs you can use `store.env` instead:
```ts
client.defineJob({
id: "job-1",
...
run: async (payload, io, ctx) => {
// store data in one job
await io.store.env.set("cacheKey", "cross-run-shared-key", { foo: "bar" });
}
})
client.defineJob({
id: "job-2",
...
run: async (payload, io, ctx) => {
// access from a different job
const value = await io.store.env.get<{ foo: string }>("cacheKey", "cross-run-shared-key");
}
})
```
## Methods
### `delete()`
@@ -94,21 +142,21 @@ A `Promise` that resolves to the stored value.
<RequestExample>
```ts Example
await client.store.env.set("key", "foo")
await client.store.job.set("key", "bar")
await client.store.run.set("key", "baz")
await io.store.env.set("cacheKey", "key", "foo")
await io.store.job.set("cacheKey", "key", "bar")
await io.store.run.set("cacheKey", "key", "baz")
await client.store.env.get("key") // "foo"
await client.store.job.get("key") // "bar"
await client.store.run.get("key") // "baz"
await io.store.env.get("cacheKey", "key") // "foo"
await io.store.job.get("cacheKey", "key") // "bar"
await io.store.run.get("cacheKey", "key") // "baz"
await client.store.env.has("key") // true
await client.store.job.has("missing") // false
await client.store.run.has("key") // true
await io.store.env.has("cacheKey", "key") // true
await io.store.job.has("cacheKey", "missing") // false
await io.store.run.has("cacheKey", "key") // true
// cleanup
await client.store.env.delete("key")
await client.store.job.delete("key")
await client.store.run.delete("key")
await io.store.env.delete("cacheKey", "key")
await io.store.job.delete("cacheKey", "key")
await io.store.run.delete("cacheKey", "key")
```
</RequestExample>
+19 -19
View File
@@ -26,68 +26,68 @@ Creates a new TriggerClient object.
## Instance properties
<ResponseField name="id" type="string">
Is used to uniquely identify the client.
</ResponseField>
### id
<ResponseField name="store" type="object">
Exposes namespaced [Key-Value Stores](/sdk/triggerclient/instancemethods/store) you can access in and outside of your Jobs.
</ResponseField>
A string that uniquely identifies the client.
### [store](/sdk/triggerclient/store)
An object to access namespaced **Key-Value Stores** in and outside of your Jobs.
## Instance methods
#### [sendEvent()](/sdk/triggerclient/instancemethods/sendevent)
### [sendEvent()](/sdk/triggerclient/instancemethods/sendevent)
Sending an event triggers any Jobs that are listening for that event (based on the name). Use [eventTrigger()](/sdk/eventtrigger) on a Job to listen for events.
You can call this function from anywhere in your code to send an event. The other way to send an event is by using [io.sendEvent()](/sdk/io) from inside a `run()` function.
#### [sendEvents()](/sdk/triggerclient/instancemethods/sendevents)
### [sendEvents()](/sdk/triggerclient/instancemethods/sendevents)
Sending multiple events triggers any Jobs that are listening for those events (based on the name). Use [eventTrigger()](/sdk/eventtrigger) on a Job to listen for events.
You can call this function from anywhere in your code to send multiple events. The other way to send multiple events is by using [io.sendEvents()](/sdk/io) from inside a `run()` function.
#### [getEvent()](/sdk/triggerclient/instancemethods/getevent)
### [getEvent()](/sdk/triggerclient/instancemethods/getevent)
The `getEvent()` method gets the event details for a given eventId.
#### [cancelEvent()](/sdk/triggerclient/instancemethods/cancel-event)
### [cancelEvent()](/sdk/triggerclient/instancemethods/cancel-event)
The `cancelEvent()` method cancels an event that is scheduled to be delivered in the future.
#### [cancelRunsForEvent()](/sdk/triggerclient/instancemethods/cancel-runs-for-event)
### [cancelRunsForEvent()](/sdk/triggerclient/instancemethods/cancel-runs-for-event)
The `cancelRunsForEvent()` method cancels the job runs (yet to be executed) that are triggered by a given eventId.
#### [getRuns()](/sdk/triggerclient/instancemethods/getruns)
### [getRuns()](/sdk/triggerclient/instancemethods/getruns)
The `getRuns()` method gets runs for a Job.
#### [getRun()](/sdk/triggerclient/instancemethods/getrun)
### [getRun()](/sdk/triggerclient/instancemethods/getrun)
The `getRun()` method gets the details for a given Run.
#### [defineJob()](/sdk/triggerclient/instancemethods/define-job)
### [defineJob()](/sdk/triggerclient/instancemethods/define-job)
The `defineJob()` method defines a new Job.
#### [defineHttpEndpoint()](/sdk/triggerclient/instancemethods/define-http-endpoint)
### [defineHttpEndpoint()](/sdk/triggerclient/instancemethods/define-http-endpoint)
The `defineHttpEndpoint()` method defines a new HTTP Endpoint.
#### [defineDynamicTrigger()](/sdk/triggerclient/instancemethods/define-dynamic-trigger)
### [defineDynamicTrigger()](/sdk/triggerclient/instancemethods/define-dynamic-trigger)
The `defineDynamicTrigger()` method defines a new Dynamic Trigger.
#### [defineDynamicSchedule()](/sdk/triggerclient/instancemethods/define-dynamic-schedule)
### [defineDynamicSchedule()](/sdk/triggerclient/instancemethods/define-dynamic-schedule)
The `defineDynamicSchedule()` method defines a new Dynamic Schedule.
#### [defineAuthResolver()](/sdk/triggerclient/instancemethods/define-auth-resolver)
### [defineAuthResolver()](/sdk/triggerclient/instancemethods/define-auth-resolver)
The `defineAuthResolver()` method defines a new Auth Resolver.
#### [on()](/sdk/triggerclient/instancemethods/on)
### [on()](/sdk/triggerclient/instancemethods/on)
Use the `on()` method to listen for run notifications across all Jobs.
+1 -1
View File
@@ -5,7 +5,7 @@ description: "Exposes namespaced **Key-Value Stores** you can access in and outs
---
<Warning>
Only use this for small values - there's a **256KB** per-item size limit.
Only use this for small values - there's a **256KB** size limit per item.
</Warning>
## Namespaces
-1
View File
@@ -14,7 +14,6 @@
],
"devDependencies": {
"@trigger.dev/tsconfig": "workspace:*",
"@trigger.dev/tsup": "workspace:*",
"@types/node": "16.x",
"rimraf": "^3.0.2",
"tsup": "7.1.x",
+1 -2
View File
@@ -1,7 +1,6 @@
import { Nullable, Prettify } from "@trigger.dev/integration-kit";
import { basicProperties, eventSpec } from "./utils";
import { ShopifyExamples, ShopifyPayloads, shopifyExample } from "./payload-examples";
import { Nullable } from "@trigger.dev/integration-kit/types";
import { Prettify } from "@trigger.dev/integration-kit";
type ShopifyThis<TResource> = Prettify<
Nullable<TResource> & {
+1 -3
View File
@@ -9,7 +9,7 @@ import {
retry,
ConnectionAuth,
} from "@trigger.dev/sdk";
import { OmitIndexSignature } from "@trigger.dev/integration-kit/types";
import { OmitIndexSignature } from "@trigger.dev/integration-kit";
import {
ApiVersion,
@@ -24,13 +24,11 @@ import {
// this has to be updated manually with each LATEST_API_VERSION bump
import { restResources, type RestResources } from "@shopify/shopify-api/rest/admin/2023-10";
import "@shopify/shopify-api/adapters/node";
import { ApiScope } from "./schemas";
import { createWebhookEventCatalog, WebhookEventCatalog } from "./triggers";
import { Webhooks, createWebhookEventSource } from "./webhooks";
import { Rest, restProxy } from "./rest";
import { GetWebhookParams } from "@trigger.dev/sdk/triggers/webhook";
export type ShopifyRestResources = OmitIndexSignature<RestResources>;
@@ -1,5 +1,4 @@
import { TypedEventSpecificationExample } from "@trigger.dev/sdk";
import { slugifyId } from "@trigger.dev/sdk/utils";
import { slugifyId, TypedEventSpecificationExample } from "@trigger.dev/sdk";
import AppUninstalled from "./AppUninstalled.json";
import AppSubscriptionsUpdate from "./AppSubscriptionsUpdate.json";
+3 -3
View File
@@ -1,3 +1,6 @@
import { PageInfo, Session } from "@shopify/shopify-api";
import { OmitIndexSignature, Optional, SomeNonNullable } from "@trigger.dev/integration-kit";
import { z } from "zod";
import { ShopifyRestResources, ShopifyRunTask } from "./index";
import { basicProperties, serializeShopifyResource } from "./utils";
import {
@@ -5,9 +8,6 @@ import {
ResourcesWithStandardMethods,
ShopifyInputType,
} from "./types";
import { PageInfo, Session } from "@shopify/shopify-api";
import { OmitIndexSignature, Optional, SomeNonNullable } from "@trigger.dev/integration-kit/types";
import { z } from "zod";
type AllReturnType<TResource extends ShopifyRestResources[ResourcesWithStandardMethods]> = Promise<{
data: RecursiveShopifySerializer<Awaited<ReturnType<TResource["all"]>>["data"]>;
+6 -2
View File
@@ -1,6 +1,10 @@
import {
EventSpecification,
GetWebhookParams,
WebhookSource,
WebhookTrigger,
} from "@trigger.dev/sdk";
import { shopifyEvent } from "./events";
import { EventSpecification } from "@trigger.dev/sdk";
import { GetWebhookParams, WebhookSource, WebhookTrigger } from "@trigger.dev/sdk/triggers/webhook";
import { createWebhookEventSource } from "./webhooks";
const shopifyEvents = {
-1
View File
@@ -6,7 +6,6 @@ import {
Prettify,
} from "@trigger.dev/integration-kit";
import { ShopifyRestResources } from "./index";
import { WebhookTopic } from "./schemas";
type OmitNonSerializable<T> = Omit<OmitFunctions<OmitIndexSignature<T>>, "session">;
+3 -4
View File
@@ -1,9 +1,8 @@
import { Base } from "@shopify/shopify-api/rest/base";
import { RecursiveShopifySerializer } from "./types";
import { WebhookTopic } from "./schemas";
import { DisplayProperty, EventSpecificationExample } from "@trigger.dev/sdk";
import { EventSpecification } from "@trigger.dev/sdk";
import { DisplayProperty, EventSpecification, EventSpecificationExample } from "@trigger.dev/sdk";
import { titleCase } from "@trigger.dev/integration-kit";
import { WebhookTopic } from "./schemas";
import { RecursiveShopifySerializer } from "./types";
export const basicProperties = (payload: Record<string, any>) => {
return payload.id ? [{ label: "ID", text: String(payload.id) }] : [];
+3 -4
View File
@@ -1,4 +1,5 @@
import { IntegrationTaskKey, verifyRequestSignature } from "@trigger.dev/sdk";
import { IntegrationTaskKey, verifyRequestSignature, WebhookSource } from "@trigger.dev/sdk";
import { registerJobNamespace } from "@trigger.dev/integration-kit";
import { z } from "zod";
import { Shopify, ShopifyRunTask } from "./index";
import {
@@ -8,8 +9,6 @@ import {
WebhookTopic,
WebhookTopicSchema,
} from "./schemas";
import { WebhookSource } from "@trigger.dev/sdk/triggers/webhook";
import { registerJobNamespace } from "@trigger.dev/integration-kit/webhooks";
export class Webhooks {
constructor(private runTask: ShopifyRunTask) {}
@@ -19,7 +18,7 @@ export class Webhooks {
return new URL(`/admin/api/${apiVersion}/`, `https://${hostName}`);
}
// just here as an example if we ever want better platform support
// just an example using raw fetch with error handling
#createWithFetch(
key: IntegrationTaskKey,
params: {
+2
View File
@@ -7,8 +7,10 @@ export * from "./triggers/dynamic";
export * from "./triggers/scheduled";
export * from "./triggers/notifications";
export * from "./triggers/invokeTrigger";
export * from "./triggers/webhook";
export * from "./io";
export * from "./types";
export * from "./utils";
export * from "./security";
import { ServerTask } from "@trigger.dev/core";
+2 -2
View File
@@ -727,9 +727,9 @@ export class TriggerClient {
* @returns An HTTP Endpoint, that can be used to create an HTTP Trigger.
* @link https://trigger.dev/docs/documentation/concepts/http-endpoints
*/
defineHttpEndpoint(options: EndpointOptions) {
defineHttpEndpoint(options: EndpointOptions, suppressWarnings = false) {
const existingHttpEndpoint = this.#registeredHttpEndpoints[options.id];
if (existingHttpEndpoint) {
if (!suppressWarnings && existingHttpEndpoint) {
console.warn(
yellow(
`[@trigger.dev/sdk] Warning: The HttpEndpoint "${existingHttpEndpoint.id}" you're attempting to define has already been defined. Please assign a different ID to the HttpEndpoint.`
+9 -6
View File
@@ -313,12 +313,15 @@ export class WebhookTrigger<
}
attachToJob(triggerClient: TriggerClient, job: Job<Trigger<TEventSpecification>, any>) {
triggerClient.defineHttpEndpoint({
id: this.key,
source: "trigger.dev",
icon: this.event.icon,
verify: async () => ({ success: true }),
});
triggerClient.defineHttpEndpoint(
{
id: this.key,
source: "trigger.dev",
icon: this.event.icon,
verify: async () => ({ success: true }),
},
true
);
triggerClient.attachWebhook({
key: this.key,
-2
View File
@@ -603,7 +603,6 @@ importers:
'@trigger.dev/integration-kit': workspace:^2.2.6
'@trigger.dev/sdk': workspace:^2.2.6
'@trigger.dev/tsconfig': workspace:*
'@trigger.dev/tsup': workspace:*
'@types/node': 16.x
rimraf: ^3.0.2
tsup: 7.1.x
@@ -616,7 +615,6 @@ importers:
zod: 3.22.3
devDependencies:
'@trigger.dev/tsconfig': link:../../config-packages/tsconfig
'@trigger.dev/tsup': link:../../config-packages/tsup
'@types/node': 16.18.11
rimraf: 3.0.2
tsup: 7.1.0_typescript@4.9.4
+2
View File
@@ -1,5 +1,7 @@
import { TriggerClient, eventTrigger } from "@trigger.dev/sdk";
import { createExpressServer } from "@trigger.dev/express";
import "@shopify/shopify-api/adapters/node";
import { Shopify } from "@trigger.dev/shopify";
export const client = new TriggerClient({