Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implemented webhook subscription integration. #33

Merged
merged 5 commits into from
Nov 27, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions MoquiConf.xml
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,15 @@
<!-- No copyright or license for configuration file, details here are not considered a creative work. -->
<moqui-conf xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://moqui.org/xsd/moqui-conf-3.xsd">
<default-property name="shopify_api_version" value="2023-01"/>

<default-property name="shopify_webhook_end_point" value="/rest/s1/shopify/webhook/payload"/>

<webapp-list>
<webapp name="webroot">
<!-- Shopify Webhook Request Filter -->
<filter name="ShopifyWebhookFilter" class="co.hotwax.shopify.ShopifyWebhookFilter" async-supported="true">
<url-pattern>/rest/s1/shopify/webhook/*</url-pattern>
</filter>
</webapp>
</webapp-list>
</moqui-conf>
129 changes: 120 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ Set of services, templates and configuration to integrate with Shopify Bulk Expo
This integration enables you,
1. To configure and poll shopify jsonl feeds from SFTP, stage and upload on shopify and run the intended shopify bulk mutation operation.
2. To configure and send shopify bulk queries.

It also polls the running bulk operation status and download and stores the result file locally.
Support for BULK_OPERATION_FINISH webhook will be implemented in next phase.

Expand Down Expand Up @@ -167,7 +168,9 @@ Supported bulk mutations and configuration,
sendServiceName="co.hotwax.shopify.system.ShopifySystemMessageServices.send#BulkMutationSystemMessage"
sendPath="component://shopify-connector/template/graphQL/BulkUpdateProductTags.ftl"
consumeServiceName="co.hotwax.shopify.system.ShopifySystemMessageServices.consume#BulkOperationResult"
receivePath="${contentRoot}/hotwax/shopify/ProductTagsFeed/result/BulkOperationResult-${systemMessageId}-${remoteMessageId}-${nowDate}.jsonl"/>
receivePath="${contentRoot}/hotwax/shopify/ProductTagsFeed/result/BulkOperationResult-${systemMessageId}-${remoteMessageId}-${nowDate}.jsonl">
<paramters parameterName="consumeSmrId" parameterValue="" systemMessageRemoteId=""/>
</moqui.service.message.SystemMessageType>

<!-- SystemMessageType record for sending bulk update product tags result to SFTP -->
<moqui.service.message.SystemMessageType systemMessageTypeId="SendBulkUpdateProductTagsResult"
Expand Down Expand Up @@ -207,10 +210,12 @@ Supported bulk mutations and configuration,
sendServiceName="co.hotwax.shopify.system.ShopifySystemMessageServices.send#BulkMutationSystemMessage"
sendPath="component://shopify-connector/template/graphQL/BulkUpdateProductTags.ftl"
consumeServiceName="co.hotwax.shopify.system.ShopifySystemMessageServices.consume#BulkOperationResult"
receivePath="${contentRoot}/hotwax/shopify/ProductVariantsFeed/result/BulkOperationResult-${systemMessageId}-${remoteMessageId}-${nowDate}.jsonl"/>
receivePath="${contentRoot}/hotwax/shopify/ProductVariantsFeed/result/BulkOperationResult-${systemMessageId}-${remoteMessageId}-${nowDate}.jsonl">
<paramters parameterName="consumeSmrId" parameterValue="" systemMessageRemoteId=""/>
</moqui.service.message.SystemMessageType>

<!-- Additional paramter configuration, a comma seprated values of namespaces -->
<moqui.service.message.SystemMessageTypeParam systemMessageTypeId="BulkUpdateProductVariants"
<moqui.service.message.SystemMessageTypeParameter systemMessageTypeId="BulkUpdateProductVariants"
parameterName="namespaces" parameterValue="" systemMessageRemoteId=""/>

<!-- SystemMessageType record for sending bulk update product variants result to SFTP -->
Expand Down Expand Up @@ -263,17 +268,17 @@ You could configure following default parameters and any additional parameters a

```aidl
<!-- Additional paramter configuration, a comma seprated values of namespaces -->
<moqui.service.message.SystemMessageTypeParam systemMessageTypeId="[systemMessageTypeId]"
<moqui.service.message.SystemMessageTypeParameter systemMessageTypeId="[systemMessageTypeId]"
parameterName="namespaces" parameterValue="" systemMessageRemoteId=""/>

<!-- Additional paramter configuration, default filter query -->
<moqui.service.message.SystemMessageTypeParam systemMessageTypeId="[systemMessageTypeId]"
<moqui.service.message.SystemMessageTypeParameter systemMessageTypeId="[systemMessageTypeId]"
parameterName="fiterQuery" parameterValue="" systemMessageRemoteId=""/>

<!-- Additional parameter configuration, time buffers for fromDate and thruDate, must be an integer value for minutes -->
<moqui.service.message.SystemMessageTypeParam systemMessageTypeId="[systemMessageTypeId]"
<moqui.service.message.SystemMessageTypeParameter systemMessageTypeId="[systemMessageTypeId]"
parameterName="fromDateBuffer" parameterValue="" systemMessageRemoteId=""/>
<moqui.service.message.SystemMessageTypeParam systemMessageTypeId="[systemMessageTypeId]"
<moqui.service.message.SystemMessageTypeParameter systemMessageTypeId="[systemMessageTypeId]"
parameterName="thruDateBuffer" parameterValue="" systemMessageRemoteId=""/>
```

Expand All @@ -286,7 +291,9 @@ You could configure following default parameters and any additional parameters a
sendServiceName="co.hotwax.shopify.system.ShopifySystemMessageServices.send#BulkQuerySystemMessage"
sendPath="component://shopify-connector/template/graphQL/BulkVariantsMetafieldQuery.ftl"
consumeServiceName="co.hotwax.shopify.system.ShopifySystemMessageServices.consume#BulkOperationResult"
receivePath="${contentRoot}/hotwax/shopify/BulkVariantsMetafieldFeed/BulkOperationResult-${systemMessageId}-${remoteMessageId}-${nowDate}.jsonl"/>
receivePath="${contentRoot}/hotwax/shopify/BulkVariantsMetafieldFeed/BulkOperationResult-${systemMessageId}-${remoteMessageId}-${nowDate}.jsonl">
<paramters parameterName="consumeSmrId" parameterValue="" systemMessageRemoteId=""/>
</moqui.service.message.SystemMessageType>

<!-- SystemMessageType record for sending bulk variants metafield query result to SFTP -->
<moqui.service.message.SystemMessageType systemMessageTypeId="SendBulkVariantsMetafieldQueryResult"
Expand Down Expand Up @@ -319,7 +326,9 @@ You could configure following default parameters and any additional parameters a
sendServiceName="co.hotwax.shopify.system.ShopifySystemMessageServices.send#BulkQuerySystemMessage"
sendPath="component://shopify-connector/template/graphQL/BulkOrderMetafieldsQuery.ftl"
consumeServiceName="co.hotwax.shopify.system.ShopifySystemMessageServices.consume#BulkOperationResult"
receivePath="${contentRoot}/hotwax/shopify/BulkOrderMetafieldsFeed/BulkOperationResult-${systemMessageId}-${remoteMessageId}-${nowDate}.jsonl"/>
receivePath="${contentRoot}/hotwax/shopify/BulkOrderMetafieldsFeed/BulkOperationResult-${systemMessageId}-${remoteMessageId}-${nowDate}.jsonl">
<paramters parameterName="consumeSmrId" parameterValue="" systemMessageRemoteId=""/>
</moqui.service.message.SystemMessageType>

<!-- SystemMessageType record for sending bulk order metafields query result to SFTP -->
<moqui.service.message.SystemMessageType systemMessageTypeId="SendBulkOrderMetafieldsQueryResult"
Expand All @@ -341,4 +350,106 @@ You could configure following default parameters and any additional parameters a
<parameters parameterName="fromDate" parameterValue=""/>
<parameters parameterName="thruDate" parameterValue=""/>
</moqui.service.job.ServiceJob>
```

## Shopify Webhook Integration

Set of services and configuration to integrate with Shopify Webhook GraphQL API.
This integration enables you to configure a shopify webhook topic, subscribe/unsubscribe to it, receive payload from the subscribed webhook topic and consume the payload to further process it.

### Webhook Filter

**co.hotwax.shopify.ShopifyWebhookFilter**: A filter to verify HMAC for all incoming webhook payloads and set the required attributes on HTTP request upon successful verification.

#### Configuration

Folliowing configuration is added to MoquiConf.xml,

```aidl
<default-property name="shopify_webhook_end_point" value="/rest/s1/shopify/webhook/payload"/>

<webapp-list>
<webapp name="webroot">
<!-- Shopify Webhook Request Filter -->
<filter name="ShopifyWebhookFilter" class="co.hotwax.shopify.ShopifyWebhookFilter" async-supported="true">
<url-pattern>/rest/s1/shopify/webhook/*</url-pattern>
</filter>
</webapp>
</webapp-list>
```
### Core Services

1. **create#WebhookSubscription**: Subscribe to shopify webhook topic with your apps callbackUrl (end point).
2. **get#WebhookSubscriptions**: Get a list of all subscribed webhooks filtered by query parameters.
3. **delete#WebhookSubscription**: Unsubscribe a specific webhook topic.
4. **verify#Hmac**: Verify hmac for the received webhook payload.
5. **receive#WebhookPayload**: Receive webhook payload in an incoming SystemMessage of the webhook topics SystemMessageType.
6. **produce#WebhookSubscriptionSystemMessage**: Service to initiate webhook subscription of a specific type by creating a system message.
7. **send#WebhookSubscriptionSystemMessage**: Send service to invoke Create Webhook Subscription API for the System Message.
8. **produce#WebhookSubscriptionDeleteSystemMessage**: Service to initiate delete webhook subscription of a specific type by creating a system message.
9. **send#WebhookSubscriptionDeleteSystemMessage**: Send service to invoke Delete Webhook Subscription API for the System Message. This service first get the webhookSubscriptionId for specified webhook topic and registered callbackUrl and the invokes Delete Webhook Subscription API for the webhookSubscriptionId.

### Subscribing a Webhook Topic

1. Following is some global configuration data for webhook subscriptions,
```aidl
<!-- Parent SystemMessageType for all the shopify webhook system message types -->
<moqui.service.message.SystemMessageType systemMessageTypeId="ShopifyWebhook"
description="Parent SystemMessageType for Shopify Webhooks"/>

<!-- EnumerationType for Shopify webhook topic mapping to webhook system message type -->
<moqui.basic.EnumerationType description="Shopify Webhook Enum" enumTypeId="ShopifyWebhookEnum"/>
```
2. Implement a consume service as needed to process webhook payload. In absence of a conusme service, the payload would just be saved as is in an incoming SystemMessage.
3. To subscribe a webhook you need to define following configuration data,
```aidl
<!-- SystemMessageType record for shopify webhook -->
<moqui.service.message.SystemMessageType systemMessageTypeId=""
description=""
parentTypeId="ShopifyWebhook"
sendServiceName="co.hotwax.shopify.webhook.ShopifyWebhookServices.send#WebhookSubscriptionSystemMessage"
sendPath="component://shopify-connector/template/graphQL/WebhookSubscriptionCreate.ftl"
consumeServiceName="[consume service to process webhook payload]">
<parameters parameterName="topic" parameterValue="[GraphQL Webhook Topic]" systemMessageRemoteId=""/>
<!-- Optional, defaluts to "/rest/s1/shopify/webhook/payload" -->
<parameters parameterName="endpoint" parameterValue="" systemMessageRemoteId=""/>
</moqui.service.message.SystemMessageType>

<moqui.basic.Enumeration description="" enumId="[systemMessageTypeId of webhook]"
enumTypeId="ShopifyMessageTypeEnum" enumCode="[Shopify Webhook Topic]"/> (https://shopify.dev/docs/api/admin-rest/2023-10/resources/webhook#event-topics)
```
4. To subscribe to the webhook invoke _produce#WebhookSubscriptionSystemMessage_ service.

### Unsubscribing a Webhook Topic

1. Following is the configuration data for deleting any webhook subscription,
```aidl
<!-- SystemMessageTypeRecord for deleting a specific webhook subscription -->
<moqui.service.message.SystemMessageType systemMessageTypeId="DeleteWebhookSubscription"
description="Delete Shopify Webhook Subscription"
sendServiceName="co.hotwax.shopify.webhook.ShopifyWebhookServices.send#WebhookSubscriptionDeleteSystemMessage"
sendPath="component://shopify-connector/template/graphQL/WebhookSubscriptionDelete.ftl">
<parameters parameterName="queryTemplateLocation" parameterValue="component://shopify-connector/template/graphQL/WebhookSubscriptionsQuery.ftl" systemMessageRemoteId=""/>
</moqui.service.message.SystemMessageType>
```
2. To unsubscribe webhook invoke _produce#WebhookSubscriptionDeleteSystemMessage_ service.

### Supported Shopify Webhooks

#### Bulk Operations Finish (bulk_operations/finish)

```aidl
<!-- SystemMessageType record for shopify BULK_OPERATION_FINISH webhook -->
<moqui.service.message.SystemMessageType systemMessageTypeId="BulkOperationsFinish"
description="Shopify Bulk Operations Finish Webhook"
parentTypeId="ShopifyWebhook"
sendServiceName="co.hotwax.shopify.webhook.ShopifyWebhookServices.send#WebhookSubscriptionSystemMessage"
sendPath="component://shopify-connector/template/graphQL/WebhookSubscriptionCreate.ftl"
consumeServiceName="co.hotwax.shopify.system.ShopifySystemMessageServices.consume#BulkOperationsFinishWebhookPayload">
<parameters parameterName="topic" parameterValue="BULK_OPERATIONS_FINISH" systemMessageRemoteId=""/>
</moqui.service.message.SystemMessageType>

<!-- Enumeration for mapping BulkOperationsFinish SystemMessageType to bulk_operations/finish shopify webhook topic -->
<moqui.basic.Enumeration description="Shopify Bulk Operation Finish Webhook" enumId="BulkOperationsFinish"
enumTypeId="ShopifyMessageTypeEnum" enumCode="bulk_operations/finish"/>
```
33 changes: 31 additions & 2 deletions data/ShopifyConfigDemoData.xml
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@
<!-- Shopify Connection Configuration -->
<moqui.service.message.SystemMessageRemote systemMessageRemoteId="ShopifyConfig"
description="Shopify Connection Configuration"
sendUrl="https://${shopifyHost}/admin/api/${shopifyApiVersion}" username="" password=""
accessScopeEnumId=""/>
sendUrl="https://${shopifyHost}/admin/api/${shopifyApiVersion}" sharedSecret=""
accessScopeEnumId="" sendSharedSecret=""/>

<!-- SystemMessageType record for importing OMS Fulfillment Feed -->
<!-- Note: By default the sendPath local directory structure is created in runtime://datamanager directory.
Expand Down Expand Up @@ -40,6 +40,12 @@
receiveMovePath="/home/${sftpUsername}/hotwax/shopify/ProductTagsFeed/archive"
sendPath="${contentRoot}/Shopify/ProductTagsFeed"/>

<!-- Configure remote SFTP server against your shopify config for consuming bulk operation result -->
<moqui.service.message.SystemMessageTypeParameter systemMessageTypeId="BulkUpdateProductTags"
parameterName="consumeSmrId"
parameterValue=""
systemMessageRemoteId=""/>

<!-- SystemMessageType record for sending bulk update product tags result to SFTP -->
<moqui.service.message.SystemMessageType systemMessageTypeId="SendBulkUpdateProductTagsResult"
description="Send Bulk Update Product Tags Result"
Expand All @@ -57,25 +63,48 @@
receiveMovePath="/home/${sftpUsername}/hotwax/shopify/ProductVariantsFeed/archive"
sendPath="${contentRoot}/shopify/ProductVariantsFeed"/>

<!-- Configure remote SFTP server against your shopify config for consuming bulk operation result -->
<moqui.service.message.SystemMessageTypeParameter systemMessageTypeId="BulkUpdateProductVariants"
parameterName="consumeSmrId"
parameterValue=""
systemMessageRemoteId=""/>

<!-- SystemMessageType record for sending bulk update product variants result to SFTP -->
<moqui.service.message.SystemMessageType systemMessageTypeId="SendBulkUpdateProductVariantsResult"
description="Send Bulk Update Product Variants Result"
parentTypeId="LocalFeedFile"
sendServiceName="co.hotwax.ofbiz.SystemMessageServices.send#SystemMessageFileSftp"
sendPath="/home/${sftpUsername}/hotwax/shopify/BulkUpdateProductVariantsResult/"/>

<!-- Configure remote SFTP server against your shopify config for consuming bulk operation result -->
<moqui.service.message.SystemMessageTypeParameter systemMessageTypeId="BulkVariantsMetafieldQuery"
parameterName="consumeSmrId"
parameterValue=""
systemMessageRemoteId=""/>

<!-- SystemMessageType record for sending bulk variants metafield query result to SFTP -->
<moqui.service.message.SystemMessageType systemMessageTypeId="SendBulkVariantsMetafieldQueryResult"
description="Send Bulk Variants Metafield Query Result"
parentTypeId="LocalFeedFile"
sendServiceName="co.hotwax.ofbiz.SystemMessageServices.send#SystemMessageFileSftp"
sendPath="/home/${sftpUsername}/hotwax/shopify/BulkVariantsMetafieldQueryResult/"/>

<!-- Configure remote SFTP server against your shopify config for consuming bulk operation result -->
<moqui.service.message.SystemMessageTypeParameter systemMessageTypeId="BulkOrderMetafieldsQuery"
parameterName="consumeSmrId"
parameterValue=""
systemMessageRemoteId=""/>

<!-- SystemMessageType record for sending bulk order metafields query result to SFTP -->
<moqui.service.message.SystemMessageType systemMessageTypeId="SendBulkOrderMetafieldsQueryResult"
description="Send Bulk Order Metafields Query Result"
parentTypeId="LocalFeedFile"
sendServiceName="co.hotwax.ofbiz.SystemMessageServices.send#SystemMessageFileSftp"
sendPath="/home/${sftpUsername}/hotwax/shopify/BulkOrderMetafieldsQueryResult/"/>

<!-- Configure shopify config systemMessageRemoteId -->
<moqui.service.message.SystemMessageTypeParameter systemMessageTypeId="BulkOperationsFinish"
parameterName="topic"
parameterValue="BULK_OPERATIONS_FINISH"
systemMessageRemoteId=""/>
</entity-facade-xml>
Loading