Restrict which HTTP methods on a loopback model relationship - models

So I have a venue model with the following rellationship:
relations": {
"events": {
"type": "hasMany",
"model": "event"
},
},
In the events I have the relationship as:
"relations": {
"venue": {
"type": "belongsTo",
"model": "venue",
"foreignKey": "venueId",
"options": {
"validate": true,
"forceId": false
}
}
},
In the explorer it shows me that I can get, put, post, patch, update and delete to venue/:id/events
Where and how do I say: no matter who you are, this specific relationship can ONLY have GET access and nothing else?

For hasMany Loopback adds the following methods:
__findById__events
__destroyById__events
__updateById__events
__get__events
__create__events
__delete__events
__count__events
Since you only want get access we keep findById, get and count, and disable the rest.
Assuming you are using Loopback 3, you can do this in your model:
Message.disableRemoteMethodByName('prototype.__destroyById__events');
Message.disableRemoteMethodByName('prototype.__updateById__events');
Message.disableRemoteMethodByName('prototype.__create__events');
Message.disableRemoteMethodByName('prototype.__delete__events');
Have a look at the documentation for the more info.
If you are using Loopback 2 you should do this instead(initally written by Kiley Hykawy):
Message.disableRemoteMethodByName('__destroyById__events', false);
Message.disableRemoteMethodByName('__updateById__events', false);
Message.disableRemoteMethodByName('__create__events', false);
Message.disableRemoteMethodByName('__delete__events', false);
false is needed to indicate that it is a non-static method, like prototype for LoopBack 3.

Related

Prevent a repeatable component on Strapi from being deleted by passing an empty array in the Repeater Field mutation?

What would be the best way to prevent a repeatable component on Strapi from being inadvertently deleted by passing an empty array in the mutation?
As an example, I have the following Collection Type:
"kind": "collectionType",
"collectionName": "books",
"info": {
"name": "Books"
},
"options": {
"increments": true,
"timestamps": true
},
"attributes": {
"pages": {
"type": "component",
"repeatable": true,
"component": "page.pages"
}
}
}
If I have passed in an empty array for the repeatable fields (pages) then it appears to remove the entire repeatable component. For context, I want users to be able to update the elements in the repeatable component but restrict wiping everything out. Per this thread, it appears I have to send in the entire array in the mutation or put request. What I don't want is for someone to send an empty array and delete everything.
mutation {
updateBook(input: {
where: { id: "602ea4add988a28e57fb355a" },
data: {
pages: []
}
}){
book {
id
pages {
text
}
}
}
}
Aparently you have to query the data on the component and then add it again with the new entry
"in strapi groups i can't just add the new entry, i have to include all previous ones or otherwise they will be deleted !
currently to update an entry in a group you have to use the parent update(), or there is an other way ?"
Originally posted by #bouhendfaycal in https://github.com/strapi/strapi/issues/4590#issuecomment-558741136

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

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.

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.

Amazon Alexa Device Discovery for Smart Home API with Lambda Failing

