My code for saving document to API looks like that
save(category) {
category.save().then(() => {
this.transitionTo('categories');
}).catch((adapterError) => {
console.log(category.get('errors').toArray());
console.log(category.get('isValid'));
});
},
When API answers is:
{"errors":[{"attribute":"name","message":"This value should not be blank."}]}
then
category.get('isValid')
still returns true.
My question is, how validation errors should looks like?
By default, ember-data's adapter determines that a response is invalid when the status code is 422. You can override the isInvalid function of the adapter to change this.
Also, ember-data now expects errors to be formatted into a json-api error object. If your backend doesn't return it in this format, you can transform it in ember by overriding the handleResponse function of the adapter.
This is a sample json-api error:
{"errors": [
{
"detail": "Must be unique",
"source": { pointer: "/data/attributes/title"}
},
{
"detail": "Must not be blank",
"source": { pointer: "/data/attributes/content"}
}
]}
If you're returning error responses you described above, you would have to do something like this in your adapter:
handleResponse(status, headers, payload) {
if (status === 422 && payload.errors) {
let jsonApiErrors = [];
for (let key in payload.errors) {
for (let i = 0; i < payload.errors[key].length; i++) {
jsonApiErrors.push({
detail: payload.errors[key][i],
source: {
pointer: `data/attributes/${key}`
}
});
}
}
return new DS.InvalidError(jsonApiErrors);
} else {
return this._super(...arguments);
}
}
Related
I have the method which gets data from contentful using graphql and returns some data:
exports.getMetadata = async (graphql, reporter, query) => {
const result = await graphql(query)
if (result.errors) {
reporter.panicOnBuild("Error while running medatada GraphQL query")
}
const {
data: {
allContentfulPages: {
edges: {
0: {
node: { meta, opengraph },
},
},
},
},
} = result
const metaJson = JSON.parse(meta.internal.content)
const opengraphJson = JSON.parse(opengraph.internal.content)
return { metaJson, opengraphJson }
}
that's how graphql query looks:
query {
# since our Contentful has enabled "locales", but pages slug doesn't need it, get only default language data
allContentfulPages(filter: { node_locale: { eq: "en-US" }, slug:{eq: "insights"} }) {
edges {
node {
meta {
internal {
content
}
}
opengraph {
internal {
content
}
}
}
}
}
}
when i start project executing npm run develop everything works fine and i don't have any error in console but while building npm run build i get TypeError: Cannot read property 'node' of undefined i tried to add statement like if result !== null ... and if result....edges[0].node !== null in many variants it didn't work, application all time breaks in one place. Please help me to figure out what;s going on ?
Too much [unguarded/unconditional] decomposition... stop at must exist node:
const { data: { allContentfulPages: { edges }}} = result;
if( edges && edges[0] ) {
return {
metaJson: JSON.parse(edges[0].node.meta.internal.content),
opengraphJson: JSON.parse(edges[0].node.opengraph.internal.content)
};
I am trying to setup a wiremock stub that will return a 400 error if any field has a null value in the json payload. Basically to simulate a Bad Request. I've been trying with a regex that matches any lowercase string for the json key but it doesn't seem to like it. I can't find any examples of what I want online so not sure if it's even possible.
My Bad Request body:
{
"cat": null,
"dog": {
"id": 1344
},
"horse": {
"id": 1
},
"fish": 1
}
My Stub:
wireMockServer.stubFor(post(urlEqualTo("/sample-api"))
.withRequestBody(matchingJsonPath("$.^[a-z]*", equalTo(null)))
.willReturn(aResponse()
.withStatus(400)
.withHeader("Content-Type", "application/json")))
In this example I would expect the stub to match "cat" as the value of it is null. This isn't the case. Can anyone tell me what I'm doing wrong?
In the WireMock documentation on Request Matching the section on JSON Path matching. In the source code there is a reference to com.jayway.jsonpath.JsonPath library used. The build.gradle refers to version 2.4.0. The documentation for the Jayway JSON Path library can be found on their Github project page. There is a good, but by no means perfect online evaluator here.
The WireMock documentation only shows support for Regular Expression for the node values in the form of the "matchesJsonPath". In the Jayway documenatation there is an online example: $..book[?(#.author =~ /.*REES/i)]. For this reason the only approach is to name all the nodes that are not allowed to be null.
In the below example mapping all the mentioned nodes will be tested, regardless of their depth (see #id). This mapping will not trigger if all the mentioned nodes are not null, but some unmentioned ones are.
{
"request": {
"urlPattern": "/sample-api",
"method": "GET",
"bodyPatterns" : [ {
"matchesJsonPath" : "$..[?(#.cat == null || #.dog == null || #.horse == null || #.fish == null || #.id == null)]"
} ]
},
"response": {
"status": "400",
"headers": {
"Content-Type": "application/json; charset=utf-8"
},
"jsonBody": {
"message": "some sapi message"
}
}
}
If you weren't aware of all possible keys, you could use a Custom Request Matcher to check if the request body contained any null values, and if so, return your 400 error. I'd recommend creating your own class, something that resembles...
public class BodyNullCheck extends RequestMatcherExtension {
#Override
public MatchResult match(Request request, Parameters parameters) {
JSONParser parser = new JSONParser();
try {
JSONObject body = (JSONObject) parser.parse(request.getBody().toString());
for(Iterator iterator = body.keySet().iterator(); iterator.hasNext();) {
String key = (String) iterator.next();
if (body.get(key) == null) {
return MatchResult.of(true);
}
}
} catch (ParseException ex) {
ex.printStackTrace();
}
return MatchResult.of(false);
}
}
The above takes the request body and converts it to a JSONObject, and then iterates over all keys in the JSONObject. If any of their values are null, then we will return true. If after iterating over all of them, a null value isn't found, we return false.
I'm performing a query to get PowerMeter details in which contains another type inside called Project. I write the query this way:
query getPowerMeter($powerMeterId: ID!) {
powerMeter: powerMeter(powerMeterId: $powerMeterId) {
id
name
registry
project {
id
name
}
}
}
When I perform the query for the first time, project is successfully returned. The problem is that when I perform subsequent queries with the same parameters and default fetchPolicy (cache-first), project isn't returned anymore.
How may I solve this problem?
Also, I call readFragment to check how powerMeter is saved in the cache and the response shows that powerMeter has project saved.
const frag = client.readFragment({
fragment: gql`
fragment P on PowerMeter {
id
name
registry
project {
id
name
}
}
`,
id: 'PowerMeter:' + powerMeterId,
});
Power Meter returned first time
{
"powerMeter":{
"id":"7168adb4-4198-443e-ab76-db0725be2b18",
"name":"asd123123",
"registry":"as23",
"project":{
"id":"41d8e71b-d1e9-41af-af96-5b4ae9e492c1",
"name":"ProjectName",
"__typename":"Project"
},
"__typename":"PowerMeter"
}
}
Fragment after calling power meter first time
{
"id":"7168adb4-4198-443e-ab76-db0725be2b18",
"name":"asd123123",
"registry":"as23",
"project":{
"id":"41d8e71b-d1e9-41af-af96-5b4ae9e492c1",
"name":"ProjectName",
"__typename":"Project"
},
"__typename":"PowerMeter"
}
Power Meter returned second time
{
"powerMeter":{
"id":"7168adb4-4198-443e-ab76-db0725be2b18",
"name":"asd123123",
"registry":"as23",
"__typename":"PowerMeter"
}
}
Fragment after calling power meter second time
{
"id":"7168adb4-4198-443e-ab76-db0725be2b18",
"name":"asd123123",
"registry":"as23",
"project":{
"id":"41d8e71b-d1e9-41af-af96-5b4ae9e492c1",
"name":"ProjectName",
"__typename":"Project"
},
"__typename":"PowerMeter"
}
Edit 1: Fetching Query
The code below is how I'm fetching data. I'm using useApolloClient and not a query hook because I'm using AWS AppSync and it doesn't support query hook yet.
import { useApolloClient } from '#apollo/react-hooks';
import gql from 'graphql-tag';
import { useEffect, useState } from 'react';
export const getPowerMeterQuery = gql`
query getPowerMeter($powerMeterId: ID!) {
powerMeter: powerMeter(powerMeterId: $powerMeterId) {
id
name
registry
project {
id
name
}
}
}
`;
export const useGetPowerMeter = (powerMeterId?: string) => {
const client = useApolloClient();
const [state, setState] = useState<{
loading: boolean;
powerMeter?: PowerMeter;
error?: string;
}>({
loading: true,
});
useEffect(() => {
if (!powerMeterId) {
return setState({ loading: false });
}
client
.query<GetPowerMeterQueryResponse, GetPowerMeterQueryVariables>({
query: getPowerMeterQuery,
variables: {
powerMeterId,
},
})
.then(({ data, errors }) => {
if (errors) {
setState({ loading: false, error: errors[0].message });
}
console.log(JSON.stringify(data));
const frag = client.readFragment({
fragment: gql`
fragment P on PowerMeter {
id
name
registry
project {
id
name
}
}
`,
id: 'PowerMeter:' + powerMeterId,
});
console.log(JSON.stringify(frag));
setState({
loading: false,
powerMeter: data.powerMeter,
});
})
.catch(err => setState({ loading: false, error: err.message }));
}, [powerMeterId]);
return state;
};
Edit 2: Fetching Policy Details
When I use fetchPolice equals cache-first or network-only, the error persists. When I use no-cache, I don't get the error.
I think this might have been the solution:
https://github.com/apollographql/apollo-client/issues/7050
Probably way too late, but it could help people coming to this issue in the future.
When using apollo client's InMemoryCache it seems you need to provide a list of possible types so the fragment matching can be done correctly when using the InMemoryCache.
You can do that manually when having few union types and a pretty stable API which doesn't change very often.
Or you automatically generate these types into a json file, which you can use directly in the InMemoryCache's possibleTypes config directly.
Visit this link to the official docs to find out how to do it.
Cheers.
According to Expo documentation with SQLite I would make a query like so:
tx.executeSql(sqlStatement, arguments, success, error)
I execute it like this:
db.transaction(tx => {
tx.executeSql('SELECT * FROM dr_report_properties WHERE orderId = (?)', [this.state.orderId]);
},
error => {
alert(error);
},
(tx, results) => {
console.log(results);
}
);
My question is how do I get the response? The above returns as undefined.
I then try (not expecting it to work but just for kicks)
console.log(tx);
This does give a console.log
(tx, results) => {
console.log('I got data');
}
)
According to the documentation:
ResultSet objects are returned through second parameter of the success callback for the tx.executeSql() method on a Transaction (see above). They have the following form:
{
insertId,
rowsAffected,
rows: {
length,
item(),
_array,
},
}
I would expect result would be this object. Any ideas at what I'm doing wrong?
The problem with the above was that I placed the call AFTER the execution it's actually in the same method as that.
The result should have gone in the callback like so:
db.transaction(
tx => {
tx.executeSql('select * from my_table', [], (trans, result) => {
console.log(trans, result)
});
}
);
Thanks to #charliecruzan from expo team!
I want to extend my api using loopback . I have read the documentation
'use strict';
module.exports = function(Meetups,pusher) {
Meetups.status = function(cb) {
var currentDate = new Date();
var currentHour = currentDate.getHours();
var OPEN_HOUR = 6;
var CLOSE_HOUR = 20;
console.log('Current hour is %d', currentHour);
var response;
if (currentHour >= OPEN_HOUR && currentHour < CLOSE_HOUR) {
response = 'We are open yeah!!! for business.';
} else {
response = 'Sorry, we are closed. Open daily from 6am to 8pm.';
}
cb(null, response);
};
Meetups.remoteMethod(
'status', {
http: {
path: '/status',
verb: 'get'
},
returns: {
arg: 'status',
type: 'string'
}
}
);
Meetups.pusher = function(cb) {
if (2>1) {
response = 'sending something';
} else {
response = 'mont blanc';
}
cb(null, response);
};
Meetups.remoteMethod(
'pusher', {
http: {
path: '/pusher',
verb: 'get'
},
returns: {
arg: 'pusher',
type: 'string'
}
}
);
};
First, I added /status route and it worked fine. But, when i tried to add /pusher . It just didnt work. I am getting an error
{
"error": {
"statusCode": 500,
"name": "ReferenceError",
"message": "response is not defined",
"stack": "ReferenceError: response is not defined\n at Function.Meetups.pusher (/Users/ankursharma/Documents/projects/meetupz/common/models/meetups.js:34:20)\n at SharedMethod.invoke (/Users/ankursharma/Documents/projects/meetupz/node_modules/strong-remoting/lib/shared-method.js:270:25)\n at HttpContext.invoke (/Users/ankursharma/Documents/projects/meetupz/node_modules/strong-remoting/lib/http-context.js:297:12)\n at phaseInvoke (/Users/ankursharma/Documents/projects/meetupz/node_modules/strong-remoting/lib/remote-objects.js:677:9)\n at runHandler (/Users/ankursharma/Documents/projects/meetupz/node_modules/strong-remoting/node_modules/loopback-phase/lib/phase.js:135:5)\n at iterate (/Users/ankursharma/Documents/projects/meetupz/node_modules/strong-remoting/node_modules/loopback-phase/node_modules/async/lib/async.js:146:13)\n at Object.async.eachSeries (/Users/ankursharma/Documents/projects/meetupz/node_modules/strong-remoting/node_modules/loopback-phase/node_modules/async/lib/async.js:162:9)\n at runHandlers (/Users/ankursharma/Documents/projects/meetupz/node_modules/strong-remoting/node_modules/loopback-phase/lib/phase.js:144:13)\n at iterate (/Users/ankursharma/Documents/projects/meetupz/node_modules/strong-remoting/node_modules/loopback-phase/node_modules/async/lib/async.js:146:13)\n at /Users/ankursharma/Documents/projects/meetupz/node_modules/strong-remoting/node_modules/loopback-phase/node_modules/async/lib/async.js:157:25\n at /Users/ankursharma/Documents/projects/meetupz/node_modules/strong-remoting/node_modules/loopback-phase/node_modules/async/lib/async.js:154:25\n at execStack (/Users/ankursharma/Documents/projects/meetupz/node_modules/strong-remoting/lib/remote-objects.js:522:7)\n at RemoteObjects.execHooks (/Users/ankursharma/Documents/projects/meetupz/node_modules/strong-remoting/lib/remote-objects.js:526:10)\n at phaseBeforeInvoke (/Users/ankursharma/Documents/projects/meetupz/node_modules/strong-remoting/lib/remote-objects.js:673:10)\n at runHandler (/Users/ankursharma/Documents/projects/meetupz/node_modules/strong-remoting/node_modules/loopback-phase/lib/phase.js:135:5)\n at iterate (/Users/ankursharma/Documents/projects/meetupz/node_modules/strong-remoting/node_modules/loopback-phase/node_modules/async/lib/async.js:146:13)"
}
}
I am pretty sure, its a very small mistake. I am beginner in loopback and trying to implement loopback in my project.
In the example they define response as a local variable to that remote method, you did not. Secondly, (Meetups,pusher) you do not need to export pusher here. You are adding to Meetups.
You have to declare response in your pusher remote method.
An alternative way without declaring response is, Simply returning the value.
Example:
Meetups.pusher = function(cb) {
if (2>1) {
return 'sending something';
} else {
return 'mont blanc';
}
};
Define the variable and return the variable or you can directly call the cb in if and else like
Meetups.pusher = function(cb) {
if (2>1) {
cb(null,'sending something');
} else {
cb(null, 'mont blanc');
}
};