I'm writing a REST-API using Oracle ORDS.
ORDS generates a Swagger 2.0 API documentation on a predefined URL.
I can not find how to add custom information like a text for the endpoint description or the name and schema for the "object" returned from the endpoint.
Does anyone here know how to adjust the ORDS generated Swagger documentation?
We recently enhanced ORDS such that you could inject custom comments into the Swagger-style OpenAPI Docs.
New Features in 18.4.0
ENH:28028432 - Echo p_comments value into generated Swagger
documentation Earlier versions
Here's an example -
Defining my POST
BEGIN
ORDS.DEFINE_HANDLER(
p_module_name => 'EXAMPLES',
p_pattern => 'id/',
p_method => 'POST',
p_source_type => 'plsql/block',
p_items_per_page => 0,
p_mimes_allowed => 'application/json',
p_comments => 'This is a bad example, has no error handling',
p_source =>
'begin
insert into identity_table (words) values (:words);
commit;
end;'
);
COMMIT;
END;
/
Now if I go to the OpenAPI endpoint for my module, you can see the Description text for the handler has been 'injected' into the service documentation.
"This is a bad example, has no error handling" -- it's a free text field, so you can basically put anything you want there.
{
"swagger": "2.0",
"info": {
"title": "ORDS generated API for EXAMPLES",
"version": "1.0.0"
},
"host": "localhost:8080",
"basePath": "/ords/pdb2/jeff/examples",
"schemes": [
"http"
],
"produces": [
"application/json"
],
"paths": {
"/id/": {
"get": {
"description": "Retrieve records from EXAMPLES",
"produces": [
"application/json"
],
"responses": {
"200": {
"description": "The queried record.",
"schema": {
"type": "object",
"properties": {
"ID": {
"$ref": "#/definitions/NUMBER"
},
"WORDS": {
"$ref": "#/definitions/VARCHAR2"
}
}
}
}
},
"parameters": []
},
"post": {
"description": "This is a bad example, has no error handling",
"responses": {
"201": {
"description": "The successfully created record.",
"schema": {
"type": "object",
"properties": {}
}
}
},
"consumes": [
"application/json"
],
"parameters": [
{
"name": "payload",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/EXAMPLES_ITEM"
}
}
]
}
},
"/id/{pk}": {
"get": {
"description": "Retrieve records from EXAMPLES",
"produces": [
"application/json"
],
"responses": {
"200": {
"description": "The queried record.",
"schema": {
"type": "object",
"properties": {
"ID": {
"$ref": "#/definitions/NUMBER"
},
"WORDS": {
"$ref": "#/definitions/VARCHAR2"
}
}
}
}
},
"parameters": [
{
"name": "pk",
"in": "path",
"required": true,
"type": "string",
"description": "implicit",
"pattern": "^[^/]+$"
}
]
}
}
},
"definitions": {
"NUMBER": {
"type": "number"
},
"VARCHAR2": {
"type": "string"
},
"EXAMPLES_ITEM": {
"properties": {
"words": {
"type": "string"
}
}
}
}
}
Related
I'm trying to return a Discovery Response, but the supportedCookingModes only seems to accept standard values and only in the format of ["OFF","BAKE"], not Custom values as indicated by the documentation. Any idea on how to specify custom values?
{
"event": {
"header": {
"namespace": "Alexa.Discovery",
"name": "Discover.Response",
"payloadVersion": "3",
"messageId": "asdf"
},
"payload": {
"endpoints": [
{
"endpointId": "asdf",
"capabilities": [
{
"type": "AlexaInterface",
"interface": "Alexa.Cooking",
"version": "3",
"properties": {
"supported": [
{
"name": "cookingMode"
}
],
"proactivelyReported": true,
"retrievable": true,
"nonControllable": false
},
"configuration": {
"supportsRemoteStart": true,
"supportedCookingModes": [
{
"value": "OFF"
},
{
"value": "BAKE"
},
{
"value": "CUSTOM",
"customName": "FANCY_NANCY_MODE"
}
]
}
}
]
}
]
}
}
}
Custom cooking modes are brand specific. This functionality is not yet publicly available. I recommend you to choose one of the existing cooking modes:
https://developer.amazon.com/en-US/docs/alexa/device-apis/cooking-property-schemas.html#cooking-mode-values
https://developers.google.com/analytics/devguides/reporting/data/v1/rest/v1alpha/TopLevel/batchRunReports
Request :
{
"entity": {
"propertyId": "XXXXXXXX"
},
"requests": [
{
"entity": {
"propertyId": "XXXXXXXX"
},
"dimensions": [
{
"name": "date"
},
{
"name": "dateHour"
},
{
"name": "firstUserCampaignName"
}
],
"metrics": [
{
"name": "sessions"
}
],
"dateRanges": [
{
"startDate": "2021-04-06",
"endDate": "2021-04-07"
}
],
"metricAggregations": [
"TOTAL"
],
"dimensionFilter": {
"andGroup": {
"expressions": [
{
"filter": {
"fieldName": "medium",
"stringFilter": {
"matchType": "EXACT",
"value": "Test"
}
}
}
]
}
},
"orderBys": [
{
"desc": true,
"metric": {
"metricName": "sessions"
}
},
{
"desc": false,
"dimension": {
"dimensionName": "dateHour"
}
}
],
"keepEmptyRows": true
}
]
}
Response:
{
"error": {
"code": 500,
"message": "Internal error encountered.",
"status": "INTERNAL"
}
}
But if remove following property from request:
"metricAggregations": [
"TOTAL"
],
I am able to see following response where there is not rows :
{
"reports": [
{
"metricHeaders": [
{
"name": "sessions",
"type": "TYPE_INTEGER"
}
],
"metadata": {},
"dimensionHeaders": [
{
"name": "date"
},
{
"name": "dateHour"
},
{
"name": "firstUserCampaignName"
}
],
"kind": "analyticsData#runReport"
}
],
"kind": "analyticsData#batchRunReports"
}
Any idea how to prevent 500 internal server error in this case ?
This error block google API call for an hour.
Furqan, there seems to be an issue with the Data API where a call using metricAggregations is failing in case the generated report is empty. In the meantime, to workaround this error, you can modify a query so that the resulting report contains more than 0 rows.
I have created an ARM template in Visual Studio project with connection to slack. When I connect to slack in Azure portal - everything is fine. My slack API connection is authorized and works. My problem is with deployment and setting $connections.
Here is my template
{
"$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
<template-params>
},
"variables": {
"slack": "[concat(parameters('appPrefix'), '-slack-', parameters('environment'))]"
},
"resources": [
{
"type": "Microsoft.Web/connections",
"apiVersion": "2016-06-01",
"location": "[resourceGroup().location]",
"name": "[variables('slack')]",
"properties": {
"api": {
"id": "[concat('/subscriptions/', subscription().subscriptionId, '/providers/Microsoft.Web/locations/', resourceGroup().location, '/managedApis/slack')]"
},
"displayName": "Slack",
"parameterValues": {}
}
},
{
"name": "[parameters('logicAppName')]",
"type": "Microsoft.Logic/workflows",
"location": "[parameters('logicAppLocation')]",
"tags": {
"displayName": "LogicApp"
},
"apiVersion": "2016-06-01",
"properties": {
"definition": {
"$schema": "https://schema.management.azure.com/providers/Microsoft.Logic/schemas/2016-06-01/workflowdefinition.json#",
"actions": {
"Collecting_went_wrong": {
"inputs": {
"host": {
"connection": {
"name": "#parameters('$connections')['slack']['connectionId']"
}
},
"method": "post",
"path": "/chat.postMessage",
"queries": {
"channel": "<channel>",
"text": "<message>",
"username": "<user>"
}
},
"runAfter": {},
"type": "ApiConnection"
}
},
"parameters": {
"$connections": {
"type": "object",
"defaultValue": {
}
}
},
"triggers": {
<trigger>
}
},
"parameters": {
"$connections": {
"value": {
"slack": {
"id": "[resourceId('Microsoft.Web/connections', variables('slack'))]",
"connectionId": "[resourceId('Microsoft.Web/connections', variables('slack'))]",
"connectionName": "slack"
}
}
}
}
},
"dependsOn": [
"[resourceId('Microsoft.Web/connections', variables('slack'))]"
]
}
],
"outputs": {}
}
The 'fun' part is validation of template during the deployment:
New-AzureRmResourceGroupDeployment : 17:08:51 - Resource Microsoft.Logic/workflows 'reporting-reminder-logic-app-dev'
failed with message '{
"error": {
"code": "ConnectionsParameterInvalid",
"message": "The provided API connection parameter 'slack' is missing the required property 'id'."
}
}'
I'm really confused here.
What I tried to deploy it as connection and added $connection parameter definition as well, deployment went through. Nevertheless, when I changed connection to $connections in template parameters, Azure portal throw at me the same validation error.
Any idea what am I doing wrong here?
Thanks
The issue was about different ID in resources -> Microsoft/Web.connections -> properties -> id and slack -> connection. Once this connection ids were the same, the validation passed.
So it's only confusing validation message.
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"]
}
Let's say I have two JSON objects (call them 'websites' and 'links'). I need to end up with both objects in a single CSV (ideally in separate columns).
I'm working with something like this:
File.open("file.json", "w") do |f|
combined = [websites, links]
f.write(JSON.pretty_generate(combined))
end
And then I'm using the Ruby gem json2csv to convert this file to a CSV. But when I do, I get the following error:
error: undefined method 'keys' for #<Array:0x007fea8a8e33f8>
I can't figure out what's wrong. When I look in file.json, it appears to be structured like this: [{websites}, {links}]. From my limited knowledge of JSON, I think that's right, but I could easily be wrong.
Also, I know this won't get me separate columns in the CSV. If anyone has an answer for that part, major bonus points.
EDIT: JSON examples included below; error message changed after minor fix on my end.
websites:
{
"uri": "https://v1/websites",
"id": 28235674,
"background": null,
"createdDate": 1399585684000,
"lastActivityDate": 1430682494000,
"lastCommunicationDate": 1430682494000,
"lastNonCommunicationChronicleDate": 1430330886000,
"lastModifiedDate": 1449263116000,
"lastViewedDate": 1421429034000,
"preferredContactType": null,
"rss": "",
"emailAddresses": [
{
"email": "",
"type": "Work"
},
{
"email": "",
"type": "Work"
},
{
"email": "",
"type": "Work"
},
{
"email": "not found",
"type": "Work"
}
],
"phoneNumbers": [
],
"streetAddresses": [
],
"socialNetworks": [
{
"profileUrl": "http://twitter.com",
"name": "Twitter"
},
{
"profileUrl": "http://www.facebook.com",
"name": "Facebook"
},
{
"profileUrl": "http://plus.google.com",
"name": "GooglePlus"
},
{
"profileUrl": "http://www.linkedin.com",
"name": "LinkedIn"
},
{
"profileUrl": "http://twitter.com",
"name": "Twitter"
}
],
"contactUrls": [
],
"tags": [
"tag1",
"tag2"
],
"mostRecentActivity": "https://v1/history",
"mostRecentChronicle": "https://v1/history",
"mostRecentCommunication": "https://v1/history",
"mostRecentNonCommunicationChronicle": "https://v1/history",
"projectStates": "https://v1/websites",
"history": "https://v1/history",
"customFieldValues": [
],
"name": "",
"primaryDomain": "",
"domains": [
""
],
"associatedPeople": "https://v1/people",
"payments": "https://payments",
"links": "https://v1/links",
"type": "https://v1/websites"
}
links:
{
"uri": "https://v1/links/custom_fields",
"id": 15529329,
"value": "Name",
"backgroundColor": null,
"customField": "https://v1/links/custom_fields"
}
combined output:
[
{
"uri": "https://v1/websites",
"id": 28235674,
"background": null,
"createdDate": 1399585684000,
"lastActivityDate": 1430682494000,
"lastCommunicationDate": 1430682494000,
"lastNonCommunicationChronicleDate": 1430330886000,
"lastModifiedDate": 1449263116000,
"lastViewedDate": 1421429034000,
"preferredContactType": null,
"rss": "",
"emailAddresses": [
{
"email": "",
"type": "Work"
},
{
"email": "",
"type": "Work"
},
{
"email": "",
"type": "Work"
},
{
"email": "not found",
"type": "Work"
}
],
"phoneNumbers": [
],
"streetAddresses": [
],
"socialNetworks": [
{
"profileUrl": "http://twitter.com/",
"name": "Twitter"
},
{
"profileUrl": "http://www.facebook.com",
"name": "Facebook"
},
{
"profileUrl": "http://plus.google.com",
"name": "GooglePlus"
},
{
"profileUrl": "http://www.linkedin.com/",
"name": "LinkedIn"
},
{
"profileUrl": "http://twitter.com/",
"name": "Twitter"
}
],
"contactUrls": [
],
"tags": [
"tag1",
"tag2"
],
"mostRecentActivity": "https://v1/history/",
"mostRecentChronicle": "https://v1/history/",
"mostRecentCommunication": "https://v1/history/",
"mostRecentNonCommunicationChronicle": "https://v1/history/",
"projectStates": "https://v1/websites/",
"history": "https://v1/history",
"customFieldValues": [
],
"name": "",
"primaryDomain": "",
"domains": [
""
],
"associatedPeople": "https://v1/people",
"links": "https://v1/links",
"type": "https://v1/websites"
},
{
"uri": "https://links/custom_fields",
"id": 15529329,
"value": "Name",
"backgroundColor": null,
"customField": "https://links/custom_fields"
}
]
JSON.pretty_generate() is expecting a hash and you are passing an array of 2 hashes. Start with: combined.map { |c| f.write(JSON.pretty_generate(c)) } and then mapping them into your CSV should be as easy as following the CSV documentation.