Deserializing Json-Api with Rails Strong Parameters - active-model-serializers

I am using Active Model Serializers 0.10.x with EmberCLI and Rails while trying to have the Json-Api as the Adapter. GET requests are working, but deserialization for the Active Model is not even though I tried to implement the rails strong_parameters solution described by jimbeaudoin here.
My latest attempt in saving a comment:
Payload:
{"data":{
"attributes": {"soft_delete":false,"soft_delete_date":null,"text":"hjfgfhjghjg","hidden":false,"empathy_level":0},
"relationships":{
"user":{"data":{"type":"users","id":"1"}},
"post":{"data":{"type":"posts","id":"1"}}},"type":"comments"}}
Console Output:
Completed 400 Bad Request in 13ms (ActiveRecord: 8.6ms)
ActionController::ParameterMissing (param is missing or the value is empty: data):
Comments Controller:
class Api::V1::CommentsController < MasterApiController
respond_to :json
...
def create
render json: Comment.create(comment_params)
end
...
def comment_params
#Deserialization issues... Waiting for #950 https://github.com/rails-api/active_model_serializers/pull/950
params.require(:data).require(:attributes).permit(:text, :user_id, :post_id, :empathy_level, :soft_delete_date, :soft_delete, :hidden)
end
end
Noting that if I set the parameters to only params.permit(...), the server saves it with everything null (I did not set any constraints on the comments model for now):
data: {id: "9", type: "comments",…}
attributes: {soft_delete: null, soft_delete_date: null, text: null, hidden: null, empathy_level: null}
id: "9"
relationships: {post: {data: null}, user: {data: null}}
type: "comments"
You can access the full code here.

Update #2: For AMS >= 0.10.2, please check other answers.
Update #1: Answer is still valid for AMS 0.10.1.
If you use 0.10.0.rc4, you can now use the Deserialization implementation described on Active Model Serializers #1248.
def post_params
ActiveModel::Serializer::Adapter::JsonApi::Deserialization.parse(params.to_h)
// or (params.to_unsafe_h) in some cases like in my example below...
end
Bonus: If you use Ember Data, then you can see an example implementation on my Fakktion Github repo.

For AMS >= 0.10.2
In 0.10.2 there was a cleanup so after 0.10.2 use:
def post_params
ActiveModelSerializers::Deserialization.jsonapi_parse(params)
end
Reference:
https://github.com/rails-api/active_model_serializers/commit/252f9c4ae932e6280dfe68605d495b208fe22ba7

With AMS 0.10.2+
Use only hash to create a parameter whitelist,
def post_params
ActiveModelSerializers::Deserialization.jsonapi_parse!(
params, only: [:title, :author, :tags]
)
end

For googlers:
If you have empty data payload, you need to add Mime support
https://stackoverflow.com/a/32013294/2664779
When you want to access json-api formatted json, you should do it like this
(in your controller)
def create
user = User.new(user_params)
...
end
private
def user_params
params.require(:data).require(:attributes).permit(:email, :password)
end
When previously I would do it like this
private
def user_params
params.require(:user).permit(:email, :password)
end

Related

GPT3 completion with insertion - invalid argument :suffix

I am trying out completions using insertions.
It seems that I am supposed to use a parameter called suffix: to inform where the end of the insert goes.
The payload to the endpoint: POST /v1/completions
{
"model": "code-davinci-002",
"prompt": "Write a JSON document for a person with first name, last name, email and phone number\n\n{\n",
"suffix": "\n}",
"temperature": 0,
"max_tokens": 256,
"top_p": 1,
"frequency_penalty": 0,
"presence_penalty": 0
}
I tried doing this from a ruby implementation of GPT3.
parameters
=> {
:model=>"code-davinci-001",
:prompt=>"generate some JSON for a person with first and last name {",
:max_tokens=>250,
:temperature=>0,
:top_p=>1,
:frequency_penalty=>0,
:presence_penalty=>0,
:suffix=>"\n}"}
post(url: "/v1/completions", parameters: parameters)
I get an invalid argument error for suffix
{"error"=>{"message"=>"Unrecognized request argument supplied: suffix", "type"=>"invalid_request_error", "param"=>nil, "code"=>nil}}
I looked at the Payload from OpenAI vs the payload from the Ruby Library and saw the issue.
My ruby library was setting the model to code-davinci-001 while OpenAI was using code-davinci-002.
As soon as I manually altered the model: attribute in debug, the completion started working correctly.
{
"id"=>"cmpl-5yJ8b01Cw26W6ZIHoRSOb71Dc4QvH",
"object"=>"text_completion",
"created"=>1665054929,
"model"=>"code-davinci-002",
"choices"=>
[{"text"=>"\n \"firstName\": \"John\",\n \"lastName\": \"Smith\"",
"index"=>0,
"logprobs"=>nil,
"finish_reason"=>"stop"}],
"usage"=>{"prompt_tokens"=>14, "completion_tokens"=>19,
"total_tokens"=>33}
}

