How to extract content/text of an attachment from an MS Teams messaged generated by a webhook? - microsoft-teams

Automation #1 is ServiceNow > webhook > MS Teams > message.
Automation #2 is Power Automate > When keywords are mention (trigger) > read message from automation #1 > Post message in chat or channel
Problem: I am unable to workout the expression needed to read the content from the message body marked as the 2nd of the 3 blocks below i.e. "content":
"body": {
"contentType": "html",
"content": "<attachment id=\"x\"></attachment>",
"plainTextContent": ""
},
"channelIdentity": {
"teamId": "x",
"channelId": "x"
},
"attachments": [
{
"id": "x",
"contentType": "application/vnd.microsoft.teams.card.o365connector",
"contentUrl": null,
"content": "{\r\n \"summary\": \"P1/2 incident next action overdue [xx_NAO_P1-2]\",\r\n \"title\": \"P1/2 incident next action overdue [xx_NAO_P1-2]\",\r\n \"themeColor\": \"ff0000\",\r\n \"sections\": [\r\n {\r\n \"text\": \"Attn: <strong>name_here</strong> (email_here) \\n</br></br>\\nThe next action for case <strong>case_number</strong> is <span style=\\\"color:red\\\"><strong>over due</strong></span>. The stated action was:\\n</br></br>\\n<span style=\\\"color:blue\\\">This is the next action for case_number.</span>\",\r\n \"title\": \"\",\r\n \"activityTitle\": \"\",\r\n \"activitySubtitle\": \"\",\r\n \"activityText\": \"\",\r\n \"markdown\": false,\r\n \"startGroup\": false\r\n },\r\n {\r\n \"markdown\": true,\r\n \"startGroup\": false\r\n }\r\n ],\r\n \"potentialAction\": []\r\n}",
"name": null,
"thumbnailUrl": null
}
],

You can use the Body Content property for this.
Below is an example of this.
Btw, with an expression that would be:
outputs('Get_message_details')?['body/body/content']

