Implementing GraphQL Global Object Identification in NestJS (code-first approach) - javascript

I am trying to implement Global Object Identification described in GraphQL's documentation in NestJS.
1.) I started by creating a Node interface:
import { ID, InterfaceType, Field } from '#nestjs/graphql'
#InterfaceType()
export abstract class Node {
#Field(type => ID)
id: number
}
2.) I implemented it in my model:
import { Table } from "sequelize-typescript";
import { ObjectType } from "#nestjs/graphql";
import { Node } from "src/node/node-interface";
#ObjectType({
implements: Node
})
#Table
export class User extends Model {
// [Class body here...]
}
3.) Then I created a Query that would return users:
import { Resolver, Query} from "#nestjs/graphql";
import { User } from "./user-model";
#Resolver(of => User)
export class UserResolver {
#Query(returns => [Node])
async users() {
let users = await User.findAll();
console.log(users);
return users;
}
}
4.) Then I performed the test query from the documentation:
{
__schema {
queryType {
fields {
name
type {
name
kind
}
args {
name
type {
kind
ofType {
name
kind
}
}
}
}
}
}
}
5.) But instead of receiving the proper response:
{
"__schema": {
"queryType": {
"fields": [
// This array may have other entries
{
"name": "node",
"type": {
"name": "Node",
"kind": "INTERFACE"
},
"args": [
{
"name": "id",
"type": {
"kind": "NON_NULL",
"ofType": {
"name": "ID",
"kind": "SCALAR"
}
}
}
]
}
]
}
}
}
6.) I get this:
{
"data": {
"__schema": {
"queryType": {
"fields": [
{
"name": "users",
"type": {
"name": null,
"kind": "NON_NULL"
},
"args": []
}
]
}
}
}
}
I have no clue what I am doing wrong. I'd appreciate any help with this.

Maybe it's too late, but I'm at Node Resolver node must be nullable
import * as GQL from '#nestjs/graphql';
#GQL.Resolver(() => Node, {})
export class NodeResolver {
#GQL.Query(() => Node, {
name: 'node',
defaultValue: [],
nullable: true,
})
node(
#GQL.Args('id', { type: () => GQL.ID } as GQL.ArgsOptions)
id: Scalars['ID'],
): Promise<Node> {
// Implement
return null;
}
}
result:
{
"name": "node",
"type": {
"name": "Node",
"kind": "INTERFACE",
},
"args": [
{
"name": "id",
"type": {
"kind": "NON_NULL",
"ofType": {
"name": "ID",
"kind": "SCALAR"
}
}
}
]
},

Related

Convert permission array to JSON

