AJV: no schema with key or ref "https://json-schema.org/draft-07/schema" - ajv

I am using ajv 8.6.2
I have schemas that validate against draft-07 using the default export
When I use the draft-09 export all of my schemas give me this error:
no schema with key or ref "https://json-schema.org/draft-07/schema"
Same error exists if I use this:
no schema with key or ref "https://json-schema.org/draft-09/schema"
Can't seem to figure out what's going on here?

AJV does not seem to support HTTPs versions of the json-schema identifiers; try using http: instead:
{
"$schema": "http://json-schema.org/draft-07/schema"
}

If you mean by
When I use the draft-09 export all
that you're using Ajv2019 instance
import Ajv2019 from "ajv/dist/2019"
const ajv = new Ajv2019()
then you need to add the draft-07 metaschema:
const draft7MetaSchema = require("ajv/dist/refs/json-schema-draft-07.json")
ajv.addMetaSchema(draft7MetaSchema)
as explained at https://ajv.js.org/guide/schema-language.html#draft-2019-09-and-draft-2020-12
FYI, don't refer to JSON meta schemas with https://, their identifiers do start with http://, ajv doesn't retrieve them, it packages them instead.

Related

Using variables in typeScript graphQL code generator

I followed this tutorial to auto generate the schema of my graphql endpoint in my front application.
However now I need to use variables, so I did something like this:
export const GET_TODO = gql`
query GetTodo($id: Int!) {
todos(id: $id) {
id
text
completed
}
}
}
I want to know if there is a way I can get the variable type "automatically" without having to redefine it in the front.
E.g.
...
query GetTodo($id: AutoGeneratedTodoIdType) {
...
PS: I am using react, in case there are framework/library specific solutions.
Yes you can, you will usually have exported types in the generated file such as GetTodoVariables

Apollo Client readFragment with custom id (keyFields)

For ref, using "#apollo/client": "^3.5.5",
I've defined my typePolicies like so as suggested in docs:
HistoricalData: {
keyFields: ["variable", "workspace"],
fields:{...}
}
and when my cache is built, I am expecting my cacheId to be like
<__typename>:<id>:<id>
HistoricalData:${props.variable}:${props.workspace}`;
but instead, when I look in the Apollo cache, it's been created using the keyField names and the values in an object, such as
HistoricalData:{"variable":"GAS.TOTAL","workspace":"ABC"}
instead of
HistoricalData:GAS.TOTAL:ABC
so when I try to readFragment it returns null
client.readFragment({
id: `HistoricalData:${props.variable}:${props.workspace}`,
fragment: apolloGQL`fragment MyHistorical on Historical {
variable
workspace
}`})
It does actually return a value from the cache if I create my id in the structure that exists in the cache and readFragment using this.
Has anyone else noticed that Apollo client is not creating the cache id's in the structure that they describe in the docs?
After some research I came upon the correct way to handle this case. I know that you have already moved on, but just in case anyone else has the same problem in the future, here goes:
As described in the documentation for customizing the cache ID, the cache ID will be an stringified object, as you pointed out. It's not quite explicit in the documentation, but at this point in time it provides this nested example for a cache ID:
Book:{"title":"Fahrenheit 451","author":{"name":"Ray Bradbury"}}
But as users we don't have to preoccupy us with the format of this ID, because there's a helper for that, called cache.identify.
For your specific case, you could use something like this:
const identifiedId = cache.identify({
__typename: 'HistoricalData',
variable: 'GAS.TOTAL',
workspace: 'ABC'
});
cache.readFragment({
id: identifiedId,
fragment: apolloGQL`fragment MyHistorical on Historical {
variable
workspace
}`
});

String interpolation in gql tag