What does Depends with no parameter do?

I am trying to implement JWT with fastapi.
Currently looking at the following libraries
fastapi-users
FastAPI JWT Auth
In both cases, I see Depends() in method parameter.
What does Depends do when there is nothing in the parameter?
https://github.com/frankie567/fastapi-users/blob/master/fastapi_users/router/auth.py
#router.post("/login")
async def login(
response: Response, credentials: OAuth2PasswordRequestForm = Depends()
):
https://indominusbyte.github.io/fastapi-jwt-auth/usage/basic/
#app.post('/login')
def login(user: User, Authorize: AuthJWT = Depends()):
I undestand when there's a function inside the parameter but would you teach me what it does when there's no parameter with Depends?
Depends() without arguments is just a shortcut for classes as dependencies.
You see that we are having some code repetition here, writing CommonQueryParams twice:
commons: CommonQueryParams = Depends(CommonQueryParams)
FastAPI provides a shortcut for these cases, in where the dependency is specifically a class that FastAPI will "call" to create an instance of the class itself.
For those specific cases, you can do the following:
Instead of writing:
commons: CommonQueryParams = Depends(CommonQueryParams)
...you write:
commons: CommonQueryParams = Depends()
You declare the dependency as the type of the parameter, and you use Depends() as its "default" value (that after the =) for that function's parameter, without any parameter in Depends(), instead of having to write the full class again inside of Depends(CommonQueryParams).
In both cases, I see Depends() in method parameter. What does Depends do when there is nothing in the parameter?
It's a great question.
Assume you have the following code.
from fastapi import FastAPI, Depends
from pydantic import BaseModel
from typing import Optional
class Location(BaseModel):
city: str
country: str
state: str
app = FastAPI()
#app.post("/withoutdepends")
async def with_depends(location: Location):
return location
#app.post("/withdepends")
async def with_depends(location: Location = Depends()):
return lcoation
We have the same Location model in two different endpoints, one uses Depends other one not.
What is the difference?
Since FastAPI is based on OpenAPI specification, we can start discovering the difference from auto-generated Swagger's docs.
This is without Depends, it expects a Request Body.
This is with Depends, it expects them as Query parameters.
How this is useful and how it works?
Actually, this is it, it expects a Callable there.
But when you use a Pydantic Model with Depends, it actually creates a query parameter for parameter inside init __init__ function.
So for example, this is the model we used above.
class Location(BaseModel):
city: str
country: str
state: str
It becomes this with Depends.
class Location(BaseModel):
def __init__(self, city: str, country: str, state: str) -> None:
...
Then they will become query parameters. This is the OpenAPI schema for the /withdepends endpoint.
"parameters": [
{
"required":true,
"schema":{
"title":"City",
"type":"string"
},
"name":"city",
"in":"query"
},
{
"required":true,
"schema":{
"title":"Country",
"type":"string"
},
"name":"country",
"in":"query"
},
{
"required":true,
"schema":{
"title":"State",
"type":"string"
},
"name":"state",
"in":"query"
}
]
This is the OpenAPI schema it created for /withoutdepends endpoint.
"requestBody": {
"content":{
"application/json":{
"schema":{
"$ref":"#/components/schemas/Location"
}
}
},
"required":true
}
Conclusion
Instead of request body, you can create query parameters with the same model.
Pydantic models are very useful for the cases when you have +5 parameters. But it expects a request body by default. But OpenAPI specification doesn't allow request body in GET operations. As it says in the specification.
GET, DELETE and HEAD are no longer allowed to have request body because it does not have defined semantics as per RFC 7231.
So by using Depends you are able to create query parameters for your GET endpoint, with the same model.

How to force nodejs script argument to take value from choices list using commander?

