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

Merge HTTP block error feedback, Batch block flexbox improvment for cards, block research tool with documentation, upgrades (PR's #393, #400, #389, #398) #407

Merged
merged 11 commits into from
Nov 6, 2023
Merged
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
# profiling files
chrome-profiler-events.json
speed-measure-plugin.json
docs/flow-analysis/*.json

# IDEs and editors
/.idea
Expand Down
5 changes: 5 additions & 0 deletions cypress.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,21 +6,26 @@ export default defineConfig({
screenshotsFolder: 'cypress/screenshots',
fixturesFolder: 'cypress/fixtures',
reporter: 'mochawesome',

reporterOptions: {
reportDir: 'cypress/results',
overwrite: false,
html: true,
json: true,
},

watchForFileChanges: false,
projectId: 'ebzwp1',
pageLoadTimeout: 100000,
defaultCommandTimeout: 30000,

retries: {
runMode: 2,
openMode: 2,
},

experimentalStudio: true,

e2e: {
setupNodeEvents(on, config) {},
supportFile: false,
Expand Down
123 changes: 95 additions & 28 deletions cypress/e2e/http_block_page_following.ts → cypress/e2e/http_block.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,101 @@ import { loadFlowCode } from '../support/helper';
// tslint:disable: quotemark
/// <reference types="Cypress" />

describe('HTTP Block Integration Tests', () => {

describe('HTTP Block Request', () => {

beforeEach(() => {
// Prevent external network request for adapter config
cy.intercept('GET', 'https://kendraio.github.io/kendraio-adapter/config.json', {
fixture: 'adapterConfig.json'
});

// Prevent external network requests for Workflow cloud
cy.intercept('GET', 'https://app.kendra.io/api/workflowCloud/listWorkflows', {
fixture: 'workflow-cloud.json'
});

// Prevent external network requests for fonts with empty CSS rule
cy.intercept('https://fonts.googleapis.com/\*\*', "\*{ }");
});

it('should return a single set of results. Without pagination', () => {
cy.intercept({
url: 'https://example.com/data'
}, {
statusCode: 200,
body: '["hippo", "giraffe"]'
});

loadFlowCode([
{ "type": "init" },
{
"type": "http",
"method": "GET",
"endpoint": "https://example.com/data"
},
{
"type": "debug",
"open": 2,
"showData": true
}
]);
cy.contains('hippo');
cy.contains('giraffe');
});

it('should return an error', () => {
cy.intercept({
url: 'https://example.com/data'
}, {
statusCode: 400,
body: { error: {
error: "Http failure 400 Bad request",
error_description: "There was a problem with your request"
}
}
});

loadFlowCode([
{ "type": "init" },
{
"type": "http",
"method": "GET",
"endpoint": "https://example.com/data",
"onError": {
"blocks": [
{
"type": "card",
"blocks": [
{
"type": "template",
"template": "Error with submission:<p>{{data.error.error}} - {{data.error.error_description}}</p>"
}
]
}
]
}

},
{
"type": "debug",
"open": 3,
"showData": true
}
]);

cy.contains('hasError:true');
cy.contains('status:400');
cy.contains('errorMessage:"Http failure response for https://example.com/data: 400 Bad Request"');
cy.get('app-template-block').contains('Error with submission')
});


});



describe('HTTP Block Follow Pagination', () => {

beforeEach(() => {
// Prevent external network request for adapter config
Expand Down Expand Up @@ -119,31 +213,6 @@ describe('HTTP Block Integration Tests', () => {
cy.contains('birds');
});

it('should return a single set of results if response is not paginated', () => {
cy.intercept({
url: 'https://example.com/data'
}, {
statusCode: 200,
body: '["hippo", "giraffe"]'
});

loadFlowCode([
{ "type": "init" },
{
"type": "http",
"method": "GET",
"endpoint": "https://example.com/data"
},
{
"type": "debug",
"open": 2,
"showData": true
}
]);
cy.contains('hippo');
cy.contains('giraffe');
});

it('should return first results only if not paginated, with proxy', () => {
cy.intercept({
url: 'https://proxy.kendra.io/',
Expand Down Expand Up @@ -191,6 +260,4 @@ describe('HTTP Block Integration Tests', () => {
// we check it does not contain a second page result:
cy.get('body').should('not.contain', 'fish');
});


});
26 changes: 26 additions & 0 deletions docs/documentation.rst
Original file line number Diff line number Diff line change
Expand Up @@ -39,3 +39,29 @@ Run the following commands (from the root of the repository, or change the paths
sphinx-autobuild docs/ docs/_build/html

Then go to http://127.0.0.1:8000 in the browser.

Investigating Block usage in saved Flows
-----------------------------------------
To help improve the documentation, developers can research block usage in existing
Flows that are saved to Flow Cloud, by configuring and using a Node.js script in the
source repository.
This script is designed to explore the configuration used for particular blocks in
flows saved to Flow Cloud. It can filter by a specific config property, allowing for
more targeted analysis. Modify the script to specify what property is of interest.

The script helps identify patterns or common configurations for specific block types, and
assists in auditing and understanding how a particular property is being used across
multiple flows. The results are output to JSON files on disk for further analysis.

The script is located at `docs/extract-example-block-usage-from-flows.js` in the git
repository.

Usage:

.. prompt:: bash $

cd docs
node extract-example-block-usage-from-flows.js

This will run the script and output the results to a JSON file located in
'/docs/flow-analysis' (or where you set it to save to).
107 changes: 107 additions & 0 deletions docs/extract-example-block-usage-from-flows.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
/**
* This script is designed to explore the configuration used for particular blocks in flows saved to Flow Cloud.
* It can optionally filter by a specific config property, allowing for more targeted analysis.
*
* Why It's Useful:
* - Helps identify patterns or common configurations for specific block types.
* - Assists in auditing and understanding how a particular property is being used across multiple flows.
*
* How to Use:
* 1. Modify the variables below to specify the block type and property of interest (if any).
* 2. Run the script using Node.js.
* 3. Check the generated JSON file for the grouped data.
*/


let inputFilePath = 'flow-analysis/flows.json';
let outputFilePath = 'flow-analysis/flow-block-config-analysis.json';

let blockType = 'form'; // Replace with the block type you're interested in
let propertyOfInterest = null; // E.g: 'uiSchema' - replace with the property you're interested in or leave as null
let KENDRAIO_APP_URL = 'https://app.kendra.io/';

const fs = require('fs');
const https = require('https');
const { URL } = require('url');

const readJsonFile = (filePath) => JSON.parse(fs.readFileSync(filePath, 'utf8'));

const writeJsonFile = (data, filePath) => fs.writeFileSync(filePath, JSON.stringify(data, null, 4));

// Function to generate grouped JSON based on block type and optionally a property of interest
const generateGroupedJson = (inputFilePath, outputFilePath, blockType, propertyOfInterest = null) => {
const flowsData = readJsonFile(inputFilePath);
let enhancedFlowsWithBlocks = [];
let groupedByAdapter = {};

// Iterate through the flows to collect blocks and their metadata
for (const flow of flowsData) {
let flowBlocks = []; // To store the blocks with the property of interest for this flow


if (flow.blocks) {
for (const block of flow.blocks) {
const hasMatchingBlock = block.type === blockType;
let hasPropertyOfInterest;
// If no property of interest is specified, so all properties are interesting!
if (!propertyOfInterest) {
hasPropertyOfInterest = true;
}

if (propertyOfInterest) {
// If the object has a specific property that matches our interest and it's not empty, then it's interesting!
hasPropertyOfInterest = block[propertyOfInterest] && Object.keys(block[propertyOfInterest]).length > 0;
}

if (hasMatchingBlock && hasPropertyOfInterest) {
flowBlocks.push(block);
}
}
}

if (flowBlocks.length > 0) {
// Add metadata
const blockMetadata = {
adapterName: flow.adapterName,
id: flow.id,
title: flow.title,
url: `${KENDRAIO_APP_URL}${flow.adapterName}/${flow.id}`
};

// Add the blocks and metadata to the enhanced list
const enhancedFlowEntry = { meta: blockMetadata, blocks: flowBlocks };
enhancedFlowsWithBlocks.push(enhancedFlowEntry);
}
}

// Group the flows by 'adapterName'
for (const entry of enhancedFlowsWithBlocks) {
const adapterName = entry.meta.adapterName;
if (!groupedByAdapter[adapterName]) {
groupedByAdapter[adapterName] = [];
}
groupedByAdapter[adapterName].push(entry);
}

// Write the grouped data to the output file
writeJsonFile(groupedByAdapter, outputFilePath);
};

// Main function to execute the script
(async () => {
// Check if 'flows.json' exists in the current directory
if (!fs.existsSync(inputFilePath)) {
// Download the JSON file using Node.js built-in https
const url = new URL(`${KENDRAIO_APP_URL}flows`);
const file = fs.createWriteStream(inputFilePath);
https.get(url, (response) => {
response.pipe(file);
file.on('finish', () => {
file.close();
generateGroupedJson(inputFilePath, outputFilePath, blockType, propertyOfInterest);
});
});
} else {
generateGroupedJson(inputFilePath, outputFilePath, blockType, propertyOfInterest);
}
})();
1 change: 1 addition & 0 deletions docs/flow-analysis/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Analysed flows will be saved here using the extraction script in the parent directory.
47 changes: 47 additions & 0 deletions docs/workflow/blocks/batch.rst
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,50 @@ Supported properties

- **blocks** (array) - the workflow (list of blocks) to run for each item in the input data list.
- **flex** (boolean) (default = false) - apply a flex layout to the batch workflow outputs.


Examples
---------

Cards Gallery Layout
^^^^^^^^^^^^^^^^^^^^
One helpful application of the Batch Block is to act as a container, allowing the child items to have distinct layouts.
A common way to use the Batch Block is in conjunction with the Cards Block. When providing the Batch Block with an array of Card Block and configuring `flex: true`, it will produce a gallery-style layout.

.. code-block:: json

// Data
[
{
"title": `"iPhone Charger fast charging"`,
"imageUrl": `"https://img.fruugo.com/product/4/36/728056364_max.jpg"`
},
{
"title": `"Wireless On-Ear Headphones"`,
"imageUrl": `"https://m.media-amazon.com/images/I/61WQ0mBtBYL._AC_SL1500_.jpg"`
},
{
"title": `"Magnetic Wireless Charger Fast"`,
"imageUrl": `"https://m.media-amazon.com/images/I/51ZTUXXpT1L._AC_SL1500_.jpg"`
}
]

{
"type": "batch",
"flex": true,
"blocks": [
{
"type": "card",
"blocks": [
{
"type": "template",
"template": "<h3>{{data.title}}</h3>"
},
{
"type": "template",
"template": "<img width='200' height='300' src='{{data.imageUrl}}' />"
}
]
}
]
}
Loading
Loading