I've already posted this as an issue in the graphql-tag repositoy but I'll post it here as I hope somebody on here has a workable solution.
What am I trying to do?
I'd like to dynamically define typedefs of local types based on props passed to my react component.
Why am I doing this?
I realise this is not the intended way of defining gql, however, I'm in the process of creating a React wrapper component around the Apollo Provider. The purpose of it is to make the process of mocking out data locally (as described here https://www.apollographql.com/docs/react/development-testing/client-schema-mocking/) more seamless with less boilerplate.
I'm going for a declarative approach where the user can simply define an array of fields (name, graphQL type and optional implementation that will default to a sensible faker implementation for the graphQL type) that will make local fields available directly on the Query type as well as an array of types (name, fields and an optional nested type) which should make it possible to define arbitrary local schemas declaratively with no boilerplate.
What's the outcome at the moment?
Just to establish a baseline, the following is working just fine and allows me to run codegeneration from Apollo CLI and query the new local test field (with a #client directive) in my application
const localTypeDefs = gql`
extend type Query {
test: String
}
`;
const client = new ApolloClient({
cache: new InMemoryCache(),
uri: "https://localhost:4000/graphql",
typeDefs: localTypeDefs,
...apolloClientOptions,
});
If I change the qgl definition to look like this instead
const name = "name";
const graphQLType = "String";
const localTypeDefs = gql`
extend type Query {
${name}: ${graphQLType}
}
`;
I get the following error when running codegen Syntax Error: Expected Name, found ":". If I change the gql definition to this
const testString = "test: String";
const localTypeDefs = gql`
extend type Query {
${testString}
}
`;
I get this error Syntax Error: Expected Name, found "}". on codegen. In general, it seems like everything after the interpolated string causes the compiler to emit this error on the next character. If I include the } in the testString, I get the error Syntax Error: Expected Name, found <EOF>..
Now, If I try to change to the function syntax instead so the localTypeDefs definition looks as follows:
const testString = "test: String";
const localTypeDefs = gql(`extend type Query { ${testString} } `);
The typedef is actually generated without any error, however, the codegeneration still fails with the error GraphQLError: Cannot query field "test" on type "Query" when I query for this field. The query hasn't change at all from the working baseline I posted at the top and if I change back to that without touching anything else, the codegen no longer complains about the query.
Curiously, if I change the above back to the baseline implementation but keep the explicit function call instead of the implicit `` string as the following:
const localTypeDefs = gql(`
extend type Query {
test: String
}
`);
I still get the error GraphQLError: Cannot query field "test" on type "Query" but as soon as I change back to the base case, everything is working just fine.
I was was able to get something similar to this working for a query by wrapping an input with double quotes:
const query = gql`
mutation RegenerateAgreement {
agreementsRegenerate(input: { userId: "${userId}" }) {
agreements {
body
prompt
}
}
}
`;

Use i18next translation hook for error messages

So I have a form which works fine and for error messages I have created a schema file which contains errors defined like this
export const dataInputCreateSchema = yupObject({
company: yup.object().required('This field is required').nullable
})
In my component I am initializing my i18next translation variable like this const { t } = useTranslation('common'). As to show the error messages in case if user touch the textbox and does not write anything then required field error will be shown and its working like this
const companyFieldError = _.get(errors,'company', '');
_.get is a lodash method that takes in an object, path and default value.
I need to know how I can pass my translation defined in common.json file to companyFieldError? Translation in common.json is something like
"errorMessages": {
"requiredField": "This is required Field"
}
I don't want to discard my schema file but I want to provide key there and that key must get translated. Is there a way to do that?
Here are some changes I will make to support translation of errors.
Use the message in the schema definition.
export const dataInputCreateSchema = yupObject({
company: yup.object().required('errorMessages.requiredField').nullable
})
Use the message to get the translated text.
const message = _.get(errors,'company', '');
const companyFieldError = t(message);

When should I use _id in MongoDB?

MongoDB has a field for every document called "_id". I see people using it everywhere as a primary key, and using it in queries to find documents by the _id.
This field defaults to using an ObjectId which is auto-generated, an example is:
db.tasks.findOne()
{
_id: ObjectID("ADF9"),
description: "Write lesson plan",
due_date: ISODate("2014-04-01"),
owner: ObjectID("AAF1") // Reference to another document
}
But in JavaScript, the underscore behind a field in an object is a convention for private, and as MongoDB uses JSON (specifically, BSON), should I be using these _ids for querying, finding and describing relationships between documents? it doesn't seem right.
I saw that MongoDB has a way to generate UUID https://docs.mongodb.com/manual/reference/method/UUID
Should I forget that _id property, and create my own indexed id property with an UUID?
Use UUIDs for user-generated content, e.g. to name image uploads. UUIDs can be exposed to the user in an URL or when the user inspects an image on the client-side. For everything that is on the server/not exposed to the user, there is no need to generate a UUID, and using the auto-generated _id is preferred.
An simple example of using UUID would be:
const uuid = require('uuid');
exports.nameFile= async (req, res, next) => {
req.body.photo = `${uuid.v4()}.${extension}`;
next();
};
How MongoDB names its things should not interfere in how you name your things. If data sent by third-party hurts the conventions you agreed to follow, you have to transform that data into the format you want as soon as it arrives in your application.
An example based in your case:
function findTaskById(id) {
var result = db.tasks.findOne({"_id": id});
var task = {
id: result._id,
description: result.description,
something: result.something
};
return task;
}
This way you isolate the use of Mongo's _id into the layer of your application that is responsible to interact with the database. In all other places you need task, you can use task.id.

Resources