About Article:
This article will cover how you can achieve the following:
How to execute journeys in sequence via API in Virtuoso.
Problem Statement:
This article will explain how to execute multiple journeys in a specific order using an API. It will guide you through the process of making API calls to ensure that these journeys run one after the other in the correct sequence.
Solution:
The execution of the journeys in required sequence via API in Virtuoso can be done by using an extension. Following is the extension code used to execute journeys sequentially.
Create 2 new extensions using the following code:
Extension 1: retrieveJourneyDetails
Resources : https://cdnjs.cloudflare.com/ajax/libs/axios/1.6.5/axios.min.js (Please ensure you have added this link in the extension resources section)
Code:
const fetchData = async () => {
const makeRequest = async (url, headers = '') => {
try {
const { data } = await axios.get(
url,
headers ? { headers: JSON.parse(headers) } : {},
);
return data;
} catch (e) {
throw new Error(`Request failed: ${e.message}`);
}
};
const basicUrl = 'https://api.virtuoso.qa/api';
const token = tokenInput;
//const projectID = 31694;
//const goalID = 138844;
const endpoint = `${basicUrl}/testsuites/execution?projectId=${projectID}&goalId=${goalID}&includeJourneyDetails=true`;
const headers = {
Authorization: `Bearer ${token}`,
'Content-Type': 'application/json',
};
if (!endpoint) {
throw new Error('Endpoint URL parameter is missing');
}
try {
const responseData = await makeRequest(endpoint, JSON.stringify(headers));
const titlesAndTags = [];
for (const journeyId in responseData.item.journeys) {
const journey = responseData.item.journeys[journeyId].journey;
const id = journey.id; // Extracting the journey ID
const title = journey.title;
const tags = journey.tags;
const canonicalId = journey.canonicalId;
const snapshotId = journey.snapshotId;
const goalId = journey.goalId
const tagNames = tags.map(tag => tag.name);
const lastExecution = responseData.item.journeys[journeyId].lastExecution;
let jobId = null;
if (lastExecution && lastExecution.job && lastExecution.job.id) {
jobId = lastExecution.job.id;
}
titlesAndTags.push({ id, title, tagNames, canonicalId, snapshotId, jobId, goalId });
}
console.log('Titles and Tags:', titlesAndTags);
return titlesAndTags;
} catch (error) {
throw new Error(`Error parsing JSON: ${error.message}`);
}
};
return fetchData();
Extension 2: findAndExecuteJourney
Resources : https://cdnjs.cloudflare.com/ajax/libs/axios/1.6.7/axios.min.js (Please ensure you have added this link in the extension resources section)
Code:
async function executeTestSuiteByTitle(response, titleToFind) {
if (!response || !Array.isArray(response) || response.length === 0) {
throw new Error('Invalid or empty response data provided');
}
function findIdByTitle(data, title) {
const normalizeTitle = (str) => str.normalize("NFKD").replace(/[\u0300-\u036F]/g, ""); // Normalizes and removes combining diacritical marks
let foundIds = [];
for (let i = 0; i < data.length; i++) {
const obj = data[i];
if (normalizeTitle(obj.title) === normalizeTitle(title)) {
foundIds.push({
id: obj.id,
goalId: obj.goalId,
snapshotId: obj.snapshotId,
canonicalId: obj.canonicalId
});
} else if (obj.children) {
const found = findIdByTitle(obj.children, title);
foundIds = foundIds.concat(found);
}
}
return foundIds;
}
const testSuiteIds = findIdByTitle(response, titleToFind);
if (testSuiteIds.length === 0) {
throw new Error(`No test suite found with title: ${titleToFind}`);
} else if (testSuiteIds.length > 1) {
throw new Error(`Multiple test suites found with title: ${titleToFind}`);
} else {
const { id, goalId, snapshotId, canonicalId } = testSuiteIds[0];
return executeTestSuiteById(id, goalId, snapshotId, canonicalId);
}
}
async function executeTestSuiteById(testSuiteId, goalId, snapshotId, canonicalId) {
const basicUrl = 'https://api.virtuoso.qa/api';
const bearerToken = token;
if (!bearerToken) {
throw new Error('Bearer token not provided');
}
const apiUrl = `${basicUrl}/goals/${goalId}/snapshots/${snapshotId}/execute?envelope=false`;
try {
await axios.post(apiUrl, {
"dataDriven": true,
"dataRowsPerTestSuiteCanonicalId": {
[canonicalId]: JSON.parse(dataRowNumber)
},
"testSuiteCanonicalIds": [canonicalId]
}, {
headers: {
'Authorization': `Bearer ${bearerToken}`
}
});
return 'Successful GET request';
} catch (error) {
throw new Error(error.message);
}
}
async function main() {
try {
const response = JSON.parse(responseInput);
const titleToFind = journeyTitle;
const result = await executeTestSuiteByTitle(response, titleToFind);
console.log(result);
return {'status': result};
} catch (error) {
console.error(error.message);
throw new Error(error.message);
}
}
main().then(done).catch(doneError);
Steps to to use:
In order to connect the second journey to the first journey we need to write the test steps using the extension in a checkpoint specifying the journey related details.
In the first journey, create a new checkpoint and add the following steps:
- Open the "https://rocketshop.virtuoso.qa/#/store/shop" in a new tab.
- retrieveJourneyDetails() returning $response
- findAndExecuteJourney_New() returning $status
Use the following parameters while using the retrieveJourneyDetails extensions in the test step.
- yourToken: Use the token created from the user profile for the organization.
- projectID: Use the projectId of the project which contains these Journeys
- Goal ID: Use the goalId of the project which contains these Journeys
Note: These can be extracted from the running Url
Use the following parameters while using the findAndExecuteJourney extensions in the test step.
- journeyTitle: Use the name of the Journey to be executed next
- Token: Use the token created from the user profile for the organization.
- responseInput: Use the output (returning variable from the above test step) i.e. $response
- dataRowNumber & env: Use the test data row number to be used (if the journey is data driven), set to [1] for default setting to use the first row of the test data table
- Env: Use the environment to be used (in uppercase) (i.e. APP or APP2)
Follow the same step for the second Journey if you wish to connect a third journey to the second journey.
Repeat the extension steps in all the Journeys which are supposed to be executed in sequence until the last Journey.
Limitations:
- Users always have to trigger the first Journey manually to trigger the sequence
- Planners will not show any effect in the sequential triggering though they are correctly established
- Data will not be transferred from one journey to another
- Journeys which are linked to the sequential execution pattern will consume more time.
Comments
0 comments
Please sign in to leave a comment.