How do you create edgeless graphql element in Gatsby? - javascript

The title may be miss leading but I'm not really sure how do I ask this question correctly. Here is the problem: I'd like to query my own API(not created yet so I made placeholder data) for global settings which might change in the future and I will only need to rebuild the website instead of editing it manually, I want to create source node called CmsSettings and pass it to GraphQL (structure similar to site.siteMetadata) but I don't know how can I achieve that. What I achieved so far is to create a source node called allCmsSettings which has my data as an object in nodes array.
exports.sourceNodes = ({ actions, createNodeId, createContentDigest }) => {
const { createNode } = actions;
const myData = {
key: 123,
app_title: `The foo field of my node`,
...
}
const nodeContent = JSON.stringify(myData);
const nodeMeta = {
id: createNodeId(`my-data${ myData.key }`),
parent: null,
children: [],
internal: {
type: `CmsSettings`,
mediaType: `text/html`,
content: nodeContent,
contentDigest: createContentDigest(myData)
}
}
const node = Object.assign({}, myData, nodeMeta);
createNode(node);
}
Here is the query used to get the data of the source node
allCmsSettings {
edges {
node {
id
app_title
...
}
}
}
Creating a query results in an array of results(which I know is the result of creating source nodes) but I'd like to create that source so that I could query it like this and:
CmsSettings {
app_title
app_keywords
app_descriptions
app_logo_path
brand_name
...
}
You get the point. I was browsing the gatsby node API but I can't find how to achieve this.
Thank you for your help

Nevermind, the answer is pretty simple, if you are new to gatsby just like me the sourceNodes export creates 2 graphql fields for you with all prefix and camel case source node. The thing that I wanted to make is already there and is queryable with
cmsSettings {
app_title
app_keywords
app_descriptions
app_logo_path
brand_name
...
}
Notice the lowercase letter even though it was declared as CmsSettings. It seems that gatsby really does some magic under the hood.

Related

how to use localstorage in to do list project

Is this the correct way to store projects and tasks with localstorage? I also need to get the localstorage every time the page refreshes. So how do I do that?
export function newProject(name) {
allProjects.push({
projectTitle: name,
id: crypto.randomUUID(),
tasks: []
})
getProjectId(name)
save(name, project)
}
export function save(title, task) {
localStorage.setItem(title, JSON.stringify(task))
}
project is undefined so you need to define it first.
const project = {
projectTitle: name,
id: crypto.randomUUID(),
tasks: []
}
allProjects.push(project)
getProjectId(name)
save(name, project)
to get all projects on refresh, you need to maintain an array of names in localstorage, or save all projects as array in one key.
EDIT:
So the question is not entirely well described, but i did my best. Add exports to functions you need and use your getProjectId if needed. I used an approach with separate array of ID's to maintain the list of projects.
function createNewProject(name) {
// create and return project object
return {
title: name,
id: crypto.randomUUID(),
tasks: []
};
}
function saveProject(storageKey, projectObject) {
// get current list of project keys or create new list
const allProjectKeys = JSON.parse(localStorage.getItem("allProjectKeys")) ?? [];
// add new one to the list
allProjectKeys.push(storageKey);
// save current list of project keys
localStorage.setItem("allProjectKeys", JSON.stringify(allProjectKeys));
// save project data
localStorage.setItem(storageKey, JSON.stringify(projectObject));
}
function getProjectByKey(storageKey) {
// get single project by given key
return JSON.parse(localStorage.getItem(storageKey));
}
function getAllProjects() {
// get list of all project keys, and map each of them to get actual project instead of project key
return JSON.parse(localStorage.getItem("allProjectKeys")).map(getProjectByKey);
}
const testProject = createNewProject("test");
saveProject(testProject.id, testProject);
console.log(getProjectByKey(testProject.id));
console.log(getAllProjects());

Firebase Function With Converter Wipes Document Reference on Set()