Okay, this is my first ever Power Automate flow, so its not pretty. I think "Compose 2" and "Compose 3" are not required. If there are any suggestions on how to eliminate the need for 2x "Parse JSON" steps I am all ears. If there is a way to bypass the JSON steps altogether, even better.
I hope my experience, can help someone else.
Power Automate – Trigger
When keywords are mentioned
Message type: Channel
Keywords to search for: “[my keyword]”
Team: [my channel]
Channel – 1: General
Power Automate – Actions
Initialize variable – msg email
Name: msg_email
Type: String
Value: [empty]
Initialize variable – msg caseNumber
Name: msg_caseNumber
Type: String
Value: [empty]
Initialize variable – msg nextAction
Name: msg_nextAction
Type: String
Value: [empty]
Apply to each
Select an output from previous steps: Value = triggerOutputs()?[‘body/value’]
Get message details
Message: Message ID = items(‘Apply_to_each’)?[messageId’]
Message type: Channel
Team: Team ID = items(‘Apply_to_each’)?[‘teamId’]
Channel: Channel ID = items(‘Apply_to_each’)?[‘channelId’]
Parent message ID = [empty]
Parse JSON
Content: Body = body(‘Get_message_details’)
Schema: [I took the output of the message and generated a schema]
{
"type": "object",
"properties": {
"##odata.context": {
"type": "string"
},
"id": {
"type": "string"
},
"replyToId": {},
"etag": {
"type": "string"
},
"messageType": {
"type": "string"
},
"createdDateTime": {
"type": "string"
},
"lastModifiedDateTime": {
"type": "string"
},
"lastEditedDateTime": {},
"deletedDateTime": {},
"subject": {},
"summary": {
"type": "string"
},
"chatId": {},
"importance": {
"type": "string"
},
"locale": {
"type": "string"
},
"webUrl": {
"type": "string"
},
"policyViolation": {},
"eventDetail": {},
"from": {
"type": "object",
"properties": {
"device": {},
"user": {},
"application": {
"type": "object",
"properties": {
"id": {
"type": "string"
},
"displayName": {
"type": "string"
},
"applicationIdentityType": {
"type": "string"
}
}
}
}
},
"body": {
"type": "object",
"properties": {
"contentType": {
"type": "string"
},
"content": {
"type": "string"
},
"plainTextContent": {
"type": "string"
}
}
},
"channelIdentity": {
"type": "object",
"properties": {
"teamId": {
"type": "string"
},
"channelId": {
"type": "string"
}
}
},
"attachments": {
"type": "array",
"items": {
"type": "object",
"properties": {
"id": {
"type": "string"
},
"contentType": {
"type": "string"
},
"contentUrl": {},
"content": {
"type": "string"
},
"name": {},
"thumbnailUrl": {}
},
"required": [
"id",
"contentType",
"contentUrl",
"content",
"name",
"thumbnailUrl"
]
}
},
"mentions": {
"type": "array"
},
"reactions": {
"type": "array"
},
"messageLink": {
"type": "string"
},
"threadType": {
"type": "string"
},
"teamId": {
"type": "string"
},
"channelId": {
"type": "string"
}
}
}
Apply to each 3
Select an output from previous steps: attachments = body(‘Parse_JSON’)?[‘attachments’]
Compose 2
Inputs: content x = items(‘Apply_to_each_3’)?[‘content’]
Parse JSON 2
Content: Outputs x = outputs(‘Compose_2’)
Schema: [I took the output of the attachment item and generated a schema]
{
"type": "object",
"properties": {
"summary": {
"type": "string"
},
"title": {
"type": "string"
},
"themeColor": {
"type": "string"
},
"sections": {
"type": "array",
"items": {
"type": "object",
"properties": {
"text": {
"type": "string"
},
"title": {
"type": "string"
},
"activityTitle": {
"type": "string"
},
"activitySubtitle": {
"type": "string"
},
"activityText": {
"type": "string"
},
"markdown": {
"type": "boolean"
},
"startGroup": {
"type": "boolean"
}
},
"required": [
"markdown",
"startGroup"
]
}
},
"potentialAction": {
"type": "array"
}
}
}
Compose 3
Inputs: bodu(…) x = body(‘Parse_JSON_2’)[‘sections’][0][‘text’]
Note: after Compose 3 there are 3 parallel branches to create 3 variables which then converge back at Get an #mention token for a user.
1. Branch for variable msg_email
Find text position “(“
Text: Outputs x = outputs(‘Compose_3’)
Search Text: (
Substring email
Text: Outputs x = outputs(‘Compose_3’)
Starting Position: add(…) x = add(int(outputs('Find_text_position_"("')?['body']),1)
Length: sub(…) x = sub(sub(int(outputs('Find_text_position_")"')?['body']),int(outputs('Find_text_position_"("')?['body'])),1)
Set variable msg email
Name: msg_email
Value: Body x = body(‘Substring_email’)
2. Branch for variable msg_caseNumber
Find text position “[case prefix]“
Text: Outputs x = outputs(‘Compose_3’)
Search Text: [case prefix]
Substring caseNumber
Text: Outputs x = outputs(‘Compose_3’)
Starting Position: Body x = body(‘Find_text_position_”[case prefix]”’)
Length: 11
Set variable msg caseNumber
Name: msg_caseNumber
Value: Body x = body(‘Substring_caseNumber’)
3. Branch for variable msg_nextAction
Find text position “colour:blue“
Text: Outputs x = outputs(‘Compose_3’)
Search Text: color:blue
Substring nextAction+end tag
Text: Outputs x = outputs(‘Compose_3’)
Starting Position: add(…) x = add(12,int(outputs('Find_text_position_"color:blue"')?['body']))
Length: [empty]
Substring nextAction
Text: Body x = body(‘Substring_nextAction+end_tag’)
Starting Position: 0
Length: sub(…) x = sub(length(outputs('Substring_nextAction+end_tag')?['body']),7)
Set variable msg nextAction
Name: msg_nextAction
Value: Body = body(‘Substring_nextAction’)
Get an #mention token for a user
User: variables(‘msg_email’)
Reply with a message in a channel
Post as: Flow bot
Post in: Channel
Message ID: outputs(‘Get_message_deatils’)?[‘body/id’]
Channel: Channel ID = outputs(‘Get_message_details’)?[‘body/channelId’]
Message:
<p>#{outputs('Get_an_#mention_token_for_a_user')?['body/atMention']} Case #{variables('msg_caseNumber')} has an <span style="color: red"><strong>overdue Next Action</strong></span>. The stated action was:.<br>
<br>
<span style="color: blue"></span><span style="color: blue">#{variables('msg_nextAction')}</span><span style="color: blue"></span></p>

Related

Is there a better/faster way to insert data to a database from an external api in Laravel?

I am currently getting data from an external API for use my in Laravel API. I have everything working but I feel like it is slow.
I'm getting the data from the API with Http:get('url) and that works fast. It is only when I start looping through the data and making edits when things are slowing down.
I don't need all the data, but it would still be nice to edit before entering the data to the database as things aren't very consitent if possible. I also have a few columns that use data and some logic to make new columns so that each app/site doesn't need to do it.
I am saving to the database on each foreach loop with the eloquent Model:updateOrCreate() method which works but these json files can easily be 6000 lines long or more so it obviously takes time to loop through each set modify values and then save to the database each time. There usually isn't more than 200 or so entries but it still takes time. Will probably eventually update this to the new upset() method to make less queries to the database. Running in my localhost it is currently take about a minute and a half to run, which just seams way too long.
Here is a shortened version of how I was looping through the data.
$json = json_decode($contents, true);
$features = $json['features'];
foreach ($features as $feature){
// Get ID
$id = $feature['id'];
// Get primary condition data
$geometry = $feature['geometry'];
$properties = $feature['properties'];
// Get secondary geometry data
$geometryType = $geometry['type'];
$coordinates = $geometry['coordinates'];
Model::updateOrCreate(
[
'id' => $id,
],
[
'coordinates' => $coordinates,
'geometry_type' => $geometryType,
]);
}
Most of what I'm doing behind the scenes to the data before going into the database is cleaning up some text strings but there are a few logic things to normalize or prep the data for websites and apps.
Is there a more efficient way to get the same result? This will ultimately be used in a scheduler and run on an interval.
Example Data structure from API documentation
{
"$schema": "http://json-schema.org/draft-04/schema#",
"additionalProperties": false,
"properties": {
"features": {
"items": {
"additionalProperties": false,
"properties": {
"attributes": {
"type": [
"object",
"null"
]
},
"geometry": {
"additionalProperties": false,
"properties": {
"coordinates": {
"items": {
"items": {
"type": "number"
},
"type": "array"
},
"type": "array"
},
"type": {
"type": "string"
}
},
"required": [
"coordinates",
"type"
],
"type": "object"
},
"properties": {
"additionalProperties": false,
"properties": {
"currentConditions": {
"items": {
"properties": {
"additionalData": {
"type": "string"
},
"conditionDescription": {
"type": "string"
},
"conditionId": {
"type": "integer"
},
"confirmationTime": {
"type": "integer"
},
"confirmationUserName": {
"type": "string"
},
"endTime": {
"type": "integer"
},
"id": {
"type": "integer"
},
"sourceType": {
"type": "string"
},
"startTime": {
"type": "integer"
},
"updateTime": {
"type": "integer"
}
},
"required": [
"id",
"userName",
"updateTime",
"startTime",
"conditionId",
"conditionDescription",
"confirmationUserName",
"confirmationTime",
"sourceType",
"endTime"
],
"type": "object"
},
"type": "array"
},
"id": {
"type": "string"
},
"name": {
"type": "string"
},
"nameId": {
"type": "string"
},
"parentAreaId": {
"type": "integer"
},
"parentSubAreaId": {
"type": "integer"
},
"primaryLatitude": {
"type": "number"
},
"primaryLongitude": {
"type": "number"
},
"primaryMP": {
"type": "number"
},
"routeId": {
"type": "integer"
},
"routeName": {
"type": "string"
},
"routeSegmentIndex": {
"type": "integer"
},
"secondaryLatitude": {
"type": "number"
},
"secondaryLongitude": {
"type": "number"
},
"secondaryMP": {
"type": "number"
},
"sortOrder": {
"type": "integer"
}
},
"required": [
"id",
"name",
"nameId",
"routeId",
"routeName",
"primaryMP",
"secondaryMP",
"primaryLatitude",
"primaryLongitude",
"secondaryLatitude",
"secondaryLongitude",
"sortOrder",
"parentAreaId",
"parentSubAreaId",
"routeSegmentIndex",
"currentConditions"
],
"type": "object"
},
"type": {
"type": "string"
}
},
"required": [
"type",
"geometry",
"properties",
"attributes"
],
"type": "object"
},
"type": "array"
},
"type": {
"type": "string"
}
},
"required": [
"type",
"features"
],
"type": "object"
}
Second, related question.
Since this is being updated on an interval I have it updating and creating records from the json data, but is there an efficient way to delete old records that are no longer in the json file? I currently get an array of current ids and compare them to the new ids and then loop through each and delete them. There has to be a better way.
Have no idea what to say to your first question, but I think you may try to do something like this regarding the second question.
SomeModel::query()->whereNotIn('id', $newIds)->delete();
$newIds you can collect during the first loop.

VSC validation with $schema and usage of additionalProperties = false

i have a dataobject.json and a corresponding example.json. I like to compare both if everything what is in the example is the same notation as in the dataobject.
I added in the exampe the dataobject file as a schema to validate against. This works, but only for the required field, not for the optional properties. - there the validation doesn't find a problem, even if there are some.
To validate these I added the "additionalProperties": false line. This works in general, so I find all the deviations, but I also get a problem that property §schema is not allowed.
How can I solve this?
the dataobject
{
"$schema": "http://json-schema.org/draft-07/schema",
"type": "object",
"title": "GroupDO",
"required": [
"id",
"name"
],
"additionalProperties": false,
"properties": {
"id": {
"type": "string",
"format": "uuid",
"example": "5dd6c80a-3376-4bce-bc47-8t41b3565325",
"description": "Unique id ."
},
"name": {
"type": "string",
"example": "ABD",
"description": "The name."
},
"GroupSort": {
"type": "integer",
"format": "int32",
"example": 1,
"description": "Defines in which order the groups should appear."
},
"GroupTextList": {
"type": "array",
"description": "A descriptoin in multiple languages.",
"items": {
"$ref": "../../common/dataobjects/Description_1000_DO.json"
}
},
"parentGroupId": {
"type": "string",
"format": "uuid",
"example": "8e590f93-1ab6-40e4-a5f4-aa1eeb2b6a80",
"description": "Unique id for the parent group."
}
},
"description": "DO representing a group object. "}
the example
{ "$schema": "../dataobjects/GroupDO.json",
"id": "18694b46-0833-4790-b780-c7897ap08500",
"version": 1,
"lastChange": "2020-05-12T13:57:39.935305",
"sort": 3,
"name": "STR",
"parentGroupId": "b504273e-61fb-48d1-aef8-c289jk779709",
"GroupTexts": [
{
"id": "7598b668-d9b7-4d27-a489-19e45h2bdad0",
"version": 0,
"lastChange": "2020-03-09T14:14:25.491787",
"languageIsoCode": "de_DE",
"description": "Tasche"
},
{
"id": "376e82f8-837d-4bb2-a21f-a9e0ebd59e23",
"version": 0,
"lastChange": "2020-03-09T14:14:25.491787",
"languageIsoCode": "en_GB",
"description": "Bag"
}
]
}
the problem messages:
property $schema is not allowed
Thanks in advance for your help.

Validating yaml file by json scheme in monaco editor gives incorrect error

monaco.languages.json.jsonDefaults.setDiagnosticsOptions({
validate: true,
schemas: \[
{
"$schema": "http://json-schema.org/draft-06/schema#",
"$ref": "#/definitions/Welcome1",
"definitions": {
"Welcome1": {
"type": "object",
"additionalProperties": false,
"properties": {
"inventory": {
"type": "array",
"items": {
"$ref": "#/definitions/Inventory"
}
}
},
"required": \[
"inventory"
\],
"title": "Welcome1"
},
"Inventory": {
"type": "object",
"additionalProperties": false,
"properties": {
"devices": {
"$ref": "#/definitions/Devices"
},
"pollfrequency": {
"type": "integer"
},
"scopedinventoryobject": {
"type": "string"
}
},
"required": \[
"devices",
"pollfrequency",
"scopedinventoryobject"
\],
"title": "Inventory"
},
"Devices": {
"type": "object",
"additionalProperties": false,
"properties": {
"platformtypes": {
"type": "array",
"items": {
"type": "string"
}
}
},
"required": \[
"platformtypes"
\],
"title": "Devices"
}
}
}
\]
});
Providing a screenshot for incorrect validation that i have. I haven't found a guide to use a monaco-yaml , so I am just trying to validate by json schema that i have for the file. Is it the right way for yaml validation in monaco editor? Or should i implement different approach right here?
I am facing the exact same problem. From what I understand when we set the schema to: monaco.languages.json.jsonDefaults.setDiagnosticsOptions
The schema is only referenced to the json language declared in model.
So, you are even defining the schema, but you are using a different language in the model. (Even so, the print error seems to be referenced to json)
I'm trying to use the package monaco-yaml because it seems that it already implements the validation

Add images via Shopware 6 API

I have a Shopware 6.3 shop and need to migrate images to it using the integration API.
How should I construct a body for a media upload? Do I need to put a file somewhere or just pass in the link?
I have managed to push new products into Shopware via guide here: https://docs.shopware.com/en/shopware-platform-dev-en/admin-api-guide/writing-entities?category=shopware-platform-dev-en/admin-api-guide#creating-entities but I am not sure how to handle media. In this guide it is only explained how to create links between already uploaded media files to products in here https://docs.shopware.com/en/shopware-platform-dev-en/admin-api-guide/writing-entities?category=shopware-platform-dev-en/admin-api-guide#media-handling but no examples as to how to actually push the media files.
I have URL's for each image I need (in the database, along with produc id's and image positions).
The entity schema describes media as:
"media": {
"name": "media",
"translatable": [
"alt",
"title",
"customFields"
],
"properties": {
"id": {
"type": "string",
"format": "uuid"
},
"userId": {
"type": "string",
"format": "uuid"
},
"mediaFolderId": {
"type": "string",
"format": "uuid"
},
"mimeType": {
"type": "string",
"readOnly": true
},
"fileExtension": {
"type": "string",
"readOnly": true
},
"uploadedAt": {
"type": "string",
"format": "date-time",
"readOnly": true
},
"fileName": {
"type": "string",
"readOnly": true
},
"fileSize": {
"type": "integer",
"format": "int64",
"readOnly": true
},
"metaData": {
"type": "object",
"readOnly": true
},
"mediaType": {
"type": "object",
"readOnly": true
},
"alt": {
"type": "string"
},
"title": {
"type": "string"
},
"url": {
"type": "string"
},
"hasFile": {
"type": "boolean"
},
"private": {
"type": "boolean"
},
"customFields": {
"type": "object"
},
"createdAt": {
"type": "string",
"format": "date-time",
"readOnly": true
},
"updatedAt": {
"type": "string",
"format": "date-time",
"readOnly": true
},
"translated": {
"type": "object"
},
"tags": {
"type": "array",
"entity": "tag"
},
"thumbnails": {
"type": "array",
"entity": "media_thumbnail"
},
"user": {
"type": "object",
"entity": "user"
},
"categories": {
"type": "array",
"entity": "category"
},
"productManufacturers": {
"type": "array",
"entity": "product_manufacturer"
},
"productMedia": {
"type": "array",
"entity": "product_media"
},
"avatarUser": {
"type": "object",
"entity": "user"
},
"mediaFolder": {
"type": "object",
"entity": "media_folder"
},
"propertyGroupOptions": {
"type": "array",
"entity": "property_group_option"
},
"mailTemplateMedia": {
"type": "array",
"entity": "mail_template_media"
},
"documentBaseConfigs": {
"type": "array",
"entity": "document_base_config"
},
"shippingMethods": {
"type": "array",
"entity": "shipping_method"
},
"paymentMethods": {
"type": "array",
"entity": "payment_method"
},
"productConfiguratorSettings": {
"type": "array",
"entity": "product_configurator_setting"
},
"orderLineItems": {
"type": "array",
"entity": "order_line_item"
},
"cmsBlocks": {
"type": "array",
"entity": "cms_block"
},
"cmsSections": {
"type": "array",
"entity": "cms_section"
},
"cmsPages": {
"type": "array",
"entity": "cms_page"
},
"documents": {
"type": "array",
"entity": "document"
}
}
},
but it is not clear what fields are crucial. Do I need to create product-media folder first and then use it's id when making a POST request to media endpoint? Can I just specify the URL and will Shopware download the image itself to a folder or keep pointing to the URL I have used. I need to house the images inside the Shopware.
There is no problem for me to download the images from the URL and push them to Shopware but I am not sure how to use the API for it (there is a lot of images and they need to be done in bulk).
One possible solution:
FIRST: create a new media POST /api/{apiVersion}/media?_response=true
SECOND: "Upload Image" /api/{apiVersion}/_action/media/{mediaId}/upload?extension={extension}&fileName={imgName}&_response=true
more information can be found here: https://forum.shopware.com/discussion/comment/278603/#Comment_278603
In CASE images are for products use the endpoint POST /api/{apiVersion}/product-media and set the coverId
A complete listing of all routes is available via the OpenAPI schema: [your-domain/localhost]/api/v3/_info/openapi3.json
It's also possible to set all the media and the cover & coverId during product creation by one request. Therefore, set the product Cover and product Media
{
"coverId":"3d5ebde8c31243aea9ecebb1cbf7ef7b",
"productNumber":"SW10002","active":true,"name":"Test",
"description":"fasdf",
"media":[{
"productId":"94786d894e864783b546fbf7c60a3640",
"mediaId":"084f6aa36b074130912f476da1770504",
"position":0,
"id":"3d5ebde8c31243aea9ecebb1cbf7ef7b"
},
{
"productId":"94786d894e864783b546fbf7c60a3640",
"mediaId":"4923a2e38a544dc5a7ff3e26a37ab2ae",
"position":1,
"id":"600999c4df8b40a5bead55b75efe688c"
}],
"id":"94786d894e864783b546fbf7c60a3640"
}
Keep in mind to check if the bearer token is valid by checking for example like this:
if (JwtToken.ValidTo >= DateTime.Now.ToUniversalTime() - new TimeSpan(0, 5, 0))
{
return Client.Get(request);
}
else
{
// refresh the token by new authentication
IntegrationAuthenticator(this.key, this.secret);
}
return Client.Get(request);
This will work for Shopware 6.4
As a general advice, it depends. The APIs changed a little bit since 6.4 and there is also an official documentation available at https://shopware.stoplight.io/docs/admin-api/docs/guides/media-handling.md.
However, i think that it is always a little easier to have a real life example. What i do in our production environment is basically these steps.
(Optional) Check, if the media object exists
Create an media-file object using the endpoint GET /media-files/
If it exist then upload an image using the new media-id reference.
Let us assume the filename is yourfilename.jpg. What you also will need is a media-folder-id, which will reference the image-folder within Shopware. This can be obtained in Shopware via Admin > Content > Media > Product Media.
Step 0
Before uploading an image to Shopware, you want to ensure that the image does not exists, so that you can skip it.
This step is optional, as it is not mandatory to create an image. However you want to have some sort of validation mechanism in a production environment.
Request-Body
POST api/search/media
This will run a request against the Shopware-API with a response.
{
"filter":[
{
"type":"equals",
"field":"fileName",
"value":"yourfilename"
},
{
"type":"equals",
"field":"fileExtension",
"value":"jpg"
},
{
"type":"equals",
"field":"mediaFolderId",
"value":"d798f70b69f047c68810c45744b43d6f"
}
],
"includes":{
"media":[
"id"
]
}
}
Step 1
Create a new media-file
Request-Body
POST api/_action/sync
This request will create a new media-object in Shopware.
The value for media_id must be any UUID. I will use this value: 94f83a75669647288d4258f670a53e69
The customFields property is optional. I just use it to keep a reference of hash value which i could use to validate changed values.
The value for the media folder id is the one you will get from your Shopware-Backend.
{
"create-media": {
"entity": "media",
"action": "upsert",
"payload": [
{
"id": "{{media_id}}",
"customFields": {"hash": "{{file.hash}}"},
"mediaFolderId": "{{mediaFolderId}}"
}
]
}
}
Response
The response will tell you that everything works as expected.
{
"success":true,
"data":{
"create-media":{
"result":[
{
"entities":{
"media":[
"94f83a75669647288d4258f670a53e69"
],
"media_translation":[
{
"mediaId":"94f83a75669647288d4258f670a53e69",
"languageId":"2fbb5fe2e29a4d70aa5854ce7ce3e20b"
}
]
},
"errors":[
]
}
],
"extensions":[
]
}
},
"extensions":[
]
}
Step 2
This is the step where we will upload an image to Shopware. We will use a variant with the content-type image/jpg. However, a payload with an URL-Attribute would also work. See the details in the official documentation.
Request-Body
POST api/_action/media/94f83a75669647288d4258f670a53e69/upload?extension=jpg&fileName=yourfilename
Note that the media-id is part of the URL. And also the filename but without the file-extension JPG!
This body is pretty straightforward an in our case there is no payload, as we use an upload with Content-Type: "image/jpeg".
This would be a payload if you want to use an URL as resource:
{
"url": "<url-to-your-image>"
}

