SwiftyJSON and Dynamic Named Objects - swift

Problem: Attempting to consume a JSON payload which contains a dynamic Object name. Since the names of these objects are not sequential or derived from a pattern, I'm unable to travers the payload with SwiftyJSON. A sample of the dynamic objects looks like this denoted in bold:
"180A": {
"id": "180A",
"label": "Oceanside Gate",
"path": "North",
"index": "1"
},
"195C": {
"id": "195C",
"label": "Dune Beach Gate",
"path": "North",
"index": "2"
},
"211F": {
"id": "211F",
"label": "Sunset Harbor Gate",
"path": "North",
"index": "3"
}
Sample JSON Payload:
{
"Direction": {
"NorthGates": {
"180A": {
"id": "180A",
"label": "Oceanside Gate",
"path": "North",
"index": "1"
},
"195C": {
"id": "195C",
"label": "Dune Beach Gate",
"path": "North",
"index": "2"
},
"211F": {
"id": "211F",
"label": "Sunset Harbor Gate",
"path": "North",
"index": "3"
}
}
}
}
Using SwiftyJSON I'm able to successfully print the label of a known Object, such as "180A":
DataManager.getTopAppsDataFromFileWithSuccess { (data) -> Void in
let json = JSON(data: data)
if let gateLabel = json["Direction"]["NorthGates"]["180A"]["label"].stringValue {
//output: "Oceanside Gate"
println("NSURLSession: \(gateLabel)")
}
}
Since the dynamic object names are not static, I'm unable to use the pre-defined object names in the code above to locate the label value. The following attempts return nil values:
//stringValue = nil
if let gateLabel = json["Direction"]["NorthGates"][0].stringValue {
println("NSURLSession: \(gateLabel)")
}
//stringValue = nil
if let gateLabel = json["Direction"]["NorthGates"][0]["label"].stringValue {
println("NSURLSession: \(gateLabel)")
}

This is the solution:
if let gates:[String: JSON] = json["Direction"]["NorthGates"].dictionaryValue {
for item in gates {
println("Dynamic Object Gate Name: \(item.0)") //Gate Name
println(item.1["label"].stringValue) //Gate Label
println(item.1["path"].stringValue) //Gate Path
println(item.1["index"].stringValue) //Gate Index
}
}

Related

Possible Notion API bug - Can't create Database Page with URL data

I have a Notion database that I'm trying to write to via the API.
I can write to text fields, but when I try to write to a URL field, I receive the following error:
{
"object": "error",
"status": 400,
"code": "validation_error",
"message": "body failed validation. Fix one:\nbody.properties.O?HT.id should be defined, instead was `undefined`.\nbody.properties.O?HT.name should be defined, instead was `undefined`.\nbody.properties.O?HT.start should be defined, instead was `undefined`."
}
This is the API description of that field:
"LinkedIn": {
"id": "O%3FHT",
"name": "LinkedIn",
"type": "url",
"url": {}
},
This is the body of the API POST request that's failing:
{
"parent": {
"database_id": "f016c02beeff4a34bf298ceb8a8a079f"
},
"properties": {
"title": [
{
"text": {
"content": "Mark Zuckerberg"
}
}
],
"%3DCzE": [
{
"text": {
"content": "CEO"
}
}
],
"HihI": [
{
"text": {
"content": "Facebook"
}
}
],
"JC%3BS": [
{
"text": {
"content": "Menlo Park, CA"
}
}
],
"O%3FHT": {
"url": "https://www.linkedin.com/in/mark-zuckerberg/"
},
"wS%7BN": {
"url": "https://www.linkedin.com/company/facebook/mycompany/verification/"
}
}
}
I'm basing the body contents on the Postman docs provided by Notion: https://www.postman.com/notionhq/workspace/notion-s-api-workspace/documentation/15568543-d990f9b7-98d3-47d3-9131-4866ab9c6df2

AWS Stepfunction: Substring from input

