Step Functions parameter (array or not to array) - arrays

I have a couple of state machines that use respectively DescribeNetworkInterfaces (and EC2 API) and ListResourceRecordSets (a Route53 API).
I compose both Parameters using the Input as follows:
For EC2:
"Parameters": {
"NetworkInterfaceIds.$": "$.detail.attachments[0].details[?(#.name==networkInterfaceId)].value"
"Resource": "arn:aws:states:::aws-sdk:ec2:describeNetworkInterfaces",
For Route 53:
"Parameters": {
"HostedZoneId.$": "$.NetworkInterfaces[0].TagSet[?(#.Key==HOSTZONEID)].Value"
},
"Resource": "arn:aws:states:::aws-sdk:route53:listResourceRecordSets"
They resolve fine but the problem is that, for some reasons, the [?(#.name==networkInterfaceId)] and the [?(#.Key==HOSTZONEID)] turn the Parmeters values into array. Respectively:
{
"NetworkInterfaceIds": [
"eni-00f25c294401006b2"
]
}
And:
{
"HostedZoneId": [
"Z0555263BOXV8ELWLRS5"
]
}
In my case, the EC2 call succeeds because the Network API does expect an array of ENIs. However the Route53 call fails because the Records API does expect just one hosted zone id. This is the error message I get from SF:
No hosted zone found with ID: ["Z0555263BOXV8ELWLRS5"] (Service: Route53, Status Code: 404, Request ID: fa5bc89b-ca3d-4fe9-b2f3-baf01d509d76)
Based on my tests, it seems that it's the select that is causing the Parameter to be turned into an array because if I point directly to a field (which I cannot do) such as this:
"Parameters": {
"NetworkInterfaceIds.$": "States.Array($.detail.attachments[0].details[1].value)"
},
The input parameter is no longer constructed as an array and the Network API fails with:
An error occurred while executing the state 'DescribeNetworkInterfaces (1)' (entered at the event id #2). The Parameters '{"NetworkInterfaceIds":"eni-00f25c294401006b2"}' could not be used to start the Task: [Cannot construct instance of java.util.ArrayList (although at least one Creator exists): no String-argument constructor/factory method to deserialize from String value ('eni-00f25c294401006b2')]
I can fix the above problem using the States.Array intrinsic but it's a moot point because I need to dynamically select the the values in the inputs.
So I am at the point where the EC2 call works because, by chance, I need an array... but I can't figure out how to turn the Route53 parameter (hosted zone id) into a non array.

It was easier than I thought. I figure there was an intrinsic that would allow me to extract a value from an array: States.ArrayGetItem.
The following did the trick:
"Parameters": {
"HostedZoneId.$": "States.ArrayGetItem($.NetworkInterfaces[0].TagSet[?(#.Key==HOSTZONEID)].Value, 0)"
},

Related

How do I call a multiple layer deep Object or a Value of a JSON database?

I created a Json Server Database like this:
"Time":
[
{
"id":1,
"name":
[
{
"id":1,
"checkin":
[
{
"id":1,
"date":"123",
"time":"123"
},
{
"id":2,
"date":"123",
"time":"123"
}
]
},
{
"id":2,
"checkout":
[
{
"id":1,
"date":"123",
"time":"123"
}
]
}
]
}
]
I don't want to get the entire Database and go through it. I just want to tell the Database where exactly my Object is and have it returned.
How would I call the call for example the first Check-in Object?
I use the Angular HttpClient like this:
this.http.get(endpoint, JSON.stringify(time), this.httpOptions))
So I need the Exact Endpoint in a format like: endpoint/id/id or similar
I imagined it like this: endpoint/time/1/1
With output:
[
{
"id":1,
"date":"123",
"time":"123"
}
]
If this is not possible please tell me anyways.
PS: The question from this thread is essentially the same as mine. Also the JSON documentation doesn't real help either, it just says you need custom routes for multilayer JSON strings but not how to implement these routes.
I'm not sure if I understand correctly where you are returning the data from. If you meant json-server, just look at the documentation (here) and then you could use an endpoint like "/posts?Id=2"
However, if you mean your own API, which does not have an endpoint that returns one record, e.g. by its ID, the only convenient solution is to create a service that will map the result from the server and return the desired value.
You can do all this in one place, but for clearer code, I recommend dividing it into:
service that will download data from the server
service that will map the data on the basis of a given parameter
component that will display the data
Below is a working example on Stackblitz.
example
Note that in the app-component I pass the ID 32 to the method from the mapping service as the parameter. The mapping service then calls a method that sends the request for all the data.
The important thing is that all data is returned to the application, not just one record. If the API you are using does not provide such an endpoint, it is not possible to return only one record.
Apparently a request like I wanted to call is still not possible. The only way to come close is to fake it with custom Routes and flattening the JSON structure like in this old thread.

Map subtask_id to TaskManager in Flink

I have an operator with parallelism=256 running on 128 task managers. Each time when I get a checkpoint failure, it happens at the same subtask of this operator, for example it's always subtask 129 that gets stuck and blocks the checkpointing. I want to understand what happened to this subtask by examining logs of the task manager that subtask 129 is running on. Is there a way in Flink to map subtask id to the corresponding Task Manager?
The taskmanager.log files contain the names of the deployed tasks including their sub task index. You could simply search for the TASK_NAME (129/256) in all taskmanager.log files.
I was able to find not a trivial, but working solution to get the required map at runtime programmatically.
The main idea is that the Rest Endpoint /jobs/:jobid/vertices/:vertexid provides the necessary information for a specific vertex in format
{
"id": "804e...",
"name": "Map -> Sink",
...
"subtasks": [
{
"subtask": 0,
"host": "ip-10-xx-yy-zz:36ddd"
},
...
]
}
The main difficulty was to get the web interface url programmatically. I was able to get it this way (probably, there is a more elegant solution):
val env = FieldUtils
.readField(getRuntimeContext.asInstanceOf[StreamingRuntimeContext], "taskEnvironment", true)
.asInstanceOf[RuntimeEnvironment]
try {
println("trying to get cluster client...")
val client = new RestClusterClient[String](env.getTaskManagerInfo.getConfiguration, "rest")
return client.getWebInterfaceURL
} catch {
case e: Exception =>
println("Failed to get cluster client : ")
e.printStackTrace()
}
Given the web interface url, I simply made an http call to it and constructed the map.

How to give variable to filter in object in backand using ionic and angularjs

I need to pass variable inside the filter of value field of object in services.
Here is my code:
.service('LoginService', function (Backand) {
var email="";
service.getUsername = function(){
email=Backand.user.getUsername();
return email;
};
service.getUserData=function(){
return Backand.object.getList("users", {
"pageSize": 20,
"pageNumber": 1,
"filter": [
{
"fieldName": "email",
"operator": "equals",
"value":email
}
],
"sort": []
})
};
)}
In the above code I store email in email variable and I want to pass
it in value field in service.getUserData() but it cannot take it. How can I figure it out?
As devqon mentioned, make sure the variable "email" actually has value when this call is made. The Backand SDK returns a promise for all method calls, meaning you'll need to update your code above with a handler for the success/failure of the GetUsername() function, as follows:
Backand.user.getUsername().then(function(data){email = data.email;});
This defines a simple success handler that will populate the email variable with the result of the call. You should then be able to pass it back to the API without issue, but you'll likely need to restructure your code to account for the asynchronous nature of the getUsername call - returning the result of GetUsername() in response to a call to this method won't work until after the promise is resolved, and at the moment you're only returning the promise itself. There is more info in our documentation at http://docs.backand.com/?javascript#sdk-methods
For security reasons, I would not suggest to have such a service at client side. Some malicious user could be able to change your code and get details of other users.
Using Backand, you can also filter at server side.
You don't need to pass the email variable; Backand has a variable that provides the email of the logged user.

Is including additional information in the output object a good idea?

I'm experimenting with a Conversation where I would like to modify the output in a couple of different ways:
different output for speech or text
different output depending on the tone of the conversation
It looks like I can add extra output details which make it through to the client ok. For example, adding speech alongside text...
{
"output": {
"speech": {
"Hi. Please see my website for details."
},
"link": "http://www.example.com",
"text": {
"Hi. Please see http://www.example.com for details."
}
}
}
For the tone, I wondered about making up a custom selection policy, unfortunately it seems to treat it the same as a random selection policy. For example...
{
"output": {
"text": {
"values": [
"Hello. Please see http://www.example.com for more details.",
"Hi. Please see http://www.example.com for details."
]
},
"append": false,
"selection_policy": "tone"
}
}
I could just add a separate tone-sensitive object to output though so that's not a big problem.
Would there be any issues adding things to output in this way?
You can definitely use the output field to specify custom variables you want your client app to see with the benefit that these variables will not persist across multiple dialog rounds (which they would if you would add them to the context field).
Now currently there is no "easy" way how to define your custom selection policy (apart from the random and sequential supported by the runtime right now) - but you could still return an array of possible answers to the client app with some attribute telling the client app which selection policy to use and you would implement this policy in the client app.

Include roles in User.login() and User.getCurrent() Angular SDK

I'm currently writing an administrative interface using the Loopback Angular SDK. After having dug through the documentation and code, I'm still no wiser as to how to include the user's roles in the response. It's causing me real headaches on the frontend because I'm not yet experienced enough with Angular to figure out how to enforce a role check on each of my states (I'm using UI-Router).
client: /auth.js
// Log the user in
$scope.doAuth = function() {
$scope.hasError = false;
$scope.busy = true;
$scope.loginResult = User.login({include: 'roles'}, $scope.credentials,
function wasSuccessfulAuth(authResponse) {
$scope.busy = true;
$rootScope.isAuthenticated = true;
$rootScope.user = authResponse.user;
$location.path('dashboard');
},
function wasFailedAuth(authResponse) {
$timeout(function() {
$scope.hasError = true;
$scope.authError = authResponse.data.error.message || 'Unknown error';
$scope.busy = false;
}, 1000);
}
)
}
server: /common/models/user.json
{
"name": "user",
"plural": "Users",
"base": "User",
"properties": {
},
"relations": {
"roles": {
"type": "belongsTo",
"model": "RoleMapping",
"foreignKey": "principalId"
}
},
"acls": [],
"methods": []
}
So this works in the API explorer, I have the routes I'd expect with an object that has a relation, but I can't seem to get any further than that... All that gets returned is the standard user login stuff (id, accessToken, email, etc) The docs seem to run cold when I get this far but I'd have thought this would have been a common use case?
This is a bit of a showstopper for me.
It's actually surprisingly easy to solve this problem using LoopBack: this is where "model scopes" come in very handy -- including the default scope which I find extremely useful for this type of situation.
First, a brief explanation of model scopes:
A model scope is like a saved query or "view", that allows you to specify a built-in filter for any query for that scope. For example, if you set the default scope to a valid filter, every single query (of any kind) against your model will have this filter applied!
This can get you in a heap of trouble, but there's one use-case that's pretty safe (all other things equal) and actually addresses your question perfectly: when I said a scope lets you give a valid filter, it turns out filters aren't just where clauses, but also include, limit, etc.
So to solve your problem, you simply need a default scope on your User model that includes whatever you need to include. For your example:
Simply add a scope object to your common/models/user.json:
{
"name": "user",
"plural": "Users",
"base": "User",
"scope": {
"include": [
"roles"
]
},
"properties": {
},
"relations": {
"roles": {
"type": "belongsTo",
"model": "RoleMapping",
"foreignKey": "principalId"
}
},
"acls": [],
"methods": []
}
By adding a default scope with an "include", LoopBack will automatically embed the object(s) of the related model based on the named relation (just like if you added it in your query -- which as discussed, is not possible, or at least not easy, in this case).
One caveat: since Role and RoleMapping are built-in models and are surely marked as non-public, I am not actually sure whether you can include them directly (but for a different reason than above). I haven't worked enough with ACLs (yet), but presumably there's more complexity around access controls especially in related models.
So, whereas my example code above explain the mechanics of doing the include here, the policy concern may slow you down (I'd be curious to know if they do).
That said, however, I guess you could add a derived model (from RoleMapping) that you make public (just like you did for user), and use it instead everywhere (including in the relation to/from user) -- hopefully that's clear (let me know if not).
In conclusion: If you add a default scope to your derived user model, to do the include for you, the AngularJS service wrapper (built by lb-ng) will be none the wiser (the include all happens on the backend):
$scope.loginResult = User.login($scope.credentials,
function wasSuccessfulAuth(authResponse) {
console.log('Related models are here: ', authResponse.roles,
authResponse.user);
...
In other words, the resulting model will contain an array, .roles[], containing the related roles to this user (based on your relation definition), and .user with the entire user model right there!
This latter point is unclear but I'm confident in that, because I did exactly the above but with a different related model that I know works. And to my surprise, user was included as well, since that's explicitly requested by the LoopBack $resource wrapper (the one created by lb-ng). So, in fact, you don't have do a separate query to get the user -- it's already there! There's no API that I see when using the $service wrapper, to get at that built-in include and change it; I guess that's what was posted in a comment above.
Hope this is helpful.
Steve
User.login returns AccessToken instance. To get user role, you should make separate request to fetch user, including role.

Resources