Skip to content

Commit

Permalink
Merge pull request #26 from adobe/rojo-add_order_bidirectional_integr…
Browse files Browse the repository at this point in the history
…ation_sample

Add order bidirectional integration sample used in the Adobe Developer's Integration Starter Kit for Adobe Commerce session
  • Loading branch information
slamech authored Oct 23, 2024
2 parents 6636d6f + c8c0537 commit d9e6ecb
Show file tree
Hide file tree
Showing 154 changed files with 34,323 additions and 0 deletions.
161 changes: 161 additions & 0 deletions starter-kit/order-bidirectional-integration-sample-prj/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
# Order Bidirectional Integration Sample Project

Sample code for a starter kit based project that demonstrates how to integrate Adobe Commerce with a mock third-party back-office system.

The project includes the following features:
- Integration of the order entity between Adobe Commerce and a mock third-party back-office system.
- Integration of an email notification system to send an email when an order is created in Adobe Commerce.

The code was used in this Adobe Developer's Integration Starter Kit for Adobe Commerce session:

[![Integration Starter Kit for Adobe Commerce](https://img.youtube.com/vi/PQvdt7DrO7I/0.jpg)](https://www.youtube.com/watch?v=PQvdt7DrO7I)

The starter kit public documentation can be found at [Adobe Developer Starter Kit docs](https://developer.adobe.com/commerce/extensibility/starter-kit/).

* [Prerequisites](#prerequisites)
* [Starter Kit first deploy & onboarding](#starter-kit-first-deploy--onboarding)
* [Development](#development)
* [References](#references)

## Prerequisites

### Create App Builder project
Go to the [Adobe developer console](https://developer.adobe.com/console) portal
- Click on `Create a new project from template`
- Select `App Builder`
- Chose a name and title
- Select stage workspace or create a new one
- Add the following API services (select default Oauth server to server)
- I/0 events
- Adobe I/O Events for Adobe Commerce
- I/O management API
- Download the [workspace configuration JSON](https://developer.adobe.com/commerce/extensibility/events/project-setup/#download-the-workspace-configuration-file) file and save it as `workspace.json` in the `./scripts/onboarding/config` starter kit folder because you will use it to configure Adobe IO Events in commerce afterward.

### Configure a new Integration in commerce
Configure a new Integration to secure the calls to Commerce from App Builder using OAuth by following these steps:
- In the Commerce Admin, navigate to System > Extensions > Integrations.
- Click the `Add New Integration` button.
- Give the integration a name. The rest of the fields can be left blank.
- Select API on the left and grant access to all the resources.
- Click Save.
- In the list of integrations, activate your integration.
- To configure the starter kit, you will need the integration details (consumer key, consumer secret, access token, and access token secret).

### Install Commerce Eventing module (only required when running Adobe Commerce versions 2.4.4 or 2.4.5)
Install Adobe I/O Events for Adobe Commerce module in your commerce instance following this [documentation](https://developer.adobe.com/commerce/extensibility/events/installation/)

> **Note**
>
> By upgrading the Adobe I/O Events for Adobe Commerce module to version 1.6.0 or greater, you will benefit from some additional automated steps during onboarding.
## Starter Kit first deploy & onboarding
Following the next steps, you will deploy and onboard the starter kit for the first time. The onboarding process sets up event providers and registrations based on your selection.

### Download the project
- Download and unzip the project
- Copy the env file `cp env.dist .env`
- Fill in the values following the comments on the env file.

### Configure the project
Install the npm dependencies using the command:
```bash
npm install
```

This step will connect your starter kit project to the App builder project you created earlier.
Ensure to select the proper Organization > Project > Workspace with the following commands:
```bash
aio login
aio console org select
aio console project select
aio console workspace select
```

Sync your local application with the App Builder project using the following command:
```bash
aio app use
# Choose the option 'm' (merge)
```

### Deploy
Run the following command to deploy the project; this will deploy the runtime actions needed for the onboarding step:
```bash
aio app deploy
```
You can confirm the success of the deployment in the Adobe Developer Console by navigating to the `Runtime` section on your workspace.

### Onboarding

#### Execute the onboarding
This step will generate the IO Events providers and the registrations for your starter kit project.
If your Commerce instance Adobe I/O Events for Adobe Commerce module version 1.6.0 or greater, the module will also be automatically configured by the onboarding script.
To start the process run the command:
```bash
npm run onboard
```

The console will return the provider's IDs and save this information:
- You will need the commerce instance ID and provider ID to configure your commerce instance later.
- You will need the backoffice provider id to send the events to the App builder project.
e.g., of output:
```bash
Process of On-Boarding done successfully: [
{
key: 'commerce',
id: 'THIS IS THE ID OF COMMERCE PROVIDER',
instanceId: 'THIS IS THE INSTANCE ID OF COMMERCE PROVIDER',
label: 'Commerce Provider'
},
{
key: 'backoffice',
id: 'THIS IS THE ID OF BACKOFFICE PROVIDER',
instanceId: 'THIS IS THE INSTANCE ID OF BACKOFFICE PROVIDER',
label: 'Backoffice Provider'
},
{
key: 'email',
id: 'THIS IS THE ID OF EMAIL PROVIDER',
instanceId: 'THIS IS THE INSTANCE ID OF EMAIL PROVIDER',
label: 'Email Provider'
}
]

```
Check your App developer console to confirm the creation of the registrations.


### Complete the Adobe Commerce eventing configuration

> **Note**
>
> If your Commerce instance Adobe I/O Events for Adobe Commerce module version is 1.6.0 or greater and the onboarding script completed successfully, the following steps are not required. The onboarding script will configure the Adobe Commerce instance automatically.
> Follow the steps in the next section to validate that the configuration is correct or skip to the next section.
You will configure your Adobe Commerce instance to send events to your App builder project using the following steps

#### Configure Adobe I/O Events in Adobe Commerce instance
To configure the provider in Commerce, do the following:
- In the Adobe Commerce Admin, navigate to Stores > Settings > Configuration > Adobe Services > Adobe I/O Events > General configuration
- Select `OAuth (Recommended)` from the `Adobe I/O Authorization Type` menu.
- Copy the contents of the `<workspace-name>.json` (Workspace configuration JSON you downloaded in the previous step [`Create app builder project`](#create-app-builder-project)) into the `Adobe I/O Workspace Configuration` field.
- Copy the commerce provider instance ID you saved in the previous step [`Execute the onboarding](#execute-the-onboarding) into the `Adobe Commerce Instance ID` field.
- Copy the commerce provider ID you saved in the previous step [`Execute the onboarding`](#execute-the-onboarding) into the `Adobe I/O Event Provider ID` field.
- Click `Save Config`.
- Enable Commerce Eventing by setting the `Enabled` menu to Yes. (Note: You must enable cron so that Commerce can send events to the endpoint.)
- Enter the merchant's company name in the `Merchant ID` field. You must use alphanumeric and underscores only.
- In the `Environment ID` field, enter a temporary name for your workspaces while in development mode. When you are ready for production, change this value to a permanent value, such as `Production`.
- (Optional) By default, if an error occurs when Adobe Commerce attempts to send an event to Adobe I/O, Commerce retries a maximum of seven times. To change this value, uncheck the Use system value checkbox and set a new value in the Maximum retries to send events field.
- (Optional) By default, Adobe Commerce runs a cron job (clean_event_data) every 24 hours that delete event data three days old. To change the number of days to retain event data, uncheck the Use system value checkbox and set a new value in the Event retention time (in days) field.
- Click `Save Config`.

#### Subscribe to events in Adobe Commerce instance
> **Note**
>
> If your Commerce instance Adobe I/O Events for Adobe Commerce module version is 1.6.0 or greater, run the commerce-event-subscribe script to automatically subscribe to the Commerce events in `scripts/commerce-event-subscribe/config/commerce-event-subscribe.json`
> ```bash
> npm run commerce-event-subscribe
> ```
> Otherwise, follow the steps below to subscribe to the events manually.
To subscribe to events, follow this [documentation](https://developer.adobe.com/commerce/extensibility/events/configure-commerce/#subscribe-and-register-events).
For events of type 'plugin' you can also check this [documentation](https://developer.adobe.com/commerce/extensibility/events/commands/#subscribe-to-an-event).
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/*
Copyright 2022 Adobe. All rights reserved.
This file is licensed to you under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. You may obtain a copy
of the License at http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software distributed under
the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
OF ANY KIND, either express or implied. See the License for the specific language
governing permissions and limitations under the License.
*/

const HTTP_OK = 200
const HTTP_BAD_REQUEST = 400
const HTTP_UNAUTHORIZED = 401
const HTTP_NOT_FOUND = 404
const HTTP_INTERNAL_ERROR = 500

const BACKOFFICE_PROVIDER_KEY = 'backoffice'

const PUBLISH_EVENT_SUCCESS = 'OK'

module.exports = {
HTTP_OK,
HTTP_BAD_REQUEST,
HTTP_UNAUTHORIZED,
HTTP_NOT_FOUND,
HTTP_INTERNAL_ERROR,
BACKOFFICE_PROVIDER_KEY,
PUBLISH_EVENT_SUCCESS
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
/*
Copyright 2022 Adobe. All rights reserved.
This file is licensed to you under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. You may obtain a copy
of the License at http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software distributed under
the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
OF ANY KIND, either express or implied. See the License for the specific language
governing permissions and limitations under the License.
*/

const Oauth1a = require('oauth-1.0a')
const crypto = require('crypto')
const got = require('got')

/**
* This function return the Adobe commerce OAuth client
*
* @returns {object} - The Oauth client
* @param {object} options - include the information to configure oauth
* @param {object} logger - Logger
*/
function getOauthClient (options, logger) {
const instance = {}

// Remove trailing slash if any
const serverUrl = options.url
const apiVersion = options.version
const oauth = Oauth1a({
consumer: {
key: options.consumerKey,
secret: options.consumerSecret
},
signature_method: 'HMAC-SHA256',
hash_function: hashFunctionSha256
})
const token = {
key: options.accessToken,
secret: options.accessTokenSecret
}

/**
* This function create the sha 256 hash
*
* @returns {string} - returns generated hash
* @param {string} baseString - base string
* @param {string} key - key to encrypt
*/
function hashFunctionSha256 (baseString, key) {
return crypto.createHmac('sha256', key).update(baseString).digest('base64')
}

/**
* This function make the call to the api
*
* @returns {object} - returns the call response
* @param {object} requestData - include the request data
* @param {string} requestToken - access token
* @param {object} customHeaders - include custom headers
*/
async function apiCall (requestData, requestToken = '', customHeaders = {}) {
try {
logger.debug('Fetching URL: ' + requestData.url + ' with method: ' + requestData.method)

const headers = {
...(requestToken
? { Authorization: 'Bearer ' + requestToken }
: oauth.toHeader(oauth.authorize(requestData, token))),
...customHeaders
}

return await got(requestData.url, {
http2: true,
method: requestData.method,
headers,
body: requestData.body,
responseType: 'json'
}).json()
} catch (error) {
logger.error(`Error fetching URL ${requestData.url}: ${error}`)
throw error
}
}

instance.consumerToken = async function (loginData) {
return apiCall({
url: createUrl('integration/customer/token'),
method: 'POST',
body: loginData
})
}

instance.get = async function (resourceUrl, requestToken = '') {
const requestData = {
url: createUrl(resourceUrl),
method: 'GET'
}
return apiCall(requestData, requestToken)
}

/**
* This function create the full url
*
* @returns {string} - generated url
* @param {string} resourceUrl - Adobe commerce rest API resource url
*/
function createUrl (resourceUrl) {
return serverUrl + apiVersion + '/' + resourceUrl
}

instance.post = async function (resourceUrl, data, requestToken = '', customHeaders = {}) {
const requestData = {
url: createUrl(resourceUrl),
method: 'POST',
body: data
}
return apiCall(requestData, requestToken, customHeaders)
}

instance.put = async function (resourceUrl, data, requestToken = '', customHeaders = {}) {
const requestData = {
url: createUrl(resourceUrl),
method: 'PUT',
body: data
}
return apiCall(requestData, requestToken, customHeaders)
}

instance.delete = async function (resourceUrl, requestToken = '') {
const requestData = {
url: createUrl(resourceUrl),
method: 'DELETE'
}
return apiCall(requestData, requestToken)
}

return instance
}

/**
* This function create the oauth client to use for calling adobe commerce api
*
* @returns {object} - returns the oauth client
* @param {object} options - define the options for the client
* @param {object} logger - define the Logger
*/
function getCommerceOauthClient (options, logger) {
options.version = 'V1'
options.url = options.url + 'rest/'
return getOauthClient(options, logger)
}

module.exports = {
getOauthClient,
getCommerceOauthClient
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/*
Copyright 2022 Adobe. All rights reserved.
This file is licensed to you under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. You may obtain a copy
of the License at http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software distributed under
the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
OF ANY KIND, either express or implied. See the License for the specific language
governing permissions and limitations under the License.
*/

const openwhisk = require('openwhisk')

class Openwhisk {
#openwhiskClient

constructor (host, apiKey) {
this.#openwhiskClient = openwhisk({ apihost: host, api_key: apiKey })
}

async invokeAction (action, data) {
return await this.#openwhiskClient.actions.invoke({
name: action,
blocking: true,
params: {
data
}
})
}
}

module.exports = Openwhisk
Loading

0 comments on commit d9e6ecb

Please sign in to comment.