As a general question is it possible do a substring function within step functions?
I receive the following event:
{
"input": {
"version": "0",
"id": "d9c5fec0-d08d-6abd-4ea5-0107fbbce47d",
"detail-type": "EBS Multi-Volume Snapshots Completion Status",
"source": "aws.ec2",
"account": "12345678",
"time": "2021-11-12T12:08:16Z",
"region": "us-east-1",
"resources": [
"arn:aws:ec2::us-east-1:snapshot/snap-0a98c2a42ee266123"
]
}
}
but need the snapshot id as input to DescribeInstances, therefore I need to extract snap-0a98c2a42ee266123 from arn:aws:ec2::us-east-1:snapshot/snap-0a98c2a42ee266123
Is there any simple way to do this within step functions?. That is to say without having to pass it to a lambda or something equally convoluted?
This has recently become possible with the addition of new intrinsic functions. ArrayGetItem gets an item by its index. StringSplit splits a string at a delimeter. Use a Pass state to extract the snapshot name from the resource ARN:
{
"StartAt": "ExtractSnapshotName",
"States": {
"ExtractSnapshotName": {
"Type": "Pass",
"Parameters": {
"input.$": "$.input",
"snapshotName.$": "States.ArrayGetItem(States.StringSplit(States.ArrayGetItem($.input.resources,0), '/'),1)"
},
"ResultPath": "$",
"End": true
}
}
}
output:
{
"input": {
"version": "0",
"id": "d9c5fec0-d08d-6abd-4ea5-0107fbbce47d",
"detail-type": "EBS Multi-Volume Snapshots Completion Status",
"source": "aws.ec2",
"account": "12345678",
"time": "2021-11-12T12:08:16Z",
"region": "us-east-1",
"resources": [
"arn:aws:ec2::us-east-1:snapshot/snap-0a98c2a42ee266123"
]
},
"snapshotName": "snap-0a98c2a42ee266123"
}

How to document complex objects in Apache Camel OpenAPI documentation using camel-openapi-java instead of camel-swagger-java component?

I am working on trying to correctly document our rest endpoints. As an example to get this working I created a sample "Healthcheck getStatus()" endpoint which is returning an object called "EndpointStatus" which has 3 fields (class is below). I was able to get this object documenting correctly and using the camel-swagger-java component and the below rest configuration / definition;
restConfiguration()
.apiContextPath(apiContextPath)
.apiProperty("api.title", "Camel Service").apiProperty("api.version", "1.0.0")
// and enable CORS
.apiProperty("cors", "true");
rest()
.path("/healthcheck")
.description("Health Check REST service")
.get("getStatus/{endpointName}")
.param()
.name("endpointName")
.type(RestParamType.path)
.allowableValues(
Stream.of(EndpointName.values())
.map(EndpointName::name)
.collect(Collectors.toList()))
.required(true)
.endParam()
.description("Get Camel Status")
.id("getStatus")
.outType(EndpointStatus.class)
.bindingMode(RestBindingMode.auto)
.responseMessage().code(200).message("Returns an EndpointStatus object representing state of a camel endpoint").endResponseMessage()
.to(CAMEL_STATUS_URI);
Here are the annotations I used on this class:
#ApiModel(description = "Endpoint Status Model")
public class EndpointStatus {
private boolean isAvailable;
private EndpointName name;
private long timestamp;
#ApiModelProperty(value = "Is the endpoint available", required = true)
public boolean isAvailable() {
return isAvailable;
}
public void setAvailable(boolean available) {
isAvailable = available;
}
#ApiModelProperty(value = "The name of the endpoint", required = true)
public EndpointName getName() {
return name;
}
public void setName(EndpointName name) {
this.name = name;
}
#ApiModelProperty(value = "The timestamp the endpoint was checked", required = true)
public long getTimestamp() {
return timestamp;
}
public void setTimestamp(long timestamp) {
this.timestamp = timestamp;
}
}
Along with the generated swagger documentation:
{
"swagger": "2.0",
"info": {
"version": "1.0.0",
"title": "Camel Service"
},
"host": "localhost:9000",
"tags": [
{
"name": "healthcheck",
"description": "Health Check REST service"
}
],
"schemes": [
"http"
],
"paths": {
"/healthcheck/getStatus/{endpointName}": {
"get": {
"tags": [
"healthcheck"
],
"summary": "Get Camel Status",
"operationId": "getStatus",
"parameters": [
{
"name": "endpointName",
"in": "path",
"required": true,
"type": "string",
"enum": [
"ENDPOINTA",
"ENDPOINTB"
]
}
],
"responses": {
"200": {
"description": "Returns an EndpointStatus object representing state of a camel endpoint",
"schema": {
"$ref": "#/definitions/EndpointStatus"
}
}
}
}
}
},
"definitions": {
"EndpointStatus": {
"type": "object",
"required": [
"available",
"name",
"timestamp"
],
"properties": {
"name": {
"type": "string",
"description": "The name of the endpoint",
"enum": [
"ENDPOINTA",
"ENDPOINTB"
]
},
"timestamp": {
"type": "integer",
"format": "int64",
"description": "The timestamp the endpoint was checked"
},
"available": {
"type": "boolean",
"description": "Is the endpoint available"
}
},
"description": "Endpoint Status Model"
}
}
}
However, when trying to move to use camel-openapi-java which supports OpenAPI Specification v3 with the same setup I am getting EndpointStatus without any fields / descriptions in my documentation.
{
"openapi": "3.0.2",
"info": {
"title": "SurePath Camel Service",
"version": "1.0.0"
},
"servers": [
{
"url": ""
}
],
"paths": {
"/healthcheck/getStatus/{endpointName}": {
"get": {
"tags": [
"healthcheck"
],
"parameters": [
{
"name": "endpointName",
"schema": {
"enum": [
"ENDPOINTA",
"ENDPOINTB"
],
"type": "string"
},
"in": "path",
"required": true
}
],
"responses": {
"200": {
"description": "Returns an EndpointStatus object representing state of a camel endpoint"
}
},
"operationId": "getStatus",
"summary": "Get Camel Status",
"x-camelContextId": "camel-1",
"x-routeId": "getStatus"
}
},
"/healthcheck/isAvailable": {
"get": {
"tags": [
"healthcheck"
],
"responses": {
"200": {
"description": "Returns status code 200 when Camel is available"
}
},
"operationId": "verb1",
"summary": "Is Camel Available",
"x-camelContextId": "camel-1",
"x-routeId": "route4"
}
}
},
"components": {
"schemas": {
"EndpointStatus": {
"type": "EndpointStatus",
"x-className": {
"format": "com.sample.bean.EndpointStatus",
"type": "string"
}
}
}
},
"tags": [
{
"name": "healthcheck",
"description": "Health Check REST service"
}
]
}
I have tried adding this into my responseMessage and it is still not documenting correctly;
responseMessage().code(200).responseModel(EndpointStatus.class).message("Returns an EndpointStatus object representing state of a camel endpoint").endResponseMessage()
Do I need different annotations / RestDefinition config to get this EndpointStatus class appearing correctly in the OpenAPI documentation?
This looks to be an issue at the moment with the camel-openapi-java component; waiting for a resolution from this jira https://issues.apache.org/jira/browse/CAMEL-15158