How can I validate different schemas based on an enumerated property value with AJV?

I need to validate a json document depending the value in one of the properties, specifically an enum property.
This is the JSON document to be validated:
{
"req": {
"user": "",
"company": "",
"dept": "",
"class": "",
"reqType": "account"
}
}
The reqType can take different values like account, dept, and class based on the enumerated values.
I have tried using anyOf for the same but it does not validate correctly.
For example, I have tried the schema below:
{
"$id": "http://example.com/example.json",
"type": "object",
"definitions": {},
"$schema": "http://json-schema.org/draft-07/schema#",
"properties": {
"req": {
"$id": "/properties/req",
"type": "object",
"properties": {
"user": {
"$id": "/properties/req/properties/user",
"type": "string",
"title": "The User Schema ",
"default": "",
"examples": [
"a"
]
},
"company": {
"$id": "/properties/req/properties/company",
"type": "string",
"title": "The Company Schema ",
"default": "",
"examples": [
"b"
]
},
"dept": {
"$id": "/properties/req/properties/dept",
"type": "string",
"title": "The Dept Schema ",
"default": "",
"examples": [
"c"
]
},
"class": {
"$id": "/properties/req/properties/class",
"type": "string",
"title": "The Class Schema ",
"default": "",
"examples": [
"d"
]
},
"reqType": {
"$id": "/properties/req/properties/reqType",
"type": "string",
"title": "The Reqtype Schema ",
"default": "",
"examples": [
"account"
],
"enum": [
"account",
"dept",
"class"
]
}
},
"required": [
"reqType"
],
"anyOf": [
{
"properties": {
"reqType": {
"enum": [
"account"
]
}
},
"required": [
"user",
"company"
]
},
{
"properties": {
"reqType": {
"enum": [
"dept"
]
}
},
"required": [
"dept"
]
}
]
}
},
"required": [
"req"
]
}
This seems to work fine when it meets all of the conditions but when I check the failing case it throws an error for others as follows:
[
{
keyword: 'required',
dataPath: '.req',
schemaPath: '#/properties/req/anyOf/0/required',
params: {
missingProperty: 'user'
},
message: 'should have required property \'user\'',
schema: ['user', 'company'],
parentSchema: {
properties: [Object],
required: [Array]
},
data: {
company: 'b', dept: 'c', class: 'd', reqType: 'account'
}
},
{
keyword: 'enum',
dataPath: '.req.reqType',
schemaPath: '#/properties/req/anyOf/1/properties/reqType/enum',
params: {
allowedValues: [Array]
},
message: 'should be equal to one of the allowed values',
schema: ['dept'],
parentSchema: {
enum: [Array]
},
data: 'account'
},
{
keyword: 'anyOf',
dataPath: '.req',
schemaPath: '#/properties/req/anyOf',
params: {},
message: 'should match some schema in anyOf',
schema: [
[Object],
[Object]
],
parentSchema: {
'$id': '/properties/req',
type: 'object',
properties: [Object],
required: [Array],
anyOf: [Array]
},
data: {
company: 'b', dept: 'c', class: 'd', reqType: 'account'
}
}
]
I expected to get an error for the first and should have validated the 2nd case. Instead it says it did not get the enum value. Am i doing something wrong here?
You're doing it right. The anyOf keyword means that one or more of the given schemas must be valid.
It checks the first and finds that the enum keyword passes, but the required keyword fails. Therefore, this schema fails.
So, it moves on to the next schema and finds that the enum keyword fails and the required keyword passes. Therefore, this schema fails too.
anyOf did not find a valid schema, so it fails and reports that neither schema passes validation.
You see, anyOf doesn't know that enum has a special meaning in this schema. Both schemas have one keyword that passes and one that fails. To anyOf, they are the same.
Here is an alternative that can give you slightly better error messaging. The error messages still end up being quite cryptic, but they are focused where the problem really is.
{
"type": "object",
"properties": {
"req": {
"type": "object",
"properties": {
"reqType": { "enum": ["account", "dept", "class"] }
},
"required": ["reqType"],
"allOf": [
{
"anyOf": [
{
"not": {
"properties": {
"reqType": { "enum": ["account"] }
}
}
},
{ "required": ["user", "company"] }
]
},
{
"anyOf": [
{
"not": {
"properties": {
"reqType": { "enum": ["dept"] }
}
}
},
{ "required": ["dept"] }
]
}
]
}
},
"required": ["req"]
}

Resources