Yii2 REST: How to customize Error response? - rest

For first I use this solution - http://www.yiiframework.com/doc-2.0/guide-rest-error-handling.html
But, I want to customize two types error.
When model validation wrong.
When something wrong (Exceptiin)
If model validation wrong i get response, like that:
{
"success": false,
"data": [
{
"field": "country_id",
"message": "Country Id cannot be blank."
},
{
"field": "currency_id",
"message": "Currency Id cannot be blank."
},
{
"field": "originator_id",
"message": "Originator Id cannot be blank."
}
]
}
But i want like that:
{
"success": false,
"data": [
"errors": [
{
"field": "country_id",
"message": "Country Id cannot be blank."
},
{
"field": "currency_id",
"message": "Currency Id cannot be blank."
},
{
"field": "originator_id",
"message": "Originator Id cannot be blank."
}
]
]
}
Second type error i get
{
"success": false,
"data": {
"name": "Exception",
"message": "Invalid request arguments",
"code": 0,
"type": "yii\\base\\InvalidParamException",
]
}
}
But i Want:
{
"success": false,
"data": {
"errors" : 1, <----------------
"name": "Exception",
"message": "Invalid request arguments",
"code": 0,
"type": "yii\\base\\InvalidParamException",
]
}
}
Because in anyway user get 200 Response and they don know about error or mistake.

If you are using default yii/rest/Controller model with error send 422.
Use yii/rest/Controller or yii/rest/ActiveController or extend it.
Or use own Serializer
http://www.yiiframework.com/doc-2.0/yii-rest-serializer.html

Maybe this could guide you, how to change error format:
class JsonErrors
{
public static function validation($model)
{
Yii::$app->response->statusCode = 422;
//Yii::$app->response->format = 'json';
$errorArray = [];
foreach($model->getErrors() as $key => $val) {
$errorArray[] = [
'field' => $key,
'message' => implode(', ', $val) // $val is array (can contain multiple error messages)
];
}
return $errorArray;
}
}
// in controller:
return JsonErrors::validation($model);
// config:
'on beforeSend' => function ($event) {
$response = $event->sender;
if($response->statusCode == 422)
{
$details = $response->data;
if(isset($response->data["message"]) && is_string($response->data["message"])) $details = $response->data["message"];
if(isset($response->data["message"]) && is_array($response->data["message"])) $details = json_decode($response->data['message']);
$response->data = [
'message' => 'Please correct your data with correct values.',
'details' => $details
];
}
}

Related

Possible Notion API bug - Can't create Database Page with URL data

I have a Notion database that I'm trying to write to via the API.
I can write to text fields, but when I try to write to a URL field, I receive the following error:
{
"object": "error",
"status": 400,
"code": "validation_error",
"message": "body failed validation. Fix one:\nbody.properties.O?HT.id should be defined, instead was `undefined`.\nbody.properties.O?HT.name should be defined, instead was `undefined`.\nbody.properties.O?HT.start should be defined, instead was `undefined`."
}
This is the API description of that field:
"LinkedIn": {
"id": "O%3FHT",
"name": "LinkedIn",
"type": "url",
"url": {}
},
This is the body of the API POST request that's failing:
{
"parent": {
"database_id": "f016c02beeff4a34bf298ceb8a8a079f"
},
"properties": {
"title": [
{
"text": {
"content": "Mark Zuckerberg"
}
}
],
"%3DCzE": [
{
"text": {
"content": "CEO"
}
}
],
"HihI": [
{
"text": {
"content": "Facebook"
}
}
],
"JC%3BS": [
{
"text": {
"content": "Menlo Park, CA"
}
}
],
"O%3FHT": {
"url": "https://www.linkedin.com/in/mark-zuckerberg/"
},
"wS%7BN": {
"url": "https://www.linkedin.com/company/facebook/mycompany/verification/"
}
}
}
I'm basing the body contents on the Postman docs provided by Notion: https://www.postman.com/notionhq/workspace/notion-s-api-workspace/documentation/15568543-d990f9b7-98d3-47d3-9131-4866ab9c6df2

ElasticSearch Reindex API and painless script to access date field

