ORDA: How do I specify field order when using entitySelection.toCollection()? - 4d-database

Background
I'm converting old 4D Database code to use new ORDA concepts introduced in v17. However, I've noticed an oddity. When I have an entitySelection that I created using ds[$vtTableName].query(), and I convert that entitySelection to a collection (using .toCollection(), the order of the fields that I specify isn't honored.
Example Code:
C_OBJECT($voSelection)
$voSelection:=ds.Users.query("Active = 'True'")
C_COLLECTION($vcUsers)
$vcUsers:=$voSelection.toCollection("FirstName, LastName, DTLastSignin")
Expected Output
I would expect $vcUsers to be a collection of objects, and that each object would look like:
{ "FirstName" : "John", "LastName" : "Smith", "DTLastSignin" : "2019-10-12T32:23:00" }
Actual Output
Instead, I'm getting a different order:
{ "DTLastSignin" : "2019-10-12T32:23:00", "FirstName" : "John", "LastName" : "Smith" }
This has broken some of my API consumers because they expect to be able to specify field order, which the old way (Selection to JSON) respects. However, toCollection() doesn't appear to.
I can't find any sort of documentation about field order and if it is even suppose to. The official documentation shows the fields respecting the order, but maybe it's just a coincidence.

The answer to this is that you can't, because the toCollection() field list is just a filter. Under the hood, the Entity's properties are being looped through in their natural order, and being filtered whether or not the field is one of the specified filter fields.
That is the reason that .toCollection is faster than the clasic Selection to JSON method.
One way around this potentially would be to use use a .map() function. However, that would degrade performance, probably. I haven't done any profiling, so I'm not sure.

Related

Jora name with Dots

I am using the Chrome plugin JsonDiscovery that use JORA to Query in JSON and make JSON query. But know when I consult the MS DevOps API that return the fields with dots in the name I coldnt make the query because JORA understand the dot like to get the next field in the hierarchic.
{
Microsoft.VSTS.Common.ValueArea: "Business",
Microsoft.VSTS.Scheduling.Effort: 40,
Microsoft.VSTS.Scheduling.StartDate: "2021-01-11T03:00:00Z",
}
Dows Someone know how I can make the query with those dots in the name ?
When a property has forbidden chars for an identifier, you should use the same approach as in JavaScript, i.e. $['Microsoft.VSTS.Common.ValueArea'].
For Jora foo['bar'] is the same as foo.bar. However, in first case you can use any chars for a property name, but the second one is faster to type and easier to read.
After many times debugging the Jora, I found the pick method.
..pick("Microsoft.VSTS.Common.ValueArea")

Returning different JSON results for the same request - is this a violation of REST?

Note the following from Roy Fielding concerning REST design, guidelines & principals.
5.2.1.1 Resources and Resource Identifiers
The key abstraction of information in REST is a resource. Any
information that can be named can be a resource: a document or image,
a temporal service (e.g. "today's weather in Los Angeles"), a
collection of other resources, a non-virtual object (e.g. a person),
and so on. In other words, any concept that might be the target of an
author's hypertext reference must fit within the definition of a
resource.
A resource is a conceptual mapping to a set of entities, not the
entity that corresponds to the mapping at any particular point in
time.
More precisely, a resource R is a temporally varying membership
function MR(t), which for time t maps to a set of entities, or values,
which are equivalent. The values in the set may be resource
representations and/or resource identifiers. A resource can map to the
empty set, which allows references to be made to a concept before any
realization of that concept exists -- a notion that was foreign to
most hypertext systems prior to the Web [61]. Some resources are
static in the sense that, when examined at any time after their
creation, they always correspond to the same value set. Others have a
high degree of variance in their value over time.
The only thing that is required to be static for a resource is the
semantics of the mapping, since the semantics is what distinguishes
one resource from another.
The key points have been bolded, the rest of the paragraph I have included is for context.
Here is the scenario.
I have a web api that has a endpoint: http://www.myfakeapi.com/people
When a client does a GET request to this endpoint, they receive back a list of people.
Person
{
"Name": "John Doe",
"Age": "23",
"Favorite Color": "Green"
}
Ok, well that's cool.
But is it against REST design practices and principles if I have a 'Person' who does not have a Favorite Color and I want to return them like this:
Person
{
"Name": "Bob Doe",
"Age": "23",
}
Or should I return them like this:
Person
{
"Name": "Bob Doe",
"Age": "23",
"Favorite Color": null
}
The issue is that the client requesting the resource has to do extra work to see if the property even exist in the first place. Some 'Person's' have favorite colors and some don't. Is it against REST principals to just omit the json property of 'Favorite Color' if they don't exist - or should that property be given a 'null' or blank value?
What does REST say about this? I am thinking that I should give back a null and not change the representation of the resource the client is requesting by omitting properties.
Off the top of my head I can't think of any REST constraints that this violates (here's a link to a brief overview if you're interested). It also doesn't violate idempotency for a GET request. However, it is still bad practice.
The consumer of your API should know what to expect and ideally this should be well documented (I like using Swagger a lot for this). Any changes in what to expect should be communicated to consumers, possibly in the form of release notes. Changes that could potentially be breaking for your consumer should be delivered in a new version of your API.
Since your Person1 and Person2 are technically different object structures, that could be breaking in itself (let's face it, we don't always find the edge cases as devs). You don't just want your API to work on a basic level and to hell with the end users - you want to design it with the end-consumer in mind so that their lives are made easier.
There are various ways we can deal with this, depends upon the use case, I'll list them only by one
1) Prefer enums (only if it makes sense to your use case)
{
"Name": "Bob Doe",
"Age": "23",
"Favorite Color": NO_COLOR
}
When you know the values for your property at the beginning, define a set of enum constants, and assign a default value if the property does not apply to the user. This helps in a few ways:
Your client knows what are the possible values so they can prepare their client system accordingly.
By giving default enum constant, we convey that value of the particular field is successfully retrieved from either persistent storage or maybe from another remote service, but it has default value because the property may not apply to the user OR user doesn't have any value for this property.
By avoiding NULL pattern, your client code will be resilient and the client can prepare their code for default enum constant.
When you start to serve more users, you may need to add a few more enum constants which may not apply to every client of yours. When you add new enums which they don't know, they can easily handle this in their parsing libraries and convert into something as per client application design. In Jackson, we can use DeserializationFeature.READ_UNKNOWN_ENUM_VALUES_AS_NULL for this.
2) Use Null - Do not create enum constants for everything
There are cases perfectly valid to have a NULL object. For instance, in the below example, it makes sense to use null if there is no favourite quote.
{
"Name": "Bob Doe",
"Age": "23",
"Favorite Quote": null
}
3) Document your required properties clearly
If you use swagger for your rest API documentation, you can mark mandatory properties as required. The ones not marked are optional. In that way, the client will be prepared to handle if they are NULL or empty string. (It should apply to other API documentation tools as well)
Bad practice:
I notice a few users code in such a way, they send errors in the same response model they send their success response 200. Refer this question & answer. This is definitely a bad practice. Don't mix two different responses and mark one property as optional - use status codes to convey any problems. I'm not talking about partial response here.
4) Add/Modify properties (as long as you're not breaking a contract with the client)
Say the Favorite Color property is added later and currently you're sending the following response to your client. You will publish your new contract to your clients when you add Favorite Color, but your clients should have fail-safe code and they should handle the unknown properties. In Jackson, we will use DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES for this. Non-breaking changes do not necessarily require v2.
Person
{
"Name": "Bob Doe",
"Age": "23",
}
So, answer to your question is, you should start looking at the first three options while you design your rest API, you don't require to omit any properties. But, you may be required to add a few properties later(covered at #4), which is perfectly fine.

Chef multiple level attribute file override with role JSON

I have an attributes file that looks like this:
default['ftp_provision']['vsftpd']['pasv_ip'] = "192.168.0.10"
where the first attribute is the cookbook name, the second is the program, and the third is the option I want to change, implemented in a template .erb file as:
pasv_ip=<%node['ftp_provision']['vsftpd']['pasv_ip']%>
This is working correctly as expected.
However, I would like to add a role to change these attributes as required for several nodes. I'm using knife role create ftp_node1 to do that doing something like:
"default_attributes": {
"ftp_provision" => {"ftp_provision" => "vsftpd" => "pasv_ip" => "192.168.0.10"}
},
I keep getting syntax errors. All the examples I've been able to see have referenced making JSON files from Ruby DSL with only one level deep of attributes (e.g. default['key']['value']) so I'd like to know how to do this correctly per role.
you'll need to use actual JSON for this, and not sure what you mean about one level deep. this will create a hash 3 or 4 levels deep, depending on how you count it. i haven't seen issues with going further with attributes, and see many cookbooks in the wild with default['really']['freakin']['long']['strings']['of'] = attributes
i took a look at chef's examples and they're using ruby's hash format there rather than json, and that method of creating hashes makes rubocop squawk and say it's been deprecated. i can certainly see how that example would mislead you.
use a linter when building json, here's one https://jsonlint.com/
also I think this may work for you:
{
"ftp_provision": {
"vsftpd": {
"pasv_ip": "192.168.0.10"
}
}
}

CloudFormation Template - any way to get a Spot-Fleet-Request ID?

I'm attempting to create a single template that creates the following:
AWS::EC2::SpotFleet resource
2 AWS::ApplicationAutoScaling::ScalingPolicy resources (scale up, scale down)
Initially, my template included only the SpotFleet resource, and I confirmed that the stack would create without issue. Once I added the ScalingPolicy resources, the stack would rollback because there was "No scalable target registered for namespace..." So, I added an additional resource.
AWS::ApplicationAutoScaling::ScalableTarget resource.
(From http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-applicationautoscaling-scalabletarget.html#cfn-applicationautoscaling-scalabletarget-resourceid)
{
"Type" : "AWS::ApplicationAutoScaling::ScalableTarget",
"Properties" : {
"MaxCapacity" : Integer,
"MinCapacity" : Integer,
"ResourceId" : String,
"RoleARN" : String,
"ScalableDimension" : String,
"ServiceNamespace" : String
}
}
The ResourceID is a required property. I have the data for all the other properties, but when researching what data is needed for the ResourceID property, I have found that the data I need is the spot-fleet-request ID, (something like this: "SpotFleetRequestId": "sfr-73fbd2ce-aa30-494c-8788-1cee4EXAMPLE").
So here's the problem: Since I am creating the spot fleet request in the same template as the scaling policy, I can't put the SpotFleetRequestId in manually, since to my knowledge this is created when the resource is and there's no way to anticipate what the request ID will be. In other templates, with other kinds of resources, I've simply used "Ref" or "Fn::GetAtt" to pass in the arn of a resource without having to manually input this. However--there seems to be no way to do this with a SpotFleetRequestID. All the research I've done has turned up nothing, not even a single template example that uses a method like I'm describing - the only examples available assume that the scalable target resource already exists and the SpotFleetRequestID is known prior to creating the ScalingPolicy.
Does anyone have any idea if referring to the SpotFleetRequestID of an AWS::EC2::SpotFleet initialized in the same template is even possible? Or am I just missing something REALLY obvious?
-KungFuBilly
Turns out that if you "Ref" the logical name of the AWS::EC2::SpotFleet it will return the request ID. Then, it's a matter of using "Fn::Join" to get the right data for the ResourceID. Should look something like this:
"ResourceId": {
"Fn::Join": [
"/",
[
"spot-fleet-request",
{
"Ref": "SpotFleet"
}
]
]
},
That will output: spot-fleet-request/"SpotFleetRequestID"

In CouchDB how do you take in parameters from REST call

Hi so I'm new to CouchDB looks great so far, but really struggling with what must be simple to do!
I have documents structured as:
{
"_id" : "245431e914ce42e6b2fc6e09cb00184d",
"_rev": "3-2a69f0325962b93c149204aa3b1fa683",
"type": "student",
"studentID": "12345678",
"Name": "Test",
"group: "A"
}
And would like to access them them with queries such as http://couchIP/student?group=A or something like that. Are Views what I need here? I don't understand how to take the parameter from the query in the Map functions in Views. example:
function(doc,req) {
if(req.group==='A'){
emit(doc.id, doc.name);
}
}
Is my understanding of how Couch is working wrong or what's my problem here? Thanks in advance, I'm sure this is Couch 101
Already read through http://guide.couchdb.org/ but it didn't really answer the question!
You need views to achieve the desired results.
Define the following map function inside a view of a design document. ( let's name the view "byGroup" and assume this lives in a design document named "_design/students" )
function(doc) {
if(doc.group){
emit(doc.group,null);
}
}
Results can be obtained from the following url
http://couchIP:5984/dbname/_design/students/_view/byGroup?startkey="A"&endkey="A"&include_docs=true
To have friendly url couchdb also provides url rewriting options.
You need to some further reading about views and the relevance that they return key/pair values.
It's not clear what you want to return from the view so I'll guess. If you want to return the whole document you'd create a view like:
function (doc) { emit(doc.group, doc) };
This will emit the group name as a key which you can lookup against, the whole doc will be returned as the value when you look it up.
If you want to just have access to the names of those users you want to do something like:
function (doc) { emit(doc.group, doc.name) };
Your question arises from a misconception about what a view does. Views use map/reduce to generate a representation of your data. You have no control of the output of your view in your query because the view is updated according to changes in your DB documents only.
Using a list is also not a good option. It may seem that you can use knowledge of your request in your list to generate a different output depending on the query parameters but this is wrong because couchdb uses ETags for caching and this means that most times you will get the same answer regardless of your list parameters since the underlying documents won't have changed. There is a trick though to fool couchdb in this case and this implies using two different alternating users but I wouldn't even try this way because surely there are easier ways to achieve your objectives and you can probably solve your problem using group as a key in your map function.