I have setup an Alexa Smart Home Skill, all settings done, oauth2 processed done and skill is enabled on my Amazon Echo device. Lambda function is setup and linked to the skill. When I "Discover Devices" I can see the payload hit my Lambda function in the log. I am literally returning via the context.succeed() method the following JSON with a test appliance. However Echo tells me that it fails to find any devices.
{
"header": {
"messageId": "42e0bf9c-18e2-424f-bb11-f8a12df1a79e",
"name": "DiscoverAppliancesResponse",
"namespace": "Alexa.ConnectedHome.Discovery",
"payloadVersion": "2"
},
"payload": {
"discoveredAppliances": [
{
"actions": [
"incrementPercentage",
"decrementPercentage",
"setPercentage",
"turnOn",
"turnOff"
],
"applianceId": "0d6884ab-030e-8ff4-ffffaa15c06e0453",
"friendlyDescription": "Study Light connected to Loxone Kit",
"friendlyName": "Study Light",
"isReachable": true,
"manufacturerName": "Loxone",
"modelName": "Spot"
}
]
}
}
Does the above payload look correct?
According to https://developer.amazon.com/public/solutions/alexa/alexa-skills-kit/docs/smart-home-skill-api-reference#discovery-messages the version attribute is required. Your response seems to be missing that attribute.
In my (very short) experience with this, even the smallest mistake in the response would generate a silent error like the one you are experiencing.
I had the same problem. If you are creating discovery for "Entertainment Device", make sure you have wrapped the output in 'event' key for context.succeed
var payload = {
endpoints:
[
{
"endpointId": "My-id",
"manufacturerName": "Manufacturer",
"friendlyName": "Living room TV",
"description": "65in LED TV from Demo AV Company",
"displayCategories": [ ],
"cookie": {
"data": "e.g. ip address",
},
"capabilities":
[
{
"interface": "Alexa.Speaker",
"version": "1.0",
"type": "AlexaInterface"
},
]
}
]
};
var header = request.directive.header;
header.name = "Discover.Response";
context.succeed({ event: {
header: header, payload: payload
} });
Although, in the sample code, this is never mentioned and an incorrect example is given (https://developer.amazon.com/public/solutions/alexa/alexa-skills-kit/docs/steps-to-create-a-smart-home-skill). However, the response body provided includes the "event" key.
Recreating lambda function helped me fix the issue. I also set "Enable trigger" check button while creating, though I'm not sure if that matters. After that my device provided by skill was found successfully.
Edit: Answer was wrong. Only useful information was this
This context.fail syntax is actually deprecated. Look up the Lambda context object properties, it should look more like "callback(null, resultObj)" now.
Did you include the return statement in your function?
return {
"header": header,
"payload": payload
}
It was missing in the example and after adding it, I was able to 'discover' my device.

Some doubts about the use of parameters into a request toward a REST web service?

I am pretty new in Spring and in REST web services and I have the following dout following a tutorial that show how to implement a RESTful web service using Spring MVC.
So, into a controller class I have this method:
#Controller
#RequestMapping("/api/categories")
public class CategoryRestController {
#RequestMapping
#ResponseBody
public CategoryList getCategories(#RequestParam("start") int start, #RequestParam("size") int size ) {
List<Category> categoryEntries = categoryService.findCategoryEntries(start, size);
return new CategoryList(categoryEntries);
}
}
This method handle HTTP GET request toward the resoruce /api/categories and return the retrieved list into JSON format (I think that it depends by the content negotiazion: if the caller put the Accept header as JSON the method return the result in JSON format, is it right?)
By the way my doubt is related the HTTP request shown in the tutorial, infact it do:
http://localhost:8080/springchocolatestore/api/categories?start=0&size=2
that is handled by the previous controller method to return a paginated list (that could be hude) in JSON format, infact I retrieve the following output:
{
"categories": [
{
"links": [
{
"rel": "self",
"href": "http://localhost:8080/springchocolatestore/api/categories/1",
"variables": [],
"templated": false,
"variableNames": []
}
],
"name": "Truffles",
"description": "Truffles",
"id": {
"rel": "self",
"href": "http://localhost:8080/springchocolatestore/api/categories/1",
"variables": [],
"templated": false,
"variableNames": []
}
},
{
"links": [
{
"rel": "self",
"href": "http://localhost:8080/springchocolatestore/api/categories/2",
"variables": [],
"templated": false,
"variableNames": []
}
],
"name": "Belgian Chocolates",
"description": "Belgian Chocolates",
"id": {
"rel": "self",
"href": "http://localhost:8080/springchocolatestore/api/categories/2",
"variables": [],
"templated": false,
"variableNames": []
}
}
]
}
Ok, so in the request I specify the pagination parameter by categories?start=0&size=2
My doubt is related to the user of this parameter. From what I have understand (but maybe it could be wrong) the use of the parameter is against the RESTful principles. Is it true or am I mising something?
Or maybe in this specific case are valid because the parameter are not specifing an object (that have to be returned into my JSON output) but are only related to some options?
I mean that maybe I can't use parameter to specify a specific object, something like this:
// RETRIEVE THE PRODUCT WITH ID=1
http://localhost:8080/springchocolatestore/api/producs?product=1
So I think that the previous is not following the RESTfull standard because I am specifing a product object with a parameter and not accessing to it as a resource, so I have to do in this way:
http://localhost:8080/springchocolatestore/api/producs/1
Can you give me some clarification?
Tnx
REST doesn't have much to do with URLs, and using request parameters is not unRESTful.
But I agree that path variables are generally used to identify a specific resource, and parameters are generally used for search or pagination parameters.

Resources