How to create custom resolvers for Gatsby page queries? - graphql

I have a Gatsby application pulling data from Sanity.
This is Sanity's schema for the course.js:
import video from './video'
export default {
// Computer name
name: `courses`,
// Visible title
title: `Courses`,
type: `document`,
fields: [
{
name: `title`,
title: `Course title`,
type: `string`,
description: `Name of the course`
},
{
name: `slug`,
title: `slug`,
type: `slug`,
options: {
source: `title`,
maxLength: 100,
}
},
{
name: `price`,
title: `Price`,
type: `number`
},
{
name: `thumbnail`,
title: `Thumbnail`,
type: `image`,
options: {
hotspot: true,
}
},
{
name: `playlist`,
title: `Playlist`,
type: `array`,
of: [
{
title: `Video`,
name: `video`,
type: `video`,
}
]
},
]
}
And this is Sanity's schema for video.js:
export default {
// Computer name
name: `video`,
// Visible title
title: `Video`,
type: `object`,
fields: [
{ name: `title`, type: `string`, title: `Video title` },
{ name: `url`, type: `url`, title: `URL` },
{ name: `public`, title: `public`, type: `boolean`, initialValue: false }
]
}
This Gatsby page query:
{
allSanityCourses {
nodes {
title
price
playlist {
url
title
public
}
}
}
}
Results in:
{
"data": {
"allSanityCourses": {
"nodes": [
{
"title": "Master VS Code",
"price": 149,
"playlist": [
{
"url": "https://www.youtube.com/watch?v=PRz1Nv9GUzs",
"title": "Introduction",
"public": true
},
{
"url": "https://www.youtube.com/watch?v=PRz1Nv9GUzs",
"title": "Philosophy",
"public": false
},
{
"url": "https://www.youtube.com/watch?v=PRz1Nv9GUzs",
"title": "Tech and Tools",
"public": false
},
{
"url": "https://www.youtube.com/watch?v=PRz1Nv9GUzs",
"title": "Integration",
"public": true
},
{
"url": "https://www.youtube.com/watch?v=PRz1Nv9GUzs",
"title": "Extensions",
"public": false
}
]
}
]
}
},
"extensions": {}
}
To prevent the url field from being injected into the React component this Gatsby page query runs on (because these urls are paid for), I need to remove it, if the public field is set to false.
I've tried inserting this into gastby-node.js:
exports.createSchemaCustomization = ({ actions, schema }) => {
const { createTypes } = actions
const typeDefs = [
schema.buildObjectType({
name: "SanityCourses",
fields: {
playlist: {
type: "[SanityVideo]",
url: {
type: "String",
resolve: (source) => "nope"
},
},
},
interfaces: ["Node"],
}),
]
createTypes(typeDefs)
}
And:
exports.createResolvers = ({ createResolvers }) => {
const resolvers = {
SanityCourses: {
playlist: {
type: "[SanityVideo]",
url: {
type: "String",
resolve(source, args, context, info) {
return "nope"
},
}
},
},
}
createResolvers(resolvers)
}
But neither seems to work. The url field returns the url as always. The resolvers don't even seem to fire (I've tried putting console.log()'s in them).
Any help on how to remove the url field if the public field is set to false, or general direction to go in would be very appreciated.

Ditch the attempt in createSchemaCustomization since you don't need to customize the schema here (though I believe there is a way to achieve what you want using it, it is not expected that the data in it is sourced from existing nodes, and this undeclared dependency can create caching issues).
Then update your createResolvers function to something like this:
exports.createResolvers = ({ createResolvers }) => {
createResolvers({
SanityVideo: {
safeUrl: {
type: "String",
resolve: (source, args, context, info) => source.public ? source.url : null
},
},
})
}
I don't believe resolvers can replace schema-originated nodes (fields), hence using safeUrl instead of url
The type you are adding a field to is SanityVideo, and it doesn't matter what the parent node is—this will apply to all instances of SanityVideo in your data

Related

schema type for an unknown object in graphql