I'm trying to implement Role Based Access control in my nestjs project and I have a permission array as follows
let permissions = [
"newsfeeds-alerts-view",
"settings-group_details-view",
"settings-group_details-edit",
"settings-privileges-view",
"settings-privileges-edit",
"settings-my_groups-create",
"settings-my_groups-view",
"settings-my_groups-edit",
"settings-my_groups-delete",
"settings-users-view",
"settings-users-edit",
"settings-users-delete",
"notifications-email-create",
"notifications-jira-create",
"notifications-jira-view",
"notifications-non_itr_ticket-create",
"notifications-non_itr_ticket-update",
"workspace_dashboard-worksapce-create",
"workspace_dashboard-worksapce-view",
"dashboard-geographic-maps-view",
"dashboard-geographic-report-itr-view",
"configurations-create_alerts-create",
"configurations-notifications-jira-create"
];
I want to create a JSON string from the above array as follows
{
"newsfeeds": {
"alerts": [
{
"name": "view"
}
]
},
"settings": {
"group_details": [
{
"name": "view"
},
{
"name": "edit"
}
],
"privileges": [
{
"name": "view"
},
{
"name": "edit"
}
],
"my_groups": [
{
"name": "view"
},
{
"name": "edit"
},
{
"name": "delete"
}
],
"users": [
{
"name": "view"
},
{
"name": "edit"
},
{
"name": "delete"
}
]
},
"notifications": {
"email": [
{
"name": "create"
}
],
"jira": [
{
"name": "create"
},
{
"name": "view"
}
],
"non_itr_ticket": [
{
"name": "create"
},
{
"name": "update"
}
]
},
"workspace_dashboard": {
"worksapce": [
{
"name": "create"
},
{
"name": "view"
}
]
},
"dashboard": {
"geographic": {
"maps": [
{
"name": "view"
}
],
"report": {
"itr": [
{
"name": "view"
}
]
}
},
"configurations": {
"create_alerts": [
{
"name": "create"
}
],
"notifications": {
"jira": [
{
"name": "create"
}
]
}
}
}
}
The permission array is dynamic. I need to group all common permission under one object.
How to achieve this, any easy methods available ?
Using array.reduce string.split to achieve your desired result
let permissions = ["newsfeeds-alerts-view","settings-group_details-view","settings-group_details-edit","settings-privileges-view","settings-privileges-edit","settings-my_groups-create","settings-my_groups-view","settings-my_groups-edit","settings-my_groups-delete","settings-users-view","settings-users-edit","settings-users-delete","notifications-email-create","notifications-jira-create","notifications-jira-view","notifications-non_itr_ticket-create","notifications-non_itr_ticket-update","workspace_dashboard-worksapce-create","workspace_dashboard-worksapce-view","dashboard-geographic-maps-view","dashboard-geographic-report-itr-view","configurations-create_alerts-create","configurations-notifications-jira-create",];
const output = permissions.reduce((r, s) => {
const path = s.split("-");
if (path.length > 1) {
const name = path.pop();
const last = path.pop();
let destination = r;
for (let key of path) {
destination[key] = destination[key] || {};
destination = destination[key];
}
destination[last] = destination[last] || [];
destination[last].push({ name });
}
return r;
}, {});
console.log(output);
.as-console-wrapper { max-height: 100% !important; top: 0; }
let obj: any = {}
permissions.forEach(permission => {
const [category, subcategory, name] = permission.split('-')
if (!obj[category]) {
obj[category] = {}
}
if (!obj[category][subcategory]) {
obj[category][subcategory] = []
}
obj[category][subcategory].push({ name })
})
let json = JSON.stringify(obj) // there you go

How to query a specific node using Graphql?

I am trying to use Graphql to return a specific instance of an object.
This is my Graphql query:
query MyQuery {
allContentfulFlexStyleBody {
edges {
node {
image {
file {
url
}
}
}
}
}
}
It returns 3 Nodes:
{
"data": {
"allContentfulFlexStyleBody": {
"edges": [
{
"node": {
"image": {
"file": {
"url": "//images.ctfassets.net/m7ipc0qjqa17/6JTBUN3mkENLvEVuC/6ff4b2da441f1c7cec2eb401534aa749/-19.jpeg"
}
}
}
},
{
"node": {
"image": {
"file": {
"url": "//images.ctfassets.net/m7ipc0qjqa17/2s6lg5oBJ7F780DI1/b4068dcc9cc889dbcd09ed992793e771/-BTS.png"
}
}
}
},
{
"node": {
"image": {
"file": {
"url": "//images.ctfassets.net/m7ipc0qjqa17/6bRRjlI1nLFCdUawZ/12af617d352b21864192dcc033198951/MyStylist_Photo_Grid_Layout__Retouched_Photos_Shortlist-6.jpeg"
}
}
}
}
]
}
},
"extensions": {}
}
I am attempting to display one image within my gatsby project as such:
{data.allContentfulFlexStyleBody.edges.map(({ node }, index) => (
<img
className={"contentFeatureImg"}
alt={``}
key={``}
src={node.image.file.url}
/>
))}
All images are being displayed. How do I access the first, second or third node exclusively without returning the entire edges array?
Accessing a specific element of the edges array works enough for what I'm doing here:
<img
className={"contentFeatureImg1"}
alt={``}
key={``}
src={data.allContentfulFlexStyleBody.edges[0].node.image.file.url}
/>

