Validate referential integrity of object arrays with Joi - joi

I'm trying to validate that the data I am returned it sensible. Validating data types is done. Now I want to validate that I've received all of the data needed to perform a task.
Here's a representative example:
{
"things": [
{
"id": "00fb60c7-520e-4228-96c7-13a1f7a82749",
"name": "Thing 1",
"url": "https://lolagons.com"
},
{
"id": "709b85a3-98be-4c02-85a5-e3f007ce4bbf",
"name": "Thing 2",
"url": "https://lolfacts.com"
}
],
"layouts": {
"sections": [
{
"id": "34f10988-bb3d-4c38-86ce-ed819cb6daee",
"name": "Section 1",
"content:" [
{
"type": 2,
"id": "00fb60c7-520e-4228-96c7-13a1f7a82749" //Ref to Thing 1
}
]
}
]
}
}
So every Section references 0+ Things, and I want to validate that every id value returned in the Content of Sections also exists as an id in Things.
The docs for Object.assert(..) implies that I need a concrete reference. Even if I do the validation within the Object.keys or Array.items, I can't resolve the reference at the other end.
Not that it matters, but my context is that I'm validating HTTP responses within IcedFrisby, a Frisby.js fork.

This wasn't really solveable in the way I asked (i.e. with Joi).
I solved this for my context by writing a plugin for icedfrisby (published on npm here) which uses jsonpath to fetch each id in Content and each id in Things. The plugin will then assert that all of the first set exist within the second.

Related

GraphQL query result for object that does not exist

I have a GraphQL query that calls a REST service to get the return object. The query contains an Id parameter that is then passed to the service. However, the REST service can respond with http status 404 Not Found if an object with that Id does not exist. That seems like the right response.
How do you model a Not Found response in GraphQL?
Is there a way to inform the GQL caller that something does not exist?
Update
Some options I am considering:
Return null
Change the GrqlhQL Query to return a list of objects and return empty list of nothing is found
Return some kind of error object with an error code
but it is unclear if there is a recommended practice in GQL API design.
You might treat it as an error and handle it accordingly.
I recommend you to check the GraphQL spec, the paragraph about error handling.
I hope it contains exactly what you are looking for.
Basically, you should return whatever you could, and inform a client about potential problems in the "errors" field.
The example from the documentation:
Request:
{
hero(episode: $episode) {
name
heroFriends: friends {
id
name
}
}
}
Response:
{
"errors": [
{
"message": "Name for character with ID 1002 could not be fetched.",
"locations": [ { "line": 6, "column": 7 } ],
"path": [ "hero", "heroFriends", 1, "name" ]
}
],
"data": {
"hero": {
"name": "R2-D2",
"heroFriends": [
{
"id": "1000",
"name": "Luke Skywalker"
},
{
"id": "1002",
"name": null
},
{
"id": "1003",
"name": "Leia Organa"
}
]
}
}
}

REST: update a resource with different fields requiring different user permissions

I have an endpoint /groups
I can create a group by POSTing some info to /groups
A single group can be read by /groups/{id}
I can update some fields in the group by POSTing to /group/{id}
HOWEVER I have different fields that are needed to be updated by users with different permissions, for instance: A group might have the structure
{
"id": 1,
"name": "some name",
"members": [
{
"user_id": 456,
"known_as": "Name 1",
"user": { /* some user object */},
"status": "accepted",
"role": "admin",
"shared": "something"
},
{
"user_id": 999227,
"known_as": "Name 1",
"user": { /* some user object */},
"status": "accepted",
"role": "basic",
"shared": "something"
},
{
"user_id": 9883,
"known_as": "Name 1",
"user": { /* some user object */},
"status": "requested",
"role": "basic",
"shared": "something"
}
],
"link": "https://some-link"
}
As an example I have the following 3 operations for the /group/{id}/members/{id} endpoint:
I want only the user to be able to update his own known_as field
I want only group admins to be able to update each member's role and status fields.
I want both the user and the admin to be able to update the shared field
My options are this:
Should I allow all updates to be done by POSTing to /group/{id}/members/{id} with a subset of the fields for a member and throw an unauthorized error if they try to update a field that they aren't allowed to update?
Or should I break each operation into say /group/{id}/members/{id}/role, /group/{id}/members/{id}/shared and /group/{id}/members/{id}/status? The problem with this is that I don't want to have to make lots of requests to update all the fields (I imagine that there will end up being quite a lot of them).
So just for clarification my question is: Is it considered proper REST to do my option 1 where I can post updates to an endpoint that may fail if you try to change a field that you aren't allowed to?
In my opinion, option 1 is much better than option 2.
As you said option 2 is a waste of bandwidth.
More importantly, with option 1 you can easily implement an atomic update (update "all-or-nothing"). It should either complete successfully or fail entirely. There should never be a partial update.
With option 2 it's very likely the update can be implemented to complete some request successfully and reject another request, even if the two requests are considered a single operation.

GET Values from a custom field via JIRA REST API

