Hands-on Adobe Firefly
Hands-on Adobe Firefly on Adobe I/O
Project Firefly is a relatively new framework that was introduced by Adobe in mid-2020 and is currently still in beta (as of 02/05/2021). I heard about the project early on, but only recently got a chance to play around with it.
After spending a few days playing around with the Firefly, I realized that it really deserved its own article, and so here we are.
I want to point out that everything I write here is based on my own experience. And I want to share that experience with people who are just starting out with the Firefly project and want to get to know it better.
Project Firefly
What is the Firefly project? As described on the homepage of Project Firefly - "Project Firefly is a complete framework for building and deploying custom web applications in minutes on our serverless platform".
The main idea is to provide developers and integrators with a platform to build and deploy their applications without having to worry about hosting or hardware for the applications. The whole system provides you an environment to develop, deploy, test and release your applications securely, provides a wide range of tools for building JS based microservices, UI interfaces for them and securel use and integrate them into your architecture.
For me, as an Adobe Commerce / Magento open source architect, this project was very interesting from a microservices perspective. Since Magento is a huge e-commerce system that is still presented as a monolith, scalability and maintainability issues are critical, especially when it comes to integrating Magento into complex architectures with millions of entities and a large number of other services.
I see the Firefly project as a way to reduce the load of the Magento monolith by delegating various processes and computations to the Firefly infrastructure and services.
How to get access
As mentioned earlier, Firefly is currently in beta and you can sign up for a preview on the project's main page: Creating a custom native Adobe Cloud app.
Unfortunately, if you're an individual developer, you'll need to provide an organization ID, which means Adobe will only provide access to organizations.
After submitting a request form, you will be granted access.
Getting Started
I won't go into too much detail here. You can follow this documentation to create a new project:
I would like to note a few points:
- I had to install Adobe AIO-CLI
If you're looking for more details, you can find the source code on Github: https://github.com/adobe/aio-cli - Connect to Preview You can also search the Adobe namespace and find other interesting projects. When you're done, you can log in to Adobe Infrastructure using the "aio login" command. - After you run "aio app run -local", you get links to open your bootstrapped application in the browser
Adobe AIO CLI running on your terminal
From this moment on, your application is running and can be tested. Note that by default the application runs with LOG_LEVEL: debug (this can be changed later in manifest.yml for each runtime action separately). Any logs that your application generates can be viewed directly in the console output. Note, however, that logging can take some time after an action is executed.
If you change something in the code, you don't have to restart the application, after a few seconds the changes will be available and you can test again.
I assume that you followed the basic instructions and were able to get the default application to work.
Let's play around
We have defined 2 milestones for our test task:
- Create the ability to enter the header and body of an API request into Magento and publish products via the POST /products endpoint.
- Create a headless service that can import files from external CSV files into Magento
Part 1: UI for API Requests
First, we are adding a new runtime action, which will execute the operations we need
In manifest.yml add:
push-product:
function: actions/push-product/index.js
web: 'yes'
runtime: 'nodejs:12'
inputs:
LOG_LEVEL: debug
apiKey: $SERVICE_API_KEY
annotations:
require-adobe-auth: false
final: true
Then in the /actions folder create a new index.js file where we can define our actions:
const Papa = require('papaparse')
const fetch = require('node-fetch')
const { Core, Events } = require('@adobe/aio-sdk')
const uuid = require('uuid')
const cloudEventV1 = require('cloudevents-sdk/v1')
const { errorResponse, getBearerToken, stringParameters, checkMissingRequestInputs } = require('../utils')
// main function that will be executed by Adobe I/O Runtime
async function main (params) {
// create a Logger
const logger = Core.Logger('main', { level: params.LOG_LEVEL || 'info' })
try {
// 'info' is the default level if not set
logger.info('Calling the main action')
const apiEndpoint = 'https://URL/rest/V1/products'
const res = await fetch(apiEndpoint, {
method: 'POST',
body: JSON.stringify(params.import_data),
headers: {
"Authorization": params.__ow_headers['authorization'],
"Content-Type": params.__ow_headers['content-type'],
}
})
if (!res.ok) {
logger.info((res))
throw new Error('request to ' + apiEndpoint + ' failed with status code ' + res.status)
}
const content = await res.json()
const response = {
statusCode: 200,
body: content
}
logger.info(stringParameters(response))
return response
} catch (error) {
// log any server errors
logger.error(error)
// return with 500
return errorResponse(500, 'server error', logger)
}
}
exports.main = main
In this code sample, parameters that you specified through your application's form are passed to Magento through the REST API. The response to this execution is returned to the user.
Restart your application and you will see a new action in your front-end UI. Now, if you select the "push-product" action and add the headers and body required by Magento REST API to call POST /products, you can successfully publish a new product to the Magento backend.
Adding a new product in Adobe Commerce using project firefly in Adobe I/O
Part 2: Create CSV Importer / Crons
For the second part we have thought about regular import of products into Magento. As a use case - the customer updates a CSV file with new products every day, and your service needs to fetch the file, parse it and publish it to Magento.
Example of a file with product entries:
First of all, in manifest.xml for your action, please remove the "web: 'yes'" flag or set it to "no".
Second, we need to configure the alert feed that triggers our runtime action once a day (we did it once a minute for testing).
To do this, in your manifest.xml add sections with triggers and with rules.
triggers:
everyMin:
feed: /whisk.system/alarms/interval
inputs:
minutes: 1
rules:
everyMinRule:
trigger: everyMin
action: generic
Triggers define intervals for the execution of your action. Rules define the mapping between trigger and action.
After making changes, you need to deploy your application using aio app deploy and use the aio rt activation list command to check if your action has been invoked.
Checking invoked actions in the aio rt activation list
So, as you can see, the task implementation doesn't look complex, and Firefly also provides ways to implement it quite quickly.
Full action code (don't forget to install npm install papaparse for CSV parsing).
In short, actions are:
- Downloading a CSV file from an external source
- Parsing the file and reading the contents
- Convert content into JSON compatible Magento Rest API/products request.
- Executing the Magento API call to the /products endpoint.
- Read response
const Papa = require('papaparse')
const fetch = require('node-fetch')
const { Core, Events } = require('@adobe/aio-sdk')
const uuid = require('uuid')
const cloudEventV1 = require('cloudevents-sdk/v1')
const { errorResponse, getBearerToken, stringParameters, checkMissingRequestInputs } = require('../utils')
function csvToJson(csv) {
const logger = Core.Logger('main', { level: 'debug' })
logger.debug(JSON.stringify(csv));
const header = csv[0];
const out = csv.map((el, i) => {
if (i === 0)
return {};
const obj = {};
el.forEach((item, index) => {
obj[header[index].trim()] = item.trim();
})
const newObj = {};
newObj.product = obj;
return newObj;
});
logger.debug(JSON.stringify(out));
return out;
}
// main function that will be executed by Adobe I/O Runtime
async function main (params) {
// create a Logger
const logger = Core.Logger('main', { level: params.LOG_LEVEL || 'info' })
try {
// 'info' is the default level if not set
logger.info('Calling the main action')
const csv = await fetch("https://URL/media/firefly.csv")
.then(resp => resp.text())
.then(result => {
const res = Papa.parse(result);
return csvToJson(res.data);
})
// replace this with tddhe api you want to access
const apiEndpoint = 'https://URL/rest/V1/products'
const content = [];
const out = Promise.all(csv.map(async (el, i) => {
if (i === 0)
return;
const res = await fetch(apiEndpoint, {
method: 'POST',
body: JSON.stringify(el),
headers: {
"Authorization": “Bearer 123123123",
"Content-Type": “application/json",
}
})
if (!res.ok) {
logger.info((res))
throw new Error('request to ' + apiEndpoint + ' failed with status code ' + res.status)
}
content.push(await res.json());
}));
const response = {
statusCode: 200,
body: content
}
logger.info(stringParameters(response))
return response
} catch (error) {
// log any server errors
logger.error(error)
// return with 500
return errorResponse(500, 'server error', logger)
}
}
exports.main = main
Debugging with VSCode
Debugging of Firefly application is quite easy.
Add the following code into ".vscode/launch.json" in case you are adding a new runtime action.
{
"type": "pwa-node",
"name": "Action:csvimportmagento-0.0.1/push-product",
"request": "launch",
"runtimeExecutable": "${workspaceFolder}/node_modules/.bin/wskdebug",
"envFile": "${workspaceFolder}/dist/.env.local",
"timeout": 30000,
"killBehavior": "polite",
"localRoot": "${workspaceFolder}",
"remoteRoot": "/code",
"outputCapture": "std",
"attachSimplePort": 0,
"runtimeArgs": [
"csvimportmagento-0.0.1/push-product",
"${workspaceFolder}/actions/push-product/index.js",
"-v",
"—disable-concurrency",
"—kind",
"nodejs:12"
]
},
...
"compounds": [
{
"name": "Actions",
"configurations": [
......
"Action:csvimportmagento-0.0.1/push-product"
]
},
{
"name": "WebAndActions",
"configurations": [
......
"Action:csvimportmagento-0.0.1/push-product"
]
}
]
Then just set breakpoints for the required elements, choose the actions you want to debug, and click start.
Debugging Project Firefly
For more details, please use this guide: https://adobeio-codelabs-debugging-adobedocs.project-helix.page/?src=/README.html![]image6
Conclusion
For me, Project Firefly is something I have been waiting for a long time. Since I started working on complex and heavy systems, I came to the conclusion that there are no other ways to delegate part of the Magento system to other services. So Firefly is exactly what I was looking for.
The topics and examples I covered in this article are just a small part of everything Firefly can do for microservices environments.
And it only took me a day to fall in love with the software.
I'm looking forward to working with React Spectrum and UI frameworks, Journaling API, CI/CD in Firefly, working with Adobe Events, and much more.