Clear database to update content with Notion API

I have a workspace in Notion, which I use to take notes for an app I have on Github.
I want to add a database which will show some download stats from different sources (incuding Github) using the beta Notion API.
Right now I can add information at the end of a database just fine, but I don't understand how to remove the content which was posted before. Or even update it if I can.
This is what I have so far:
import { Client } from "#notionhq/client";
import dotenv from "dotenv";
import { Octokit } from "#octokit/rest";
dotenv.config();
const octokit = new Octokit();
const notion = new Client({ auth: process.env.NOTION_TOKEN });
const databaseId = process.env.NOTION_DATABASE_ID;
async function addEntry(release, name, download_count, tag) {
try {
await notion.request({
path: "pages",
method: "POST",
body: {
parent: { database_id: databaseId },
properties: {
Version: {
title: [
{
text: {
content: release,
},
},
],
},
Name: {
rich_text: [
{
text: {
content: name,
},
},
],
},
"Download Count": {
type: "number",
number: download_count,
},
Tags: {
multi_select: [{ name: "Github" }, { name: tag }],
},
},
},
});
console.log("Success! Entry added.");
} catch (error) {
console.error(error.body);
}
}
(async () => {
const latest_release = await octokit.repos.listReleases({
owner: "ShadowMitia",
repo: "steam_randomiser",
});
const releases = latest_release.data;
let github_downloads = {};
for (let release of releases) {
for (let asset of release.assets) {
console.log(release["tag_name"], asset["name"], asset["download_count"]);
// github_downloads[asset["label"]];
addEntry(
`${release["tag_name"]}`,
`${asset["name"]}`,
asset["download_count"],
asset["name"].includes("linux") ? "Linux" : "Windows"
);
}
}
})();
To delete (archive) a page in a database. Set the archive parameter to true.
curl --location --request PATCH 'https://api.notion.com/v1/pages/YOUR_PAGE_ID' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer YOUR_BOT_TOKEN' \
--data'{
"parent":{
"database_id":"YOUR_DATABASE_ID"
},
"archived": true,
"properties":{
"Name":{
"title":[
{
"text":{
"content":"A Test Page"
}
}
]
},
"Email": {
"email": "hello#test.com"
},
"multiselect_tags": {
"type": "multi_select",
"multi_select":[{
"name": "Duc Loi Market"
},
{
"name": "Rainbow Grocery"
}]
}
}
}'
To clear data in a page you would set the data to empty or null depending on the property being updated. For example, if you have an array, you would set the property to an empty array.
"multiselect_tags": {
"type": "multi_select",
"multi_select":[ {
"name": "Duc Loi Market"
},
{
"name": "Rainbow Grocery"
}
]
}
}
//Empty a multi_select property
"multiselect_tags": {
"type": "multi_select",
"multi_select":[]
}
If the property is a string, like the email property, set it to null
"Email": {
"email": "hello#test.com"
}
//Empty the value of the email property on a page
"Email": {
"email": null
}

How to parse FractalTransformer with normalizr