ElasticSearch Reindex API and painless script to access date field

I try to familiarize myself with the Reindexing API of ElasticSearch and the use of Painless scripts.
I have the following model:
"mappings": {
"customer": {
"properties": {
"firstName": {
"type": "text",
"fields": {
"keyword": {
"ignore_above": 256,
"type": "keyword"
}
}
},
"lastName": {
"type": "text",
"fields": {
"keyword": {
"ignore_above": 256,
"type": "keyword"
}
}
},
"dateOfBirth": {
"type": "date"
}
}
}
}
I would like to reindex all documents from test-v1 to test-v2 and apply a few transformations on them (for example extract the year part of dateOfBirth, convert a date value to a timestamp, etc) and save the result as a new field. But I got an issue when I tried to access it.
When I made the following call, I got an error:
POST /_reindex?pretty=true&human=true&wait_for_completion=true HTTP/1.1
Host: localhost:9200
Content-Type: application/json
{
"source": {
"index": "test-v1"
},
"dest": {
"index": "test-v2"
},
"script": {
"lang": "painless",
"inline": "ctx._source.yearOfBirth = ctx._source.dateOfBirth.getYear();"
}
}
And the response:
{
"error": {
"root_cause": [
{
"type": "script_exception",
"reason": "runtime error",
"script_stack": [
"ctx._source.yearOfBirth = ctx._source.dateOfBirth.getYear();",
" ^---- HERE"
],
"script": "ctx._source.yearOfBirth = ctx._source.dateOfBirth.getYear();",
"lang": "painless"
}
],
"type": "script_exception",
"reason": "runtime error",
"script_stack": [
"ctx._source.yearOfBirth = ctx._source.dateOfBirth.getYear();",
" ^---- HERE"
],
"script": "ctx._source.yearOfBirth = ctx._source.dateOfBirth.getYear();",
"lang": "painless",
"caused_by": {
"type": "illegal_argument_exception",
"reason": "Unable to find dynamic method [getYear] with [0] arguments for class [java.lang.String]."
}
},
"status": 500
}
According to this tutorial Date fields are exposed as ReadableDateTime so they support methods like getYear, and getDayOfWeek. and indeed, the Reference mentions those as supported methods.
Still, the response mentions [java.lang.String] as the type of the dateOfBirth property. I could just parse it to e.g. an OffsetDateTime, but I wonder why it is a string.
Anyone has a suggestion what I'm doing wrong?

