@shopify/shopify-api
Version:
Shopify API Library for Node - accelerate development with support for authentication, graphql proxy, webhooks
221 lines (187 loc) • 5.64 kB
text/typescript
import {ValidationErrorReason, ValidationInvalid} from '../utils/types';
import {AdapterArgs} from '../../runtime/types';
import {Session} from '../session/session';
import {ShopifyEventsHeader, ShopifyHeader} from '../types';
export const WebhookType = {
Webhooks: 'webhooks',
Events: 'events',
} as const;
export type WebhookTypeValue = (typeof WebhookType)[keyof typeof WebhookType];
export const WEBHOOK_HEADER_NAMES = {
[WebhookType.Webhooks]: {
hmac: ShopifyHeader.Hmac,
topic: ShopifyHeader.Topic,
domain: ShopifyHeader.Domain,
apiVersion: ShopifyHeader.ApiVersion,
webhookId: ShopifyHeader.WebhookId,
subTopic: ShopifyHeader.SubTopic,
name: ShopifyHeader.Name,
triggeredAt: ShopifyHeader.TriggeredAt,
eventId: ShopifyHeader.EventId,
},
[WebhookType.Events]: {
hmac: ShopifyEventsHeader.Hmac,
topic: ShopifyEventsHeader.Topic,
domain: ShopifyEventsHeader.Domain,
apiVersion: ShopifyEventsHeader.ApiVersion,
eventId: ShopifyEventsHeader.EventId,
handle: ShopifyEventsHeader.Handle,
action: ShopifyEventsHeader.Action,
resourceId: ShopifyEventsHeader.ResourceId,
triggeredAt: ShopifyEventsHeader.TriggeredAt,
},
} as const;
export enum DeliveryMethod {
Http = 'http',
EventBridge = 'eventbridge',
PubSub = 'pubsub',
}
export type WebhookHandlerFunction = (
topic: string,
shop_domain: string,
body: string,
webhookId: string,
apiVersion?: string,
subTopic?: string,
context?: any,
) => Promise<void>;
interface BaseWebhookHandler {
id?: string;
includeFields?: string[];
metafieldNamespaces?: string[];
subTopic?: string;
context?: any;
}
export interface HttpWebhookHandler extends BaseWebhookHandler {
deliveryMethod: DeliveryMethod.Http;
callbackUrl: string;
}
export interface HttpWebhookHandlerWithCallback extends HttpWebhookHandler {
callback: WebhookHandlerFunction;
}
export interface EventBridgeWebhookHandler extends BaseWebhookHandler {
deliveryMethod: DeliveryMethod.EventBridge;
arn: string;
}
export interface PubSubWebhookHandler extends BaseWebhookHandler {
deliveryMethod: DeliveryMethod.PubSub;
pubSubProject: string;
pubSubTopic: string;
}
export type WebhookHandler =
| HttpWebhookHandler
| HttpWebhookHandlerWithCallback
| EventBridgeWebhookHandler
| PubSubWebhookHandler;
// See https://shopify.dev/docs/api/admin-graphql/latest/enums/webhooksubscriptiontopic for available topics
export type WebhookRegistry<Handler extends WebhookHandler = WebhookHandler> =
Record<string, Handler[]>;
// eslint-disable-next-line no-warning-comments
// TODO Rethink the wording for this enum - the operations we're doing are actually "subscribing" and "unsubscribing"
// Consider changing the values when releasing v12.0.0 when it can be safely deprecated
export enum WebhookOperation {
Create = 'create',
Update = 'update',
Delete = 'delete',
}
export interface RegisterParams {
session: Session;
}
export interface RegisterResult {
success: boolean;
deliveryMethod: DeliveryMethod;
result: unknown;
operation: WebhookOperation;
}
export type RegisterReturn = Record<string, RegisterResult[]>;
interface WebhookHttpEndpoint {
__typename: 'WebhookHttpEndpoint';
callbackUrl: string;
}
interface WebhookEventBridgeEndpoint {
__typename: 'WebhookEventBridgeEndpoint';
arn: string;
}
interface WebhookPubSubEndpoint {
__typename: 'WebhookPubSubEndpoint';
pubSubProject: string;
pubSubTopic: string;
}
type WebhookEndpoint =
| WebhookHttpEndpoint
| WebhookEventBridgeEndpoint
| WebhookPubSubEndpoint;
export interface WebhookCheckResponseNode<
T = {
endpoint: WebhookEndpoint;
},
> {
node: {
id: string;
topic: string;
includeFields: string[];
metafieldNamespaces: string[];
} & T;
}
export interface WebhookCheckResponse<T = WebhookCheckResponseNode> {
webhookSubscriptions: {
edges: T[];
pageInfo: {
endCursor: string;
hasNextPage: boolean;
};
};
}
export type AddHandlersParams = Record<
string,
WebhookHandler | WebhookHandler[]
>;
export interface WebhookProcessParams extends AdapterArgs {
rawBody: string;
context?: any;
}
export interface WebhookValidateParams extends WebhookProcessParams {}
export const WebhookValidationErrorReason = {
...ValidationErrorReason,
MissingHeaders: 'missing_headers',
} as const;
export type WebhookValidationErrorReasonType =
(typeof WebhookValidationErrorReason)[keyof typeof WebhookValidationErrorReason];
interface BaseWebhookFields {
apiVersion: string;
domain: string;
hmac: string;
topic: string;
triggeredAt?: string;
}
export interface WebhooksWebhookFields extends BaseWebhookFields {
webhookType: typeof WebhookType.Webhooks;
webhookId: string;
subTopic?: string;
name?: string;
eventId?: string;
}
export interface EventsWebhookFields extends BaseWebhookFields {
webhookType: typeof WebhookType.Events;
eventId: string;
handle?: string;
action?: string;
resourceId?: string;
}
export type WebhookFields = WebhooksWebhookFields | EventsWebhookFields;
export interface WebhookValidationInvalid extends Omit<
ValidationInvalid,
'reason'
> {
valid: false;
reason: WebhookValidationErrorReasonType;
}
export interface WebhookValidationMissingHeaders extends WebhookValidationInvalid {
reason: typeof WebhookValidationErrorReason.MissingHeaders;
missingHeaders: string[];
}
export type WebhookValidationValid = WebhookFields & {valid: true};
export type WebhookValidation =
| WebhookValidationValid
| WebhookValidationInvalid
| WebhookValidationMissingHeaders;