I'm trying to use paularmstrong/normalizr on JSON that comes from FractalTransformer and whose nested childs have "data" attribute. Example of JSON:
{
"data": {
"object": "Offer",
"id": "5g6aqocew4qjzl40",
"real_id": 26,
"name": "Random Name",
"created_at": {
"date": "2019-06-18 11:13:08.000000",
"timezone_type": 3,
"timezone": "UTC"
},
"readable_created_at": "1 year ago",
"site": {
"data": {
"object": "Site",
"id": "65zody8vj29vlegd",
"name": "Test Site",
"real_id": 1
}
},
"countries": {
"data": [
{
"object": "Country",
"code": "US",
"name": "United States"
},
{
"object": "Country",
"code": "DE",
"name": "Germany"
}
]
}
},
"meta": {
"include": [
"site",
"countries"
],
"custom": []
}
}
Schemas I use:
export const offerSchema = new schema.Entity('offers')
export const siteSchema = new schema.Entity('sites', {}, {
processStrategy: (value) => {
return { ...value.data }
},
idAttribute: (value) => {
return value.data.id
},
})
export const countrySchema = new schema.Entity('countries')
offerSchema.define({
site: siteSchema,
countries: [countrySchema],
})
Now the issue is that I remove 'data' from the site since it's just one object successfully, but I can't do it in the country case. Whatever I tried with custom processStrategy fails, as country is object that has data which is array (I assume this is where the issue is, going from Entity to Array). And in idAttribute function I always get complete array so can't determine the ID of single entry. So the end result is that the ID of countries is undefined. Any ides?
I actually managed with another approach. I added processStrategy on the parent, 'Offer' in this case, so all 'data' parts get stripped before they reach other child schemas.
const normalizrStripDataOptions = {
processStrategy: (value) => {
const ret = { ...value }
Object.keys(ret).forEach((key) => {
if (ret[key] !== null) {
if (ret[key].data && Array.isArray(ret[key].data)) {
ret[key] = [...ret[key].data]
}
if (ret[key].data && typeof ret[key].data === 'object') {
ret[key] = { ...ret[key].data }
}
}
})
return ret
},
}
export const offerSchema = new schema.Entity('offers', {}, normalizrStripDataOptions)
export const siteSchema = new schema.Entity('sites')
export const countrySchema = new schema.Entity('countries')
offerSchema.define({
site: siteSchema,
countries: [countrySchema],
})

Remote method doesn't show up in loopback API explorer

I have a role-mapping model which maps a userId to a roleId, I need a remote method on the role-mapping model to retrieve the role-mappingId for a given userId.
this the code for the remoteMethod
'use strict';
module.exports = function(Rolemapping) {
Rolemapping.getRolesByUser = async function (id, cb) {
const roleMappings = await Rolemapping.find({ where: { principalId: id
} })
cb(null, roleMappings);
};
Rolemapping.remoteMethod("getRolesByUser", {
http: {
path: "/getRolesByUser",
verb: "get"
},
accepts: [
{ arg: "userId", type: "string", http: { source: "query" } }
],
returns: {
arg: "result",
type: "string"
},
description: "Cvs "
});
};
this is the role-mapping json file :
{
"name": "roleMapping",
"base": "RoleMapping",
"idInjection": true,
"options": {
"validateUpsert": true
},
"properties": {},
"validations": [],
"relations": {
"role": {
"type": "belongsTo",
"model": "role",
"foreignKey": "roleId"
}
},
"acls": [],
"methods": {}
}
the above remote method doesn't show up in the loopback API explorer.
RoleMapping is a built-in model, its role-mapping.js file is hidden in node_modules/loopback, I've tested it and it doesn't look like will load a js file for itself from common/models.
It looks like a boot script is your only option. It's the same code, but your function receives the server object.
server/boot/get-roles-by-user.js
module.exports = function(server) {
const Rolemapping = server.models.RoleMapping;
Rolemapping.getRolesByUser = async function (id) {
return JSON.stringify(await Rolemapping.find({ where: { principalId: id
} }))
};
Rolemapping.remoteMethod("getRolesByUser", {
http: {
path: "/getRolesByUser",
verb: "get"
},
accepts: [
{ arg: "userId", type: "string", http: { source: "query" } }
],
returns: {
arg: "result",
type: "string"
},
description: "Cvs "
});
}
I've also removed the cb parameter from your remote method, because methods which return a Promise do not need it, just return the value like you would for any other function

Categories

Resources