Mock service worker with openApi backend - reactjs

I am working with MSW and OpenAPI-backend package.
I want to mock the booth browser server and test server.
I have OpenAPI definition available form with I generate generated.ts for RTK Query (out of scope for this question). I want to use OpenAPI spec to use it with OpenAPI Backend and generate MSW rest worker for browser and for test.
Setup is next:
index.tsx
import worker from './mocks/browser';
if (process.env.NODE_ENV === 'development') {
worker.start();
}
mock/browser.ts
import { setupWorker, rest } from 'msw';
import { OpenAPIBackend } from 'openapi-backend';
import type { Document } from 'openapi-backend';
import definition from './api.json';
// create our mock backend with openapi-backend
const api = new OpenAPIBackend({ definition: definition as Document });
api.register('notFound', (c, res, ctx) => res(ctx.status(404)));
api.registerHandler('notImplemented', async (c, req, res) => {
const { status, mock } = await api.mockResponseForOperation(
c.operation.operationId as string
);
return res.status(status).json(mock);
});
api.register('validationFail', (c, res, ctx) =>
res(ctx.status(400), ctx.json({ error: c.validation.errors }))
);
const worker = setupWorker(
rest.get('/*', (req) =>
api.handleRequest({
...req,
path: req.url.pathname,
headers: req.headers.all(),
method: req.method,
body: req.body,
})
)
);
export default worker;
api.JSON
{
"openapi": "3.0.1",
"info": {
"title": "Fetch API",
"description": "Source of truth for Fetch dashboard",
"version": "0.1.5"
},
"paths": {
"/config": {
"get": {
"tags": [
"Configuration"
],
"summary": "Retreive configuration object",
"description": "Returns configuration object (map) containing configuration parameters for UI (Map<String, String>)",
"responses": {
"200": {
"description": "successfull operation",
"content": {
"application/json": {
"schema": {
"type": "object",
"additionalProperties": {
"type": "string"
},
"description": "Map serialized to json object.",
"example": {
"FA_COLOR": "red",
"FA_NAME": "fetch"
}
}
}
}
}
}
}
},
"/links": {
"get": {
"tags": [
"Notifications & Links",
"Walking Skeleton"
],
"summary": "List all defined links for hospital",
"description": "Retreives all defined links for hospital. Hospital ID is indirectly obtained from user identity.",
"responses": {
"200": {
"description": "successfull operation",
"content": {
"application/json": {
"schema": {
"type": "array",
"items": {
"$ref": "#/components/schemas/Link"
}
}
}
}
}
}
}
},
"/kpis": {
"get": {
"tags": [
"KPIS"
],
"summary": "List all KPIs for hospital(s) that current user is managing.",
"description": "Retreives all KPIs available for hospitals that current user is managing.",
"responses": {
"200": {
"description": "OK",
"content": {
"application/json": {
"schema": {
"type": "array",
"items": {
"$ref": "#/components/schemas/KPI"
}
}
}
}
}
}
}
}
},
"components": {
"schemas": {
"KPI": {
"type": "object",
"properties": {
"id": {
"type": "string",
"enum": [
"revenue",
"labour"
]
},
"hospital_id": {
"type": "string",
"description": "id of hospital that this KPI describes"
},
"goal": {
"type": "number",
"description": "full month goal"
},
"actual": {
"type": "number",
"description": "actual result"
},
"mtd_goal": {
"type": "number",
"description": "month to date goal, so that we can track projected fulfillment of goal."
},
"details": {
"type": "object",
"description": "Semi-structured way of describing details of calculation. Every KPI will potentialiy be described differently."
}
},
"required": [
"id",
"hospital_id",
"goal",
"actual",
"mtd_goal"
]
},
"Link": {
"type": "object",
"properties": {
"id": {
"type": "string",
"description": "unique id of link"
},
"hospital_id": {
"type": "string",
"description": "id of hospital that this Link is configured for"
},
"title": {
"type": "string",
"description": "human readable title for URL"
},
"description": {
"type": "string",
"description": "Description of current link (alt text , or tooltip)"
},
"url": {
"type": "string",
"description": "Location of external resource"
},
"urgent": {
"type": "boolean",
"description": "Is urgency of notification elevated?"
},
"count": {
"type": "number",
"description": "Actual notification value. How many 'tasks' are waiting manager in external system."
},
"updated_at": {
"type": "integer",
"description": "Date/time of last notification update",
"format": "int64"
},
"children": {
"type": "array",
"description": "Since notifications are possibly presented in hierarchy all children of this notification will be gathered here",
"items": {
"$ref": "#/components/schemas/Link"
}
}
},
"required": [
"id",
"hospital_id",
"title",
"url"
],
"example": [
{
"id": 1,
"hospital_id": "001",
"title": "Link1",
"description": "description of Link1",
"url": "https://www.example.com/link1",
"urgent": true,
"count": 1,
"updated_at": 1631113184221,
"children": [
{
"id": 2,
"hospital_id": "001",
"title": "Link2",
"description": "description of Link2",
"url": "https://www.example.com/link2",
"urgent": true,
"count": 1,
"updated_at": 1631113184221
}
]
},
{
"id": 3,
"hospital_id": "002",
"title": "Link3",
"description": "description of Link3",
"url": "https://www.example.com/link3",
"urgent": false,
"count": 2,
"updated_at": 1631113184221
}
]
}
}
}
}
component.tsx
const { data: links, error, isLoading } = useGetLinksQuery({});
Which is fethcing localhost:3000/links
Error I am getting is:
mockServiceWorker.js:222 [MSW] Uncaught exception in the request handler for "GET http://localhost:3000/links":
Error: Unknown operation
at OpenAPIValidator.validateRequest (http://localhost:3000/static/js/vendors~main.chunk.js:63911:13)
at OpenAPIBackend.<anonymous> (http://localhost:3000/static/js/vendors~main.chunk.js:54246:45)
at async OpenAPIBackend.handleRequest (http://localhost:3000/static/js/vendors~main.chunk.js:54152:22)
This exception has been gracefully handled as a 500 response, however, it's strongly recommended to resolve this error, as it indicates a mistake in your code. If you wish to mock an error response, please see this guide: https://mswjs.io/docs/recipes/mocking-error-responses
getResponse # mockServiceWorker.js:222
async function (async)
getResponse # mockServiceWorker.js:175
handleRequest # mockServiceWorker.js:113
async function (async)
handleRequest # mockServiceWorker.js:112
(anonymous) # mockServiceWorker.js:271
Network tabs give:
Request URL: http://localhost:3000/links
Request Method: GET
Status Code: 500 (from service worker)
Referrer Policy: strict-origin-when-cross-origin
All related to article: https://dev.to/epilot/testing-react-with-jest-and-openapi-mocks-8gc
and https://testing-library.com/docs/react-testing-library/example-intro/
Thank you.

In your OpenApi u don't have any operationId.
You need to setup a unique Id by route.
{
"openapi": "3.0.1",
"info": {
"title": "Fetch API",
"description": "Source of truth for Fetch dashboard",
"version": "0.1.5"
},
"paths": {
"/config": {
"get": {
"tags": [
"Configuration"
],
"operationId": "config",
"summary": "Retreive configuration object",
"description": "Returns configuration object (map) containing configuration parameters for UI (Map<String, String>)",
"responses": {
"200": {
"description": "successfull operation",
"content": {
"application/json": {
"schema": {
"type": "object",
"additionalProperties": {
"type": "string"
},
"description": "Map serialized to json object.",
"example": {
"FA_COLOR": "red",
"FA_NAME": "fetch"
}
}
}
}
}
}
}
},
"/links": {
"get": {
"tags": [
"Notifications & Links",
"Walking Skeleton"
],
"operationId": "links",
"summary": "List all defined links for hospital",
"description": "Retreives all defined links for hospital. Hospital ID is indirectly obtained from user identity.",
"responses": {
"200": {
"description": "successfull operation",
"content": {
"application/json": {
"schema": {
"type": "array",
"items": {
"$ref": "#/components/schemas/Link"
}
}
}
}
}
}
}
},
"/kpis": {
"get": {
"tags": [
"KPIS"
],
"summary": "List all KPIs for hospital(s) that current user is managing.",
"description": "Retreives all KPIs available for hospitals that current user is managing.",
"responses": {
"200": {
"description": "OK",
"content": {
"application/json": {
"schema": {
"type": "array",
"items": {
"$ref": "#/components/schemas/KPI"
}
}
}
}
}
}
}
}
},
"components": {
"schemas": {
"KPI": {
"type": "object",
"properties": {
"id": {
"type": "string",
"enum": [
"revenue",
"labour"
]
},
"hospital_id": {
"type": "string",
"description": "id of hospital that this KPI describes"
},
"goal": {
"type": "number",
"description": "full month goal"
},
"actual": {
"type": "number",
"description": "actual result"
},
"mtd_goal": {
"type": "number",
"description": "month to date goal, so that we can track projected fulfillment of goal."
},
"details": {
"type": "object",
"description": "Semi-structured way of describing details of calculation. Every KPI will potentialiy be described differently."
}
},
"required": [
"id",
"hospital_id",
"goal",
"actual",
"mtd_goal"
]
},
"Link": {
"type": "object",
"properties": {
"id": {
"type": "string",
"description": "unique id of link"
},
"hospital_id": {
"type": "string",
"description": "id of hospital that this Link is configured for"
},
"title": {
"type": "string",
"description": "human readable title for URL"
},
"description": {
"type": "string",
"description": "Description of current link (alt text , or tooltip)"
},
"url": {
"type": "string",
"description": "Location of external resource"
},
"urgent": {
"type": "boolean",
"description": "Is urgency of notification elevated?"
},
"count": {
"type": "number",
"description": "Actual notification value. How many 'tasks' are waiting manager in external system."
},
"updated_at": {
"type": "integer",
"description": "Date/time of last notification update",
"format": "int64"
},
"children": {
"type": "array",
"description": "Since notifications are possibly presented in hierarchy all children of this notification will be gathered here",
"items": {
"$ref": "#/components/schemas/Link"
}
}
},
"required": [
"id",
"hospital_id",
"title",
"url"
],
"example": [
{
"id": 1,
"hospital_id": "001",
"title": "Link1",
"description": "description of Link1",
"url": "https://www.example.com/link1",
"urgent": true,
"count": 1,
"updated_at": 1631113184221,
"children": [
{
"id": 2,
"hospital_id": "001",
"title": "Link2",
"description": "description of Link2",
"url": "https://www.example.com/link2",
"urgent": true,
"count": 1,
"updated_at": 1631113184221
}
]
},
{
"id": 3,
"hospital_id": "002",
"title": "Link3",
"description": "description of Link3",
"url": "https://www.example.com/link3",
"urgent": false,
"count": 2,
"updated_at": 1631113184221
}
]
}
}
}
}

Related

hide show widget conditionally in form using react-jsonschema-form liberary

I have created a form using below json configuration for react-jsonschema-form
{
"schema": {
"title": "Employee Detail",
"type": "object",
"required": ["name", "email", "country"],
"properties": {
"name": {
"type": "string",
"title": "Name",
"default": "",
"autoComplete": "off"
},
"email": {
"type": "string",
"title": "Email",
"default": ""
},
"registeredAddress": {
"type": "object",
"title": "Registered Address",
"properties": {
"line1": {
"type": "string",
"title": "Address Line1",
"default": ""
},
"line2": {
"type": "string",
"title": "Address Line2",
"default": ""
},
"city": {
"type": "string",
"title": "City",
"default": ""
},
"postal": {
"type": "string",
"title": "Postal",
"default": ""
},
"country": {
"type": "string",
"title": "Country",
"default": "",
"enum": ["IN", "PK", "CN"],
"enumNames": ["India", "Pakistan", "China"]
}
}
},
"isAddressDifferent": {
"type": "boolean",
"title": "Is Mailing Address different from Registered Address",
"default": false
},
"about": {
"type": "string",
"title": "About",
"default": "",
"autoComplete": "off"
}
},
"if": {
"properties": {
"isAddressDifferent": {
"const": true
}
}
},
"then": {
"properties": {
"mailingAddress": {
"type": "object",
"title": "Mailing Address",
"properties": {
"line1": {
"type": "string",
"title": "Address Line1",
"default": ""
}
}
}
}
}
}
}
I am hiding mailingAddress property on the basis of isAddressDifferent checkbox value, but I want to position mailingAddress on ui after that checkbox and above the about ui widget
Can anyone help me on this?
Below is the codesandbox url for code
https://codesandbox.io/s/rjfs-form-demo-mix96s?file=/src/custom-form/form-config.json

Power automate - Custom connector - Webhook trigger with dynamic schema properties

I'm trying to create a webhook trigger in Power Automate via the OpenApi spec.
The webhook url must be in the body and it's required, but I also want to have dynamic properties in the body aswell. So far I have this:
"/webhook/triggers/{entityType}": {
"x-ms-notification-content": {
"description": "When a operation is performed on a entity",
"x-ms-summary": "When a operation is performed on a entity",
"schema": {
"type": "object",
"properties": {
"data": {
"type": "object",
"x-ms-dynamic-schema": {
"operationId": "GetDynamicSchema",
"parameters": {
"entityType": {
"parameter": "entityType"
}
}
}
},
"id": {
"type": "string"
},
"source": {
"type": "string"
},
"type": {
"type": "string"
},
"time": {
"format": "date-time",
"type": "string"
},
"dataSchema": {
"type": "string"
},
"dataContentType": {
"type": "string"
},
"extensionAttributes": {
"type": "object",
"additionalProperties": {
"type": "object"
}
}
}
}
},
"post": {
"description": "When a operation is performed on a entity",
"x-ms-trigger": "single",
"x-ms-visibility": "important",
"summary": "When a operation is performed on a entity",
"operationId": "RegisterTriggerWithEntity",
"consumes": ["application/json"],
"produces": ["application/json"],
"parameters": [
{
"name": "entityType",
"x-ms-summary": "Entity",
"description": "The entity",
"x-ms-url-encoding": "single",
"in": "path",
"required": true,
"type": "string",
"x-ms-dynamic-values": {
"operationId": "GetEntityTypes",
"value-path": "name",
"value-title": "description"
}
},
{
"name": "body",
"in": "body",
"required": true,
"schema": {
"type": "object",
"properties": {
"webhookUrl": {
"type": "string",
"x-ms-notification-url": true,
"x-ms-visibility": "internal"
},
"data": {
"type": "object",
"x-ms-dynamic-schema": {
"operationId": "GetSchema",
"parameters": {
"entityType": {
"parameter": "entityType"
}
}
},
"x-ms-dynamic-properties": {
"operationId": "GetSchema",
"parameters": {
"entityType": {
"parameterReference": "entityType"
}
}
}
}
},
"required": [
"webhookUrl"
],
"x-ms-visibility": "important"
}
}
],
"responses": {
"201": {
"description": "Created"
},
"401": {
"description": "Unauthorized"
},
"403": {
"description": "Forbidden"
}
}
}
}
Which passes validation, I also have the schema properties prompted in the UI ready to be provided by the user, but on saving the flow, I get the following OpenApiOperationParameterValidationFailed:
Flow save failed with code 'OpenApiOperationParameterValidationFailed' and message 'Input parameter 'body' validation failed in workflow operation 'When_a_operation_is_performed_on_a_entity': The API operation 'RegisterTriggerWithEntity' requires the property 'body/data' to be set to one of its defined enum values '[]' but is set to '{ "firstProperty": "testValue" }'.'.
What might be the problem with this flow? What is the suggested way of having a webhook trigger with dynamic schema properties?

400 Bad Request on Alexa AddOrUpdateReport using JSON from documentation

My issue is basically described in title but I'll add a little bit more details here.
I'm trying to proactively send an update report to Alexa's Event Hub for Smart Home skills, however this fails with a 400 Bad Request error, and this is what the server responds:
{
"header": {
"namespace": "System",
"name": "Exception",
"messageId": "a154410c-2364-4c5b-9028-accde5048e1e"
},
"payload": {
"code": "INVALID_REQUEST_EXCEPTION",
"description": "Event or endpoint is missing in the request."
}
}
I decided then to check if my request was wrong by sending the one that can be found on their documentation, and I'm getting the very same error.
This is the JSON copied from Amazon's documentation (I just replaced device metadata and the token):
{
"event": {
"header": {
"namespace": "Alexa.Discovery",
"name": "AddOrUpdateReport",
"payloadVersion": "3",
"messageId": "5f8a426e-01e4-4cc9-8b79-65f8bd0fd8a4"
},
"payload": {
"endpoints": [
{
"endpointId": "<unique ID of the endpoint>",
"manufacturerName": "Sample Manufacturer",
"description": "Smart Light by Sample Manufacturer",
"friendlyName": "Kitchen Light",
"additionalAttributes": {
"manufacturer" : "Sample Manufacturer",
"model" : "Sample Model",
"serialNumber": "<the serial number of the device>",
"firmwareVersion" : "<the firmware version of the device>",
"softwareVersion": "<the software version of the device>",
"customIdentifier": "<your custom identifier for the device>"
},
"displayCategories": [
"LIGHT"
],
"capabilities": [
{
"type": "AlexaInterface",
"interface": "Alexa.PowerController",
"version": "3",
"properties": {
"supported": [
{
"name": "powerState"
}
],
"proactivelyReported": true,
"retrievable": true
}
},
{
"type": "AlexaInterface",
"interface": "Alexa.BrightnessController",
"version": "3",
"properties": {
"supported": [
{
"name": "brightness"
}
],
"proactivelyReported": true,
"retrievable": true
}
}
],
"connections": [
],
"cookie": {
}
}
],
"scope": {
"type": "BearerToken",
"token": "access-token-from-Amazon"
}
}
}
}
What I don't understand is why it reports that event or endpoints are missing if they're clearly there and if this JSON is the one that they provide in their documentation.
Does anyone have this issue too?
EDIT: pasting my payload as requested.
{
"event": {
"header": {
"namespace": "Alexa.Discovery",
"name": "AddOrUpdateReport",
"payloadVersion": "3",
"messageId": "132137185729061389"
},
"payload": {
"endpoints": [
{
"endpointId": "Rmxvb2RTZW5zb3JfMDE=",
"friendlyName": "Flood sensor",
"description": "Flood sensor",
"manufacturerName": "MyCompany",
"displayCategories": [
"ACTIVITY_TRIGGER"
],
"cookie": {
"DeviceType": "FloodSensor"
},
"capabilities": [{
"interface": "Alexa",
"type": "AlexaInterface",
"version": "3"
},
{
"interface": "Alexa.EndpointHealth",
"type": "AlexaInterface",
"version": "3",
"properties": {
"supported": [{
"name": "connectivity"
}],
"proactivelyReported": true,
"retrievable": true
}
}
]
}
],
"scope": {
"type": "BearerToken",
"token": "<redacted>"
}
}
}
}

Send workflow information to custom connector

I need help with sending workflow information in header/body of calls to custom connector. I am trying to load a drop down list in one of the parameters of a logic app using values returned from an API call. The API end point requires basic workflow information such as the resource group and workflow name which are normally available in headers of http requests from logic app execution.
Normally when I use #{workflow().name} in logic app's json, it is substituted with the workflow name. In case of custom connector, the WDL syntax is passed as is without any transformation.
Here is a simplified swagger json with all relevant sections.
{
"swagger": "2.0",
"info": {
"title": "{{dynamicHostName}}",
"version": "1.0.0"
},
"host": "{{dynamicHostName}}",
"basePath": "/",
"schemes": [
"https"
],
"paths": {
"/sftpsource": {
"post": {
"operationId": "SftpSource",
"summary": "Sftp as source system",
"description": "Use Sftp as source system",
"produces": [
"application/json"
],
"consumes": [
"application/json"
],
"parameters": [
{
"name": "params",
"in": "body",
"required": true,
"schema": {
"type": "object",
"properties": {
"hostName": {
"type": "string",
"x-ms-summary": "Host Name",
"x-ms-visibility": "advanced"
},
"portNumber": {
"type": "string",
"x-ms-summary": "Port Number",
"x-ms-visibility": "advanced"
},
"userName": {
"type": "string",
"x-ms-summary": "User Name",
"x-ms-visibility": "advanced"
},
"password": {
"type": "string",
"x-ms-summary": "Password",
"x-ms-visibility": "advanced"
},
"filePath": {
"type": "string",
"x-ms-summary": "File Path"
},
"system": {
"type": "string",
"x-ms-visibility": "advanced",
"x-ms-summary": "System",
"x-ms-dynamic-values": {
"operationId": "GetTaggedSystems",
"parameters": {
"workflow-name": "#{workflow().name}"
},
"value-path": "systemId",
"value-title": "systemName"
}
}
}
}
}
],
"responses": {
"202": {
"description": "Request is queued"
},
"500": {
"description": "Server Error"
}
}
}
},
"/taggedSystems" : {
"get": {
"operationId": "GetTaggedSystems",
"summary": "Tagged Systems",
"x-ms-visibility": "advanced",
"description": "Get all systems tagged to this flow",
"parameters": [
{
"name": "workflow-name",
"in": "header",
"required": true,
"type": "string"
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/TaggedSystems"
}
},
"202": {
"description": "Work is still in progress"
},
"500": {
"description": "An error occured while trying to fetch tagged systems."
}
}
}
}
},
"definitions": {
"TaggedSystems": {
"type": "array",
"items": {
"type": "object",
"properties": {
"systemId": {
"type": "string"
},
"systemName": {
"type": "string"
}
},
"required": [
"systemId",
"systemName"
]
}
}
}
}
You can have an internal parameter defined as header/body where you can pass dynamic expressions of what you need from the workflow environment at the time of execution of the flow.
For complete information regarding the flow, you can pass #{workflow()} as an internal parameter.
Hope it helps.

Json schema for array of objects doesn't validate

I have this schema for a json response
{
"title": "Products",
"description": "schema for products",
"type": "array",
"properties": {
"id": {
"description": "id of a product",
"type": "integer"
},
"name": {
"description": "name of the product",
"type": "string"
},
"created_at": {
"description": "record created_at",
"type": "string",
"format": "date-time"
},
"updated_at": {
"description": "record updated_at",
"type": "string",
"format": "date-time"
}
},
"required": ["id", "name"]
}
and I want to match this schema with this json
[{
"id": 1,
"name": "Cricket Ball"
}, {
"id": 2,
"name": "Soccer Ball"
}, {
"id": 3,
"name": "football ball"
}, {
"id": 4,
"name": "Basketball ball"
}, {
"id": 5,
"name": "Table Tennis ball"
}, {
"id": 6,
"name": "Tennis ball"
}]
This schema matches the response but it also matches the schema in which the required field is this
"required": ["ids", "names"]
I think the schema is validated against the array and the objects in the array are not validated.
The way you have it set up now, your properties key refers to the array itself, not to each item, and is being ignored (because arrays don't have properties, they just have items). You need to use the items key to validate each item in the array, like so:
{
"title": "Products",
"description": "schema for products",
"type": "array",
"items": {
"type": "object",
"properties": {
"id": {
"description": "id of a product",
"type": "integer"
},
"name": {
"description": "name of the product",
"type": "string"
},
"created_at": {
"description": "record created_at",
"type": "string",
"format": "date-time"
},
"updated_at": {
"description": "record updated_at",
"type": "string",
"format": "date-time"
}
},
"required": ["id", "name"]
}
}
try map
new_array = response.map{ |k| { 'id': k['properties']['id']['description'], 'name': k['properties']['name']['description'] } }

Resources