AWS Stepfunction pass data to next lambda without all the extra padding - aws-lambda

I have created a state machine with AWD CDK (typescript) and it all works fine. It is just the output of Lambda 1 which is the input for Lambda 2, has some sort of state machine padding which I am not interested in.
Definition of state machine:
{
"StartAt": "",
"States": {
"...applicationPdf": {
"Next": "...setApplicationProcessed",
"Type": "Task",
"Resource": "arn:aws:states:::lambda:invoke",
"Parameters": {
"FunctionName": "...applicationPdf",
"Payload.$": "$"
}
},
"...setApplicationProcessed": {
"Next": "Done",
"Type": "Task",
"Resource": "arn:aws:states:::lambda:invoke",
"Parameters": {
"...applicationPdf",
"Payload.$": "$"
}
},
"Done": {
"Type": "Succeed"
}
}
}
Output of Lambda1 (applicationPdf):
{
"ExecutedVersion": "$LATEST",
"Payload": {
...
},
"SdkHttpMetadata": {
"AllHttpHeaders": {
...
},
"HttpHeaders": {
....
},
"HttpStatusCode": 200
},
"SdkResponseMetadata": {
....
},
"StatusCode": 200
}
So I am only interested in Payload, not all the other stuff.
The reason I want to do is that is I want to run the 2nd lambda separately I just want the Event going into the Lambda, to be the Payload object, not the the object with ExecutedVersion etc.
Does anyone know how to do this?
I will have a look at the Parameters option of the definition, maybe the answer lies there.

Thanks for your question and for your interest in Step Functions.
The ResultSelector and OutputPath fields can be used to manipulate the output of a state, which can be particularly helpful when a state outputs values which you do not need access to in subsequent states. The difference between them is that ResultSelector is applied before the state's ResultPath is applied, while OutputPath is applied after it.
As you noted, you can use OutputPath to filter out any unwanted metadata before being passed on to the next state.

I found one solution, add the outputPath:
return new LambdaInvoke(this, 'lamba', {
lambdaFunction: Function.fromFunctionArn(this, name, this.createLabmdaArn('applicationPdf')),
outputPath: '$.Payload',
});
This seems to work and might be THE solution.

Related

AWS step functions - Nested Map type

I am new to AWS step functions and I'm trying to nest multiple Map type Lambda tasks. I am experiencing unexpected behaviour with my approach though. get_item_list outputs a list of items to be fed into the get_item_pages Map. The get_item_pages Map, iterates over get_info. I then want to pass each output of get_info to another Map type, get_more_info. The reason I am using Map functions is so that I can utilise the fan out functionality. The first Map type, get_item_pages, fans out as expected. In the Lambda CloudWatch logs, I can see 10+ log streams start. The get_more_info state does not appear to be fanning out in the same fashion though. I often only see a single log stream in the Lambda CloudWatch for the nested Map state.
Am I missing something obvious in my implementation or am I going about this in an entirely wrong way?
{
"Comment": "A nested map example",
"StartAt": "get_item_list",
"States": {
"get_item_list": {
"Type": "Task",
"Resource": "arn:aws:lambda:...",
"Next": "get_item_pages",
"ResultPath": "$.data"
},
"get_item_pages": {
"Type": "Map",
"ItemsPath": "$.data.all_items",
"MaxConcurrency": 100,
"Iterator": {
"StartAt": "get_info",
"States": {
"get_info": {
"Type": "Task",
"Resource": "arn:aws:lambda:...",
"ResultPath": "$.data",
"Next": "get_more_info"
},
"get_more_info": {
"Type": "Map",
"ItemsPath": "$.data.all_data",
"MaxConcurrency": 100,
"Iterator": {
"StartAt": "get_detailed_info",
"States": {
"get_detailed_info": {
"Type": "Task",
"Resource": "arn:aws:lambda:...",
"End": true
}
}
},
"End": true
}
}
},
"End": true
}
}
}
This is an old question, but thought I'd answer for anyone else. I can't say for certain, but it seems likely that you're experiencing that because the "End": true (meant to indicate completion of the map) is supposed to be at the Task level, not the Map level. Putting "End": true at the map level indicates the end of the state machine.
The exact outcome is probably indeterminant because AWS doesn't tell you EXACTLY how the iterations will flow through the map, only that it will be done so in accordance with the concurrency you set, so 100. Even so, all iterations (up to 100) are not guaranteed to move through the map concurrently.
So, what's likely happening is the first iteration is flowing through the get_more_info (nested) map and then triggering "End": true, thus ending the entire state machine.