I have the following object coming back from db:
{
"total_rows": 200,
"bookmark": "g1AAAABteJzLYWBgYMpgTmEQTM4vTc5ISXIwNDLXMwBCwxyQVCJDUv3___-zMpjc7D8wgEEiCx71eSwgJQ1A6j-GtiwA6MscCg",
"rows": [
{
"id": "51a1ff51b3b4719d05e40ac4bb0d0566",
"objects": {
"0": {
"type": "ipv4-addr",
"value": "192.168.1.10",
"resolves_to_refs": "2"
},
"1": {
"type": "network-traffic"
}
}
],
"counts": {
"created_by_ref": {
"0203a7e6-b174-4af9-812d-ab889816e868": 1,
"0250789a-14c3-4751-b4a0-c017af82b8f1": 1,
"03c63db6-2a84-4627-88be-a83208d524e6": 1,
"05cba3da-11ff-4a7a-aae9-0b1614cd5300": 1,
"fc825d33-26ea-4563-9478-2e1887b87112": 1
},
"file.hashes.MD5": {
"UNDEFINED": 200
},
"file.name": {
"UNDEFINED": 200
},
"ipv4_addr.value": {
"127.0.0.1": 200,
"192.168.1.10": 200
},
"last_observed": {
"1583503380000": 5,
"1583589780000": 9,
"1585749840000": 12
}
},
"num_of_rows": 10
}
I am trying to fit in a graphql schema to the above. I have the following which works paryially:
const graphql = require("graphql");
const { GraphQLObjectType, GraphQLString, GraphQLSchema, GraphQLInt, GraphQLList } = graphql;
const SearchResultType = new GraphQLObjectType({
name: "SearchResult",
fields: ()=>({
total_rows: { type: GraphQLInt },
bookmark: { type: GraphQLString },
//rows: { type: new GraphQLList(GraphQLInt) },
num_of_rows: { type: GraphQLInt }
})
});
const RootQuery = new GraphQLObjectType({
name: "RootQueryType",
fields: {
searchResult:{
type: SearchResultType,
args: { id: { type: GraphQLString } },
resolve(parentValue: any, args: any) {
console.log(args)
return resultMock;
}
}
}
});
module.exports = new GraphQLSchema({
query: RootQuery,
});
The above works for those data types which are defined. However there are some objects in the mockResult above like:
"objects": {
"0": {
"type": "ipv4-addr",
"value": "192.168.1.10",
"resolves_to_refs": "2"
},
"1": {
"type": "network-traffic"
}
or
"counts": {
"created_by_ref": {
"0203a7e6-b174-4af9-812d-ab889816e868": 1,
"0250789a-14c3-4751-b4a0-c017af82b8f1": 1,
"03c63db6-2a84-4627-88be-a83208d524e6": 1,
"05cba3da-11ff-4a7a-aae9-0b1614cd5300": 1,
"fc825d33-26ea-4563-9478-2e1887b87112": 1
So as you see these objects keys are random or at least not guessable until we receive them. Is there any way I can define a sth like this: rows: { type: new GraphQLList(any random object we do not know ) }, as a type in schema below:
const SearchResultType = new GraphQLObjectType({
name: "SearchResult",
fields: ()=>({
total_rows: { type: GraphQLInt },
bookmark: { type: GraphQLString },
rows: { type: new GraphQLList(any random object we do not know ) },
num_of_rows: { type: GraphQLInt }
})
});
You can use the GraphQL JSON Scalar (for example from this implementation). I would not recommend doing this though (in fact years ago I did a talk "GraphQL JSON Scalar considered harmful"). Instead, you might want to transform map-like objects into lists of key-value pairs.
So for example for your counts object you could do the following:
type CreatedByRef {
key: ID
count: Int
}
Object.keys(counts.created_by_ref).map(key => ({
key,
count: counts.created_by_ref[key],
}));
This will change the shape of the result but preserve all the properties of GraphQL.

Return only non-null values from GraphQLList object

My sampleJSON -
{
"entries": [
{
"fields":{
"title":"My test title"
}
},
{
"fields":{
"description":"My test description"
}
}
]
}
Schema.js -
const rootQuery = new GraphQLObjectType({
name: 'testQuery',
fields: {
Articles: {
type: articleItem,
resolve(parentValue) {
return axios.get(`/getArticles`).then(resp => resp.data);
}
}
}
});
const articleItem = new GraphQLObjectType({
name: 'articleItem',
fields: () => ({
entries: {type: new GraphQLList(entry)}
})
});
const entry = new GraphQLObjectType({
name: 'entry',
fields: () => ({
fields: {type: fields}
})
});
const fields = new GraphQLObjectType({
name: 'fields',
fields: () => ({
title: {type: GraphQLString},
description: {type: GraphQLString}
})
});
GraphQL query i am using to query the data in the above JSON -
query articles{
Articles {
entries{
fields{
title,
description
}
}
}
}
I am wondering why the query returns "title" even though it is null in the second object and likewise with description in the first object. Is there a way to just return " title " or " description " only if it not null?
Current result of the query -
{
"data" : {
"entries" [
{
"fields": {
"title": "My test title",
"description": null
}
},
{
"fields": {
"title": null,
"description" : "My test description"
}
}
]
}
}
Required result -
{
"data" : {
"entries" [
{
"fields": {
"title": "My test title"
}
},
{
"fields": {
"description" : "My test description"
}
}
]
}
}
Appreciate any help with this !, thanks.
Way too late to answer, but if you stumble upon this, you can make non-nullable (!) by using GraphQLNonNull().
Here is the example for non-nullable integer
random: {
type: new GraphQLNonNull(GraphQLInt)
}

Static values in GraphQL

Is there a way to produce static values in a graphql query?
For example, let's say that I have a user object with a name and email field. For some reason, I always want the status of a user to be "ACCEPTED". How can I write a query that accomplishes this?
What I want to do:
query {
user(id: 1) {
email
name
status: "ACCEPTED"
}
}
The result I want:
{
"data": {
"user": {
"email": "me#myapp.com",
"name": "me",
"status": "ACCEPTED"
}
}
}
You can make your resolve function return a static value, e.g. like this in JavaScript:
const HomeWorldType = new GraphQLObjectType({
name: 'HomeWorld',
fields: () => {
return {
id: {
type: GraphQLInt,
resolve: () => 7,
},
name: { type: GraphQLString },
climate: { type: GraphQLString },
population: { type: GraphQLString },
}
}
})

Mongoose model configuration for elasticsearch

I'm having an issue with mongoosastic in combination with KeystoneJS. I want to search for all the posts of a specific user with a match on the title. I have the following model:
var keystone = require('keystone'),
Types = keystone.Field.Types,
mongoosastic = require('mongoosastic');
User = keystone.list('User');
var Post = new keystone.List('Post');
Post.add({
title: {
type: String,
required: true,
es_indexed: true
},
state: {
type: Types.Select,
options: 'draft, published, archived',
default: 'draft',
es_indexed: true
},
author: {
type: Types.Relationship,
ref: 'User',
es_schema: User,
es_include_in_parent: true,
es_indexed:true
},
publishedDate: {
type: Types.Date,
index: true,
dependsOn: {
state: 'published'
}
},
content: {
extended: {
type: Types.Html,
wysiwyg: true,
height: 400,
es_indexed: true
}
},
});
Post.schema.plugin(mongoosastic, {
hosts: [
process.env.SEARCHBOX_SSL_URL
]
});
Post.defaultColumns = 'title, state|20%, author|20%, publishedDate|20%';
Post.register();
Post.model.createMapping(function(err, mapping) {
if (err) {
console.log('error creating mapping', err);
} else {
console.log('mapping created for Post');
}
});
User.schema.plugin(mongoosastic, {
populate: [
{path: 'author'}
]
});
I tried the approach of "Indexing Mongoose references" (https://github.com/mongoosastic/mongoosastic#indexing-mongoose-references) but I can't get it to work. Elasticsearch is not returning any hits.. My search query:
{
"query": {
"filtered": {
"query": { "match": { "title": "req.query.query" }},
"filter": { "term": { "author": "req.query.userId" }} // or author._id
}
}
}
Can someone provide me the correct Post model configuration & search query? Thanks!

How to allow kendo grid to bind to an undefined field

I have this json object
{
id: string,
name: string,
category: {
id: string
name: string,
}
}
I want to have column that bind to productCategory.name. However that field is nullable. When productCategory is null/undefined, kendo will throw error. How can i tell kendo that if field is undefined, just show empty string?
EDIT
Below is my sample data
[{
"id":1,
"name":"Spaghetti",
"category":{
"id":"1",
"name":"Food"
}},
{
"id":2,
"name":"Coca-cola",
"category":null
}}]
Below is my kendo datasource
var kendoDataSource = new kendo.data.DataSource({
schema: {
data: "data",
total: "total",
model: {
id: "id",
fields: {
id: { type: "string" },
name: { type: "string" },
"category.name": { type: "string" }
}
}
}
});
Data above will throw "undefined" error, because second product does not have category.
Try using empty object instead of null
created a fiddle,check this:
http://jsfiddle.net/Sowjanya51/h930jcru/
Just provide a default value in your model like this:
var kendoDataSource = new kendo.data.DataSource({
schema: {
data: "data",
total: "total",
model: {
id: "id",
fields: {
id: { type: "string" },
name: { type: "string" },
"category.name": { type: "string" },
category: { defaultValue: {
id: "",
name: "",
}
}
}
}
}
});
This will initialize productCategory if it is null or undefined.

Resources