I would like to GET all drop down options for a custom field. For system fields, I use the following URI:
http://localhost:8080/rest/api/2/project/XXXX/components
(for components, versons, etc. Basically system fields), so I tried the following for a custom field
http://localhost:8080/rest/api/2/project/XXXX/customfield_10000
and got a 404 error. I'm not sure what I'm doing wrong as I've been googling for the past 19 hours. The best I search result I got was the following documentation: JIRA Developers Documentation
Please assist, I'm not sure What I'm missing
You can get that information either from the createmeta or editmeta REST resources.
Use editmeta if you want to retrieve the available options when editing a specific issue. E.g.
GET /rest/api/2/issue/TEST-123/editmeta
Use createmeta when you want to retrieve the options for a project in combination with an issue type. E.g.
GET /rest/api/2/issue/createmeta?projectKeys=MYPROJ&issuetypeNames=Bug&expand=projects.issuetypes.fields
The customfields with options will be returned like this:
"customfield_12345": {
"schema": {
"type": "string",
"custom": "com.atlassian.jira.plugin.system.customfieldtypes:select",
"customId": 12345
},
"name": "MySelectList",
"allowedValues": [
{
"self": "http://jira.url/rest/api/2/customFieldOption/14387",
"value": "Green",
"id": "14387"
},
{
"self": "http://jira.url/rest/api/2/customFieldOption/14384",
"value": "Blue",
"id": "14384"
}
]
}

Getting specific headers along with other body data in Users.thread: get

You can very easily select what headers you want in a Users.thread: get-request, like so:
format = metadata
metadataHeaders = From
GET https://www.googleapis.com/gmail/v1/users/me/threads/14eaffaf5e3e8242?metadataHeaders=From&format=metadata&key={YOUR_API_KEY}
Response:
{
"id": "14eaffaf5e3e8242",
"historyId": "510358",
"messages": [
{
"id": "14eaffaf5e3e8242",
"threadId": "14eaffaf5e3e8242",
"labelIds": [
"SENT",
"INBOX",
"IMPORTANT"
],
"snippet": "Wow Emil!",
"historyId": "510292",
"internalDate": "1437471536000",
"payload": {
"mimeType": "multipart/mixed",
"headers": [
{
"name": "From", // I just got the header I asked for.
"value": "Emil Tholin <emtholin#gmail.com>"
}
]
},
"sizeEstimate": 9260
}, ...
}
You can also get certain parts of the body very easily. E.g. here I ask for the attachmentIds of all the attachments in the message body:
field = messages/id,messages/payload/parts/body/attachmentId
GET https://www.googleapis.com/gmail/v1/users/me/threads/14eaffaf5e3e8242?fields=messages%2Fid%2Cmessages%2Fpayload%2Fparts%2Fbody%2FattachmentId&key={YOUR_API_KEY}
Response:
{
"messages": [
{
"id": "14eaffaf5e3e8242",
"payload": {
"parts": [
{
"body": { // This message had an attachment.
"attachmentId": "ANGjdJ_0lphTo48BO0xBT_YOSo3tYah23hzpjyATe3GwfziK0I6401P_8-ZYoGuCQPHhpPP0-S_pjL68WIEZzQ0tu72RcIOE4UY3kA4u8PjXPf3Cm5PxVJjmH9N0hm0fFX31RYo8bfZQ6l7bDbYbnCSZbckG7g8enGaKMPbBzIEEC4HXr_YghOYWSfrXKXiFLnxWN4LfsFk3IXUN2tVvMe_0xMhDDfBlqYPnXHr2PhPghq7bQojNxiH4YziIqaKmwiU4xqVfygbae-K-_Q2blyz0EgI4OXjMzwz56Q5e1w"
}
}
]
}
},
{
"id": "14eaffb277b61cd0" // This message had no attachment.
}, ...
]
}
As you can see in the first request, no part of the body is retrieved when asking for specific metadata headers. Individual fields are also hard to pick out in the fields-parameter, since headers are not key-value pairs, but objects on the form { "name": <HEADER_NAME>, "value": <HEADER_VALUE> }.
Is there any way to combine these two requests? I would like to get all the relevant metadata about a message at the same time as getting how many attachments there are in the message.
From Users.threads.get.
"metadata": Returns email headers with message metadata such as identifiers and labels.
The fields parameter is only used to limit the amount of data returned. Since metadata mode is already a limited response you can't use fields to get data outside of that subset. Your best option would be to use your second example with fields and then filter the metadata values locally.

Subscribing to an entity without specifying attributes

I'm trying to subscribe to an entity to get notifications using ONCHANGE.
The thing is that I'd like to get notified when new attributes get added or removed from the entity, otherwise said, I want a notification whenever anything changes on that entity.
Is that possible? I tried setting an empty condValues list in the query like this:
{
"entities": [
{
"type": "case",
"isPattern": "false",
"id": "Case1"
}
],
"reference": "http://localhost:1028/accumulate",
"duration": "P1M",
"notifyConditions": [
{
"type": "ONCHANGE",
"condValues": [
"Test Node 1"
]
}
],
"throttling": "PT5S"
}
But it didn't work.
PS: Note that I omitted the attributes Array to receive all the attributes on notification, this does work.
Current Orion version (0.19.0) doesn't implement such feature. However, it is planed to be done in the future (see this issue at Orion github repository).
EDIT: since Orion 0.27.0 you can subscribe to changes in any attribute. In order to do so, do the subscription omitting the condValues field (or use an empty array [] as value).