Problem creating "Global-OptionSet" attribute using CRM Dynamics WebApi

I'm trying to create a "Global OptionSet"-attribute (sd_MyAttribute) for an existing entity (entity ID = 70816501-edb9-4740-a16c-6a5efbc05d84) via Dynamics CRM WebAPI.
The JSON I send is this using method "POST":
{
"#odata.type": "Microsoft.Dynamics.CRM.PicklistAttributeMetadata",
"OptionSet": {
"#odata.type": "Microsoft.Dynamics.CRM.OptionSetMetadata",
"IsGlobal": true,
"Name": "sd_MyPickList",
"OptionSetType": "Picklist",
"MetadataId": "a50cfc0a-e206-ea11-a811-000d3ab82e70"
},
"AttributeType": "Picklist",
"SchemaName": "sd_MyAttribute",
"Description": {
"#odata.type": "Microsoft.Dynamics.CRM.Label",
"LocalizedLabels": [
{
"#odata.type": "Microsoft.Dynamics.CRM.LocalizedLabel",
"Label": "This is the attribute I want to create.",
"LanguageCode": 1033
}
]
},
"DisplayName": {
"#odata.type": "Microsoft.Dynamics.CRM.Label",
"LocalizedLabels": [
{
"#odata.type": "Microsoft.Dynamics.CRM.LocalizedLabel",
"Label": "This is the attribute I want to create.",
"LanguageCode": 1033
}
]
},
"RequiredLevel": {
"Value": "None",
"CanBeChanged": true
}
}
I expected to get a status 204 response, indicating that a new Picklist attribute on the entity using the sd_MyPickList option set has been created.
Unfortunately, the response is:
{
"error": {
"code": "0x80048403",
"message": "Only Local option set can be created through the attribute create. IsGlobal flag must be set to 'false'.",
"innererror": {
"message": "Only Local option set can be created through the attribute create. IsGlobal flag must be set to 'false'.",
"type": "Microsoft.Crm.CrmException",
"stacktrace": " ...)"
}
}
}
There is already an issue in the github project (see https://github.com/MicrosoftDocs/dynamics-365-customer-engagement/issues/601), but I wonder whether there is a way around this problem - what json do I need to send to create an attribute adressing a global option set? Is there someone who has successfully created such an entity attribute via web-api?
There is a usecase, I don't have the ability to use an existing library for that and importing a solution is not an option in my case.
Would be perfect if someone can provide a simple json that can be send e.g. using the Contact entity and any global optionset.
Finally, I found a way to accomplish what I need. To specify the global option set I need to use the "#odata.bind" action in the JSON data. For an attribute "sd_MyAttribute" that uses the global OptionSet with the MetaDataId "62654906-7A0b-ea11-a817-000d3ab826fd", I need to do POST:
{
"#odata.type": "Microsoft.Dynamics.CRM.PicklistAttributeMetadata",
"GlobalOptionSet#odata.bind": "/GlobalOptionSetDefinitions(62654906-7A0b-ea11-a817-000d3ab826fd)",
"AttributeType": "Picklist",
"SchemaName": "sd_MyAttribute",
"Description": { ... },
"DisplayName": { ... },
"RequiredLevel": { ... }
}
If the entity does have the MetaDataId "70916b01-edb2-4840-a16b-6a2efbc75d84", the URI for the POST would be "/api/data/v9.0/EntityDefinitions(70916b01-edb2-4840-a16b-6a2efbc75d84)/Attributes" (logical- or schema-names are not supported).
Hope my question and answer does help someone who gets the same error message.

Pass collection into custom angular schematic?

I want to create a custom angular schematic that can accept a collection of action names. I will then generate 3 ngrx actions for every action name provided from the user.
for example I want to create a schematic that can be invoked like this:
ng g my-collection:my-schematic --actions=GetById,GetByFirstName
Then I'll generate code for GetById, GetByIdSuccess, GetByIdError, GetByFirstName, GetByFirstNameSuccess, GetByFirstNameError.
The issue is I've only seen angular schematics that will accept a single value as an input parameter. Anyone know how to handle collections in a custom angular schematic?
you can follow this blog, it will teach you how to create your own schematics project:
https://blog.angular.io/schematics-an-introduction-dc1dfbc2a2b2
after you generate your schematics project in file collection.json you can extend the #ngrx/schematics:
{
...
"extends": ["#ngrx/schematics"],
}
and then use the ngrx schematics to generate 3 actions like this:
externalSchematic('#ngrx/schematics', 'action')
I haven't found a good example of how to an array of string into a schematic parameter, but I found a workaround. What I did was have a simple string input parameter that is consistently delimited (I used , to delimit the values). Here is my schema:
export interface Schema {
name: string;
path?: string;
project?: string;
spec?: boolean;
actions: string;
__actions: string[];
store?: string;
}
I parse the actions param that is provided and generate the string array __actions and use that property in my templates. Here is a snippet from my index.ts:
export default function(options: ActionOptions): Rule {
return (host: Tree, context: SchematicContext) => {
options.path = getProjectPath(host, options);
const parsedPath = parseName(options.path, options.name);
options.name = parsedPath.name;
options.path = parsedPath.path;
options.__actions = options.actions.split(',');
options.__actions = options.__actions.map(_a => classify(_a));
If you know of a better way to process these please share. Thanks!
You need to pass the actions multiple times.
ng g my-collection:my-schematic --actions=GetById --actions=GetByFirstName
Define the parameter as an array within your schema.json file.
...
"actions": {
"type": "array",
"items": {
"type": "string"
},
"description": "The name of the actions."
},
...
Also in your schema.ts.
export interface Schema {
actions: string[]
}
If you want to pull them right off of the command args, you can do the following:
{
"$schema": "http://json-schema.org/draft-07/schema",
"$id": "Sample",
"title": "Sample Schematic",
"type": "object",
"description": "Does stuff.",
"additionalProperties": false,
"properties": {
"things": {
"type": "array",
"items": {
"type": "string"
},
"$default": {
"$source": "argv"
},
"description": "Things from the command-line args."
}
}
}
Then when you run your schematic you can do:
schematics schematic-lib:sample stuff other-stuff more-stuff
In this case, the things property will be ['stuff', 'other-stuff', 'more-stuff'].
Edit: Note that the required value in the schema won't cause the schematic to fail if you don't provide any args. You'd need to do validation on that property in your schematic.

Which is the better design for this API response

I'm trying to decide upon the best format of response for my API. I need to return a reports response which provides information on the report itself and the fields contained on it. Fields can be of differing types, so there can be: SelectList; TextArea; Location etc..
They each use different properties, so "SelectList" might use "Value" to store its string value and "Location" might use "ChildItems" to hold "Longitude" "Latitude" etc.
Here's what I mean:
"ReportList": [
{
"Fields": [
{
"Id": {},
"Label": "",
"Value": "",
"FieldType": "",
"FieldBankFieldId": {},
"ChildItems": [
{
"Item": "",
"Value": ""
}
]
}
]
}
The problem with this is I'm expecting the users to know when a value is supposed to be null. So I'm expecting a person looking to extract the value from "Location" to extract it from "ChildItems" and not "Value". The benefit to this however, is it's much easier to query for things than the alternative which is the following:
"ReportList": [
{
"Fields": [
{
"SelectList": [
{
"Id": {},
"Label": "",
"Value": "",
}
]
"Location": [
{
"Id": {},
"Label": "",
"Latitude": "",
"Longitude": "",
"etc": "",
}
]
}
]
}
So this one is a reports list that contains a list of fields which on it contains a list of fieldtype for every fieldtype I have (15 or something like that). This is opposed to just having a list of reports which has a list of fields with a "fieldtype" enum which I think is fairly easy to manipulate.
So the Question: Which format is best for a response? Any alternatives and comments appreciated.
EDIT:
To query all fields by fieldtype in a report and get values with the first way it would go something like this:
foreach(field in fields)
{
switch(field.fieldType){
case FieldType.Location :
var locationValue = field.childitems;
break;
case FieldType.SelectList:
var valueselectlist = field.Value;
break;
}
The second one would be like:
foreach(field in fields)
{
foreach(location in field.Locations)
{
var latitude = location.Latitude;
}
foreach(selectList in field.SelectLists)
{
var value= selectList.Value;
}
}
I think the right answer is the first one. With the switch statement. It makes it easier to query on for things like: Get me the value of the field with the id of this guid. It just means putting it through a big switch statement.
I went with the first one because It's easier to query for the most common use case. I'll expect the client code to put it into their own schema if they want to change it.

How can I sort GeoJson file showing defined tags?

Good morning to all and thank you for your help.
I'm working in a map page (map.html) create by leaflet library that take data from a external geojson file called water_well.js. This file, previously generated by overpass service is just a list of markers. every Marker have some proprerties. Follow an exemple:
"properties": {
"operator:type": "international",
"is_in:district": "west_mamprusi",
"is_in:region": "northern",
"source:date": "2012-02-11",
"source:ele": "gps",
"water_wells:source_type": "borehole"
},
The main page extract those data from the file before with this javascript:
var wwMarker = L.geoJson(water_well, {
pointToLayer : function (feature, latlng) {
lat = feature.geometry.coordinates[0];
lng = feature.geometry.coordinates[1];
//following code that make error
op_type = feature.properties.operator_type;
district = feature.properties.is_in:district;
region = feature.properties.is_in:region;
source_date = feature.properties.source:date;
source_ele = feature.properties.source:ele;
source_type = feature.properties.water_wells:source_type;
.....
I'm sure the problem is my Zero javascript knowledge, but I'm not a programmer and I do this map for my NGO engaged in water wells in Burkina Faso.
The script for extraction of the data don't work in this point:
op_type = feature.properties.operator:type;
The problem is ":" because is invalid character.
The second question is that not all markers in the first file called water_well.js have the same "properties" filled ad actually it is possible that someone have different group of "properties like those two:
{
"type": "Feature",
"id": "node/1606958159",
"properties": {
"#id": "node/1606958159",
"amenity": "drinking_water",
"man_made": "water_well",
"name": "puits 4"
},
"geometry": {
"type": "Point",
"coordinates": [
-3.6235696,
12.02171
]
}
},
{
"type": "Feature",
"id": "node/1913126817",
"properties": {
"#id": "node/1913126817",
"ele": "170.8000030517578",
"grid_proximity": "grid_further_500_m",
"is_in:district": "builsa",
"is_in:region": "upper_east",
"man_made": "water_well",
"operational_status": "open",
"operator:type": "individual",
"pipe_connection": "no",
"pump": "manual",
"seasonal": "another_pattern",
"source": "MVP,Columbia University",
"source:date": "2012-02-14",
"source:ele": "gps",
"water_wells:source_type": "unprotected_well"
},
"geometry": {
"type": "Point",
"coordinates": [
-1.2430456,
10.3233693
]
}
},
maybe it is possible to extract all properties of each item independently from which one is present or not..... This can be de better way to solve the problem but I've no idea how to do that.
This is what I do (ckick the water tap to see pop-up): www.h2openmap.org/map
This is almost what I would like to do (ckick the water tap to see pop-up): overpass-turbo.eu/s/7Ov
Thank you for spending your time reading my question.
Have a nice day everyone, Francesco
You can access those properties using the bracketnotation, instead of using:
district = feature.properties.is_in:district;
Use bracketnotation:
district = feature.properties['is_in:district'];
Reference on property-accessors: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Property_Accessors
Now if you want to do something based on if a property exists there is a hasOwnProperty method available on objects. Since feature.properties is an object you can use that in a condition:
if (features.properties.hasOwnProperty('is_in:district')) {
// Property exists, do stuff
}
// or
if (!features.properties.hasOwnProperty('is_in:district')) {
// Property does not exist, do stuff
}
If you want to do something base on wether multiple properties exist you can use the && (and) operator:
if (features.properties.hasOwnProperty('is_in:district') &&
features.properties.hasOwnProperty('source:data')) {
// Properties exist, do stuff
}
// Or
if (!features.properties.hasOwnProperty('is_in:district') &&
!features.properties.hasOwnProperty('source:data')) {
// Properties do not exist, do stuff
}
You could use the || (or) operator to see if at least one of the conditions matches:
if (features.properties.hasOwnProperty('is_in:district') ||
features.properties.hasOwnProperty('source:data')) {
// At least one of the properties exist, do stuff
}
// Or
if (!features.properties.hasOwnProperty('is_in:district') ||
!features.properties.hasOwnProperty('source:data')) {
// At least one of the properties does not exist, do stuff
}
Reference for this can be found here under "Logical operators": https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Expressions_and_Operators
You can use something like to build (or don't build) the data object that you need for your popup. Hope that helps.

Resources