I have a scheduled function that resets an integer value back to zero in my firestore. The problem that I'm running into is that, while the merge-set succeeds (for the specified properties), it somehow resets my Organization document reference to null.
So far I've tried the following
Not using a converter along with the Update() function (instead of Set()). While this works, it is untyped, and I have to get rid of the converter which encapsulates the moment() to Date conversion.
Using Set() and simply pass the entire object.
user.reference?.withConverter(userConverter).set(user)
This is also working but it overrides the entire user object and can lead to concurrency issues in case a user also updates his object while the timed function is running.
I'm looking for a solution that allows me to use the converter class along with a merge Set().
The User interface looks like this
export interface User extends Document {
email?: string
name?: string
organization?: Organization | null
numberOfForwards?: number
lastForwardReset?: moment.Moment
}
with its converter like so
export class UserConverter implements firestore.FirestoreDataConverter<User> {
toFirestore(user: User): firestore.DocumentData {
return {
email: user.email,
name: user.name,
organization: user.organization ? user.organization.reference : null,
number_of_forwards: user.numberOfForwards,
last_forward_reset: user.lastForwardReset?.toDate()
}
}
fromFirestore(snapshot: firestore.QueryDocumentSnapshot): User {
const data = snapshot.data()!
return {
reference: snapshot.ref,
email: data.email,
name: data.name,
organization: data.organization ? { reference: data.organization } : null,
numberOfForwards: data.number_of_forwards,
lastForwardReset: moment(data.last_forward_reset.toDate())
}
}
}
export const resetNumberOfForwards = functions.pubsub
.schedule('every 15 minutes')
.onRun(async () => {
const reset = (user: User) => {
console.log(`Resetting ${user.email} from [${user.numberOfForwards}] to [0]`)
// Claim user reference
user.reference
?.withConverter(userConverter)
.set({ numberOfForwards: 0, lastForwardReset: Moment() }, { merge: true })
}
for the partial set to work, I've included the following snippet on top of my file
firebase.firestore().settings({
ignoreUndefinedProperties: true
})
I think there are two issues going on here. For a partial set() you should use the merge option or else it will overwrite the document.
ref.set(data, {merge: true})
In addition, in your toFirestore method, either set the organization field as undefined and let the ignoreUndefinedProperties: true setting remove it, or don't include it at all if organization was not given. Something like this
toFirestore((numberOfForwards, lastForwardReset, ...user): User): firestore.DocumentData {
if (user.organization) {
user.organization = user.organization.reference;
}
return {
...user,
number_of_forwards: numberOfForwards,
last_forward_reset: lastForwardReset?.toDate()
}
}
I took out the numberOfForwards and lastForwardReset fields from the user object here and use the spread operator to copy over the remaining fields to the return value, but you could also save a temporary object, modify it, and return that.
PS: I know this is old, but it came up in my search so thought I might add an answer still.

How can I limit the objects from a group in a query in Gatsby?

I have this query in my code which allows me to build a tag cloud for this blog front page
tagCloud:allContentfulBlogPost {
group(field: tags, limit: 8) {
fieldValue
}
}
It's passing data that I map in my component using {data.tagCloud.group.map(tag => (...))};. The code works nicely, but it won't be limited by the filter I'm passing above in the group(fields: tags, limit: 8) in my query. It renders all the tags and not only the first eight.
I've unsuccessfully tried the skip filter as well for the sake of seeing if it works.
Is this the proper way to limit the count to my mapping component in Gatsby?
The Contentful source plugin doesn't define arguments on any of the nodes it creates, unfortunately. Instead you would need to create these yourself. The easiest way to do that is through the createResolvers API.
Here's a similar example from a project of mine:
// in gatsby-node.js
exports.createResolvers = ({ createResolvers }) => {
createResolvers({
SourceArticleCollection: {
// Add articles from the selected section(s)
articles: {
type: ["SourceArticle"],
args: {
// here's where the `limit` argument is added
limit: {
type: "Int",
},
},
resolve: async (source, args, context, info) => {
// this function just needs to return the data for the field;
// in this case, I'm able to fetch a list of the top-level
// entries that match a particular condition, but in your case
// you might want to instead use the existing data in your
// `source` and just slice it in JS.
const articles = await context.nodeModel.runQuery({
query: {
filter: {
category: {
section: {
id: {
in: source.sections.map((s) => s._ref),
},
},
},
},
},
type: "SourceArticle",
})
return (articles || []).slice(0, args.limit || source.limit || 20)
},
},
},
})
}
Because resolvers run as part of the data-fetching routines that support the GraphQL API, this will run server-side at build-time and only the truncated/prepared data will be sent down to the client at request time.

MST: How to create the main store correctly?

I want to create rootStore which contains others store. The problem is that the children contain properties like:
id: types.identifier(types.string),
And when I create the rootStore, I get an error from the child:
[mobx-state-tree] Error while converting {} to SomeModelStore: at path "/id" value undefined is not assignable to type: identifier(string) (Value is not a string), expected an instance of identifier(string) or a snapshot like identifier(string) instead.
I tried to use types.late but it did not help.
The solution I found is to wrap all properties into types.maybe
Examples:
error:
https://codesandbox.io/s/yvnznxyvyj?module=%2Fmodels%2FSomeModelStore.js
workaround:
https://codesandbox.io/s/0mv558yq50?module=%2Fmodels%2FSomeModelStore.js
Here https://codesandbox.io/s/yvnznxyvyj?module=%2Fmodels%2FSomeModelStore.js you create an empty object
.model("FirstStore", {
item: types.optional(SomeModelStore, {})
})
but type
SomeModelStore
didn't support empty fields. If you write like this
export const FirstStore = types
.model("FirstStore", {
item: types.optional(SomeModelStore, {
id: 'defaultId',
activate: false,
name: 'defaultName'
})
})
it will work. Or you can use "types.maybe" instead of "types.optional".
export const FirstStore = types
.model("FirstStore", {item: types.maybe(SomeModelStore)})
Also read about types.reference
I think it's a better way to use it in your case.

Modify collection value in Meteor

I want to write a query that would change the type of a project field from string to object.
So, if project field has the value abcd now, I want it to have an object like this:
{id: 'abcd'}
So:
project: 'abcd'
Turns to:
project: {id: 'abcd'}
I have no problems doing it in mongo:
db.hello.find({}).forEach((project) => {
project.project = {
id: x.project
}
db.hello.save(x)
})
But I don't know how to do it in Meteor. So far I have:
Projects.update($set: { client: ??? } }, { multi: true });
My 2 main problems are:
I don't know how to get the current value of client
I don't know how to change type
First of all, if you already ran the query, then you are aware that the db has already been adjusted yes? Because if you did run that, it would have updated all of the documents in that collection!
Please note that this should be ran server-side, I don't think that the $type is supported by all versions of minimongo.
// grab the cursor all string typed `project` fields
const cursor = Projects.find({ project: { $type : "string" } });
// grab the data from the cursor
const projects = cursor.fetch();
// Loop on each project and update
projects.forEach( project => Projects.update(project._id, {
$set: {
project: { id: project }
}
}) )

Categories