I am using commander in node.js script. I am able to set default value to one argument.
var args = require('commander')
// set 'pending' as defaut value of status
args.option('-s --status <statusString>', 'Status to filter questions', 'pending').parse(process.argv)
console.log('status:', args.status)
How can I force status value to be from ["pending", "rejected", "accepted", "hold"] only? I did not find anything relevant in documention.
This is what I could achieve:
var options = ["pending", "rejected", "accepted", "hold"];
args.option(
'-s --status <statusString>',
`Status to filter questions: ${options}`,
function(val, _) {
if (!options.includes(val)) {
throw Error(`${val} is not from ${options}`);
}
return val;
},
'pending')
.parse(process.argv)
Not perfect, since you need to format help string and validate input value by yourself. Also, throwing an error from the validation function is not handled by Commander nicely and causes it to fail with the whole stacktrace in the output. I could not find a better way to tell Commander that the input is invalid.
In my case I finally just switched to argparse, which is a clone of the Python command line parser, and seems to be better thought over. This is how you limit choices with it:
const ArgumentParser = require('argparse').ArgumentParser;
const argparser = new ArgumentParser({ description: 'An example'});
argparser.addArgument('--status', {
choices: ['pending', 'rejected', 'accepted', 'hold'],
defaultValue: 'pending'});
const args = argparser.parseArgs();
This will do the job, including nice help message and input validation.
I think that in your case this is what you were looking for:
args
.addOption(
new Option('-s --status <statusString>', 'Status to filter questions')
.choices(["pending", "rejected", "accepted", "hold"])
.default('pending')
)
.parse(process.argv)
Also please take a look to another specific example I've prepared and tested successfully (NPM commander v8.2.0)
program
.addOption(
new Option('-s --status <statusString>', 'Status to filter questions')
.choices(["pending", "rejected", "accepted", "hold"])
.default('pending')
)
.action((args) => {
console.log(args)
})
Side note: please notice that for this second example I've used a slightly different naming convention for clarity: I've used program (vs original args) in the first line, as I was planning to use args name for the variable array received in the arrow function used in action() instead. Don't let that change confuse you! ;)
IMPORTANT: more on the official examples, direct link to a related example right here: https://github.com/tj/commander.js/blob/HEAD/examples/options-extra.js#L13

How to remove the `__typename` field from the graphql response which fails the mutations

I tried changing the addTypeName: false in the Apollo client in GraphQL
apollo.create({
link: httpLinkWithErrorHandling,
cache: new InMemoryCache({ addTypename: false }),
defaultOptions: {
watchQuery: {
fetchPolicy: 'network-only',
errorPolicy: 'all'
}
}
But it works and it throws the following messages in the console
fragmentMatcher.js:26 You're using fragments in your queries, but either don't have the addTypename:true option set in Apollo Client, or you are trying to write a fragment to the store without the __typename.Please turn on the addTypename option and include __typename when writing fragments so that Apollo Clientcan accurately match fragments.
,
Could not find __typename on Fragment PopulatedOutageType
and
fragmentMatcher.js:28 DEPRECATION WARNING: using fragments without __typename is unsupported behavior and will be removed in future versions of Apollo client. You should fix this and set addTypename to true now.
even if i change false to true new InMemoryCache({ addTypename: true }), the mutations start failing because of the unwanted typename in the mutation
is there any way to resolve this issue
Cleaning Unwanted Fields From GraphQL Responses
In the above thread, I have posted the answer for this problem please refer to it

Watson Conversation API says: 'Patterns are defined but type is not specified.'

I am trying to call the updateValue method of the Watson Conversation API using the Watson SDK for Node.js. The request updates the patterns of the patterns-type entity value.
My request fails with a 400 Bad Request and the message:
[ { message: 'Patterns are defined but type is not specified.',
path: '.patterns' } ],
Here is the code I'm using to call the API - pretty standard.:
let params = {
workspace_id: '<redacted>',
entity: 'myEntityType',
type: 'patterns', // tried with and without this line
value: 'myCanonicalValue',
new_patterns: ['test'],
};
watsonApi.updateValue(params, (error, response) => {
if (error) {
console.log('Error returned by Watson when updating an entity value.');
reject(error);
} else {
resolve(response);
}
});
Actually, what the request is doing is trying to delete a pattern from the pattern list. Since there is no endpoint for deleting patterns, I fetch the list of patterns, delete the one I need to delete from the pattern list, and send the now-reduced patterns list via the updateValue method. In the above example, imagine the pattern list was ['test', 'test2']. By calling updateValue with ['test'] only, we are deleting the test2 pattern.
I am using a previous API version but I've also tested it in the Assistant API Explorer and the version 2018-07-10 results in the same problem when sending a raw request body formed as follows:
{
"patterns": ["test"]
}
Am I doing something wrong or did I forget a parameter?
It's not a bug, but it is a non-intuitive parameter name. The service accepts a type parameter and the Node SDK has a wrapper parameter called new_type. If you are using this to update patterns and not synonyms (the default), then you need to specify new_type as "patterns" even though the parameter is listed as optional.
This appears to be a bug in Watson Conversation Node.js SDK.
To avoid this, always add new_type: 'patterns' to the params:
let params = {
workspace_id: '<redacted>',
entity: 'myEntityType',
new_type: 'patterns',
value: 'myCanonicalValue',
new_patterns: ['test'],
};
I read the Watson Assistant API for updateValue the following way:
The new_type parameter is not required, valid values are synonyms or patterns. However, if you don't provide that parameter, the default kicks in. According to the documentation the default is synonyms. This would explain the error when you pass in patterns.

Resources