I try to familiarize myself with the Reindexing API of ElasticSearch and the use of Painless scripts.
I have the following model:
"mappings": {
"customer": {
"properties": {
"firstName": {
"type": "text",
"fields": {
"keyword": {
"ignore_above": 256,
"type": "keyword"
}
}
},
"lastName": {
"type": "text",
"fields": {
"keyword": {
"ignore_above": 256,
"type": "keyword"
}
}
},
"dateOfBirth": {
"type": "date"
}
}
}
}
I would like to reindex all documents from test-v1 to test-v2 and apply a few transformations on them (for example extract the year part of dateOfBirth, convert a date value to a timestamp, etc) and save the result as a new field. But I got an issue when I tried to access it.
When I made the following call, I got an error:
POST /_reindex?pretty=true&human=true&wait_for_completion=true HTTP/1.1
Host: localhost:9200
Content-Type: application/json
{
"source": {
"index": "test-v1"
},
"dest": {
"index": "test-v2"
},
"script": {
"lang": "painless",
"inline": "ctx._source.yearOfBirth = ctx._source.dateOfBirth.getYear();"
}
}
And the response:
{
"error": {
"root_cause": [
{
"type": "script_exception",
"reason": "runtime error",
"script_stack": [
"ctx._source.yearOfBirth = ctx._source.dateOfBirth.getYear();",
" ^---- HERE"
],
"script": "ctx._source.yearOfBirth = ctx._source.dateOfBirth.getYear();",
"lang": "painless"
}
],
"type": "script_exception",
"reason": "runtime error",
"script_stack": [
"ctx._source.yearOfBirth = ctx._source.dateOfBirth.getYear();",
" ^---- HERE"
],
"script": "ctx._source.yearOfBirth = ctx._source.dateOfBirth.getYear();",
"lang": "painless",
"caused_by": {
"type": "illegal_argument_exception",
"reason": "Unable to find dynamic method [getYear] with [0] arguments for class [java.lang.String]."
}
},
"status": 500
}
According to this tutorial Date fields are exposed as ReadableDateTime so they support methods like getYear, and getDayOfWeek. and indeed, the Reference mentions those as supported methods.
Still, the response mentions [java.lang.String] as the type of the dateOfBirth property. I could just parse it to e.g. an OffsetDateTime, but I wonder why it is a string.
Anyone has a suggestion what I'm doing wrong?

Not Receiving AccessToken on Simulator

My Fulfilment End Point isnt receiving accessToken. Is this normal?
Becouse when im on the simulator it doesnt really ask to login to my authorization end point thus, it wont get a access token. My Oauth is working perfectly after tesing with Oauth Play Ground.
action.json
{
"accountLinking": {
"clientId": "", // SENSITIVE INFORMATION BLANK
"clientSecret": "", // SENSITIVE INFORMATION BLANK
"grantType": "AUTH_CODE",
"authenticationUrl": "", // SENSITIVE INFORMATION BLANK
"accessTokenUrl": "" // SENSITIVE INFORMATION BLANK
},
"actions": [{
"description": "",
"name": "MAIN",
"fulfillment": {
"conversationName": "PASS TEXT"
},
"intent": {
"name": "actions.intent.MAIN",
"trigger": {
"queryPatterns": [
"talk to APP NAME"
]
}
}
}],
"conversations": {
"PASS TEXT": {
"name": "PASS TEXT",
"url": "" // MY FULFILLMENT END POINT
"fulfillmentApiVersion": 2
}
}
}
Fulfilment Received Request
The request was logged into a text file
jsondata = file_get_contents('php://input');
$postdata = json_decode($jsondata, true);
$RAWfile = print_r($postdata, true);
file_put_contents('RAWfile.txt', $RAWfile);
RAWfile.txt
Array
(
[user] => Array
(
[userId] => APhe68HH0PP0nTYnY8jBJed31WqF
[locale] => en-US
)
[conversation] => Array
(
[conversationId] => 1500924702161
[type] => NEW
)
[inputs] => Array
(
[0] => Array
(
[intent] => actions.intent.MAIN
[rawInputs] => Array
(
[0] => Array
(
[inputType] => VOICE
[query] => ask APP NAME to bla bla
)
)
[arguments] => Array
(
[0] => Array
(
[name] => trigger_query
[rawText] => bla bla
[textValue] => bla bla
)
)
)
)
[surface] => Array
(
[capabilities] => Array
(
[0] => Array
(
[name] => actions.capability.AUDIO_OUTPUT
)
)
)
[device] => Array
(
)
[isInSandbox] => 1
)
Simulator Request
{
"query": "bla bla",
"accessToken": "ya29.Gl2TBLrbKjcgK-6jsARmc4Zvkx8qT2X2rE3vsuwEVc_Ey2-q4OUqNWaJPBlXzIjONb_u2MbrE-rgnfwQSZpbZReXsmZjoGOy18Tvp7xzzSJb-cW9SjZ32uLBaKO7vGE",
"expectUserResponse": true,
"conversationToken": "CiZDIzU5O...",
"surface": "GOOGLE_HOME",
"inputType": "VOICE",
"locale": "en-US",
"location": {
"address": "Googleplex, Mountain View, CA, United States",
"position": {
"lat": 37.421980615353675,
"lng": -122.08419799804688
},
"zipCode": "94043",
"city": "Mountain View"
},
"debugInfo": {
"assistantToAgentDebug": {
"assistantToAgentJson": {
"user": {
"userId": "bla",
"locale": "en-US"
},
"conversation": {
"conversationId": "1501004260313",
"type": "NEW"
},
"inputs": [
{
"intent": "actions.intent.MAIN",
"rawInputs": [
{
"inputType": "VOICE",
"query": "bla"
}
],
"arguments": [
{
"name": "trigger_query",
"rawText": "bla",
"textValue": "bla"
}
]
}
],
"surface": {
"capabilities": [
{
"name": "actions.capability.AUDIO_OUTPUT"
}
]
},
"device": {},
"isInSandbox": true
}
}
}
}
Simulator Response
{
"response": "Sure. Here's the test version of bla.\nsure\n",
"conversationToken": "bla",
"audioResponse": "//NExAASS...",
"debugInfo": {
"agentToAssistantDebug": {
"agentToAssistantJson": {
"conversationToken": "{\"state\":null,\"data\":{}}",
"expectUserResponse": false,
"finalResponse": {
"richResponse": {
"items": [
{
"simpleResponse": {
"textToSpeech": "sure",
"displayText": "sure"
}
}
],
"suggestions": []
}
}
}
}
}
}
Account linking is done through the Actions Console at https://console.actions.google.com/
From the "Overview" menu item, you'll need to get to step 5: Account Linking.
In there, you can set your endpoints, scopes required, etc.
Think you have to manually complete that part - if you look in the response, you'll see a URL - copy/paste and run that, it should complete the account linking process -
https://developers.google.com/actions/identity/account-linking
"In the Log area, copy and paste the URL from the debugInfo.sharedDebugInfo.debugInfo field into your web browser. You will be redirected to the authentication URL for your own server that was specified in API.AI or in the Actions Console."