CloudFormation - Access Output of Parent Stack in Child Nested stack

I have a master Cloudformation template which invokes two child templates. I have my first template run and the Outputs captured in the Outputs section of the resource. I have given lot of tries in using the ChildStack01 Output values in the Second Template which is nested and I am not sure why I get Template format error: Unresolved resource dependencies [XYZ] in the Resources block of the template. Here is my master template.
{
"AWSTemplateFormatVersion": "2010-09-09",
"Resources": {
"LambdaStack": {
"Type": "AWS::CloudFormation::Stack",
"Properties": {
"TemplateURL": "https://s3.amazonaws.com/bucket1/cloudformation/Test1.json",
"TimeoutInMinutes": "60"
}
},
"PermissionsStack": {
"Type": "AWS::CloudFormation::Stack",
"Properties": {
"TemplateURL": "https://s3.amazonaws.com/bucket1/cloudformation/Test2.json",
"Parameters": {
"LambdaTest": {
"Fn::GetAtt": ["LambdaStack", "Outputs.LambdaTest"]
}
},
"TimeoutInMinutes": "60"
}
}
}
}
Here is my Test1.json Template
{
"Resources": {
"LambdaTestRes": {
"Type": "AWS::Lambda::Function",
"Properties": {
"Description": "Testing AWS cloud formation",
"FunctionName": "LambdaTest",
"Handler": "lambda_handler.lambda_handler",
"MemorySize": 128,
"Role": "arn:aws:iam::3423435234235:role/lambda_role",
"Runtime": "python2.7",
"Timeout": 300,
"Code": {
"S3Bucket": "bucket1",
"S3Key": "cloudformation/XYZ.zip"
}
}
}
},
"Outputs": {
"LambdaTest": {
"Value": {
"Fn::GetAtt": ["LambdaTestRes", "Arn"]
}
}
}
}
Here is My Test2.json which has to use the output of Test1.json.
{
"Resources": {
"LambdaPermissionLambdaTest": {
"Type": "AWS::Lambda::Permission",
"Properties": {
"Action": "lambda:invokeFunction",
"FunctionName": {
"Ref": "LambdaTest"
},
"Principal": "apigateway.amazonaws.com",
"SourceArn": {
"Fn::Join": ["", ["arn:aws:execute-api:", {
"Ref": "AWS::Region"
}, ":", {
"Ref": "AWS::AccountId"
}, ":", {
"Ref": "TestAPI"
}, "/*"]]
}
}
}
},
"Parameters": {
"LambdaTest": {
"Type": "String"
}
}
}
It is not enough to just have output, you need to export that output.
Look here: http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/using-cfn-stack-exports.html
So you need something like:
"Outputs": {
"LambdaTest": {
"Value": {
"Fn::GetAtt": ["LambdaTestRes", "Arn"]
}
"Export": {
"Name": "LambdaTest"
}
}
}
You have two unresolved Ref resource dependencies in Test2.json, one to LambdaTest and one to TestAPI.
For LambdaTest, it looks like you're trying to pass this as a parameter from the parent stack, but you haven't specified it as an input Parameter in the child Test2.json template. Add an entry in Test2.json's Parameters section, like this:
"Parameters": {
"LambdaTest": {
"Type": "String"
}
},
Regarding TestAPI, this reference doesn't seem to appear anywhere else in your templates, so you should either specify this as a fixed string directly, or add another input Parameter in your Test2.json stack (see above) and then provide it from the parent stack.
The error is coming from test1.json(LambdaStack).
Logical ID
An identifier for the current output. The logical ID must be alphanumeric (a-z, A-Z, 0-9) and unique within the template.
It seems you have two logical ID with the same name "LambdaTest", one in resource section and other in output section.