Yii2 overriding fields() to rename fields in case of validation errors

Ref: http://www.yiiframework.com/doc-2.0/guide-rest-resources.html#overriding-fields
I am overriding fields() to rename fields in User model.
GET - response data fields are renamed as expected
POST - validation error field names are not renamed
1. User table columns
public function rules()
{
return [
[['User_Name', 'Password', 'Auth_Token', 'User_Type_Id', 'User_Status_Id'], 'required'],
]
}
2. overriding fields()
public function fields()
{
return [
'user_id' => 'User_Id',
'access_token' => 'Auth_Token',
'user_name' => 'User_Name',
'user_type' => 'userType.User_Type_Name',
'name' => 'userProfile.Fname',
];
}
3. Get User data (fields renamed as expected)
[
{
"user_id": "1",
"access_token": "123",
"user_name": "kiran",
"user_type": "Customer",
"name": "customer"
}
]
Validate model (error field names are not renamed)
[
{
"field": "User_Name",
"message": "User Name cannot be blank."
},
{
"field": "Password",
"message": "Password cannot be blank."
},
{
"field": "Auth_Token",
"message": "Auth Token cannot be blank."
},
{
"field": "User_Type_Id",
"message": "User Type ID cannot be blank."
},
{
"field": "User_Status_Id",
"message": "User Status ID cannot be blank."
}
]
Using getter/setter methods to load the post data inside user model.
Ref: Yii2 REST create with fields()
Overwriting addError() method to change error field name
public function addError($attribute, $error = '') {
$renamed_attr = $this->renamedFields($attribute);
parent::addError($renamed_attr, $error);
}

How to create google datastore composite indices via REST API?

I am trying to change the order of my results but I keep getting an error saying You need an index to execute this query.
In my console, I doesn't say that any indices exist, but I set most of the indexed options to true.
I know in Java, I can create indices that relate to multiple properties either ascending or descending, how do I do this with the REST API?
Following the REST API docs for Google Datastore, my entities are created like this:
{
"mode": "TRANSACTIONAL",
"transaction": "Eb2wksWfYDjkGkkABRmGMQ_vKGijwNwm-tbxAbUPRt8N2RaUCynjSbGT7jFQw3pgaDCT7U0drs3RTPLSIN8TQikdqkdl7pLm2rkMqORmKlO_I_dp",
"mutation": {
"insertAutoId": [
{
"key": {
"path": [
{
"kind": "Attendance"
}
]
},
"properties": {
"section": {
"indexed": true,
"stringValue": "Venturers"
},
"date": {
"dateTimeValue": "2015-01-16T00:00:00+00:00",
"indexed": true
},
"attendee": {
"indexed": true,
"keyValue": {
"path": [
{
"id": "5659313586569216",
"kind": "Attendee"
}
]
}
},
"presence": {
"indexed": false,
"integerValue": 0
}
}
}
]
}
}
And I am trying to query like this:
{
"gqlQuery": {
"allowLiteral": true,
"queryString": "SELECT * FROM Attendance WHERE section = #section ORDER BY date ASC",
"nameArgs": [
{
"name": "section",
"value": {
"stringValue": "Venturers"
}
}
]
}
}
And I get this error:
{
"error": {
"errors": [
{
"domain": "global",
"reason": "FAILED_PRECONDITION",
"message": "no matching index found.",
"locationType": "header",
"location": "If-Match"
}
],
"code": 412,
"message": "no matching index found."
}
}
For future reference:
You can't make a composite index directly through the REST API. You must go through php app engine.
How to build datastore indexes (PHP GAE)