How can i use a resolve in a param default value, something like
params: {
id: {
type: 'int',
value: ['lists', function(lists) {
return lists[0].id; <====
}],
squash: false,
dynamic: false
}
},
resolve: {
lists: ['MailingListService', function (MailingListService) {
//load the tree data
return MailingListService.getMailingLists().then(function (lists) {
return lists;
});
}],
},
Because parameters have to be processed synchrounously, you cannot use a resolve in a default parameter value.
However, you can use a placeholder value such as null and map it to a resolve value later.
params: {
id: {
type: 'int',
value: null,
squash: false,
dynamic: false
}
},
resolve: {
lists: ['MailingListService', function (MailingListService) {
//load the tree data
return MailingListService.getMailingLists().then(function (lists) {
return lists;
});
}],
listId: ['lists', '$stateParams', function($stateParams) {
return ($stateParams.id === null) lists[0].id : listId;
}],
list: ['MailingListService', 'listId', function (MailingListService, listId) {
return MailingListService.getMailingList(listId);
}],
},
Related
thx for any help.
Im using at the frontend the apollo-client and at the backend graphql-nexus,prisma2 and graphql-yoga server.
I want to solve the n + 1 problem with #paljs/plugins.
At the frontend I have a query posts like:
query posts{
posts {
id
favoritedBy(where: { id: { equals: $currentUserId } }) {
id
}
author {
id
avatar {
id
}
}
link {
id
}
games {
id
}
tags {
id
}
likes(where: { user: { id: { equals: $currentUserId } } }) {
id
}
}
}
Posts resolver:
import { PrismaSelect } from '#paljs/plugins'
export const posts = queryField('posts', {
type: 'Post',
list: true,
args: {
...
},
resolve: async (_parent, args, { prisma, request }, info) => {
const select = new PrismaSelect(info).value
let opArgs: FindManyPostArgs = {
take: 10,
orderBy: {
[args.orderBy]: 'desc',
},
...select
}
const post = await prisma.post.findMany(opArgs)
//The result I want to return with the "sub-models" like likes, author tags...
console.log(JSON.stringify(post, undefined, 2))
return post
},
})
I logging the queries
const prisma = new PrismaClient({
log: ['query'],
})
My Problem: With PrismaSelect, I have 5 queries more than without and If I check the request-time at the frontend I need 300-400ms longer with PrismaSelect. So what I'm doing wrong?
I saw in the #paljs/plugins doc the select in the context. Maybe that is my mistake. How can I use the select in the context?
Here ist my Context:
import { PrismaClient, PrismaClientOptions } from '#prisma/client'
import { PubSub } from 'graphql-yoga'
import { PrismaDelete, onDeleteArgs } from '#paljs/plugins'
class Prisma extends PrismaClient {
constructor(options?: PrismaClientOptions) {
super(options)
}
async onDelete(args: onDeleteArgs) {
const prismaDelete = new PrismaDelete(this)
await prismaDelete.onDelete(args)
}
}
export const prisma = new PrismaClient({
log: ['query'],
})
export const pubsub = new PubSub()
export interface Context {
prisma: PrismaClient
request: any
pubsub: PubSub
}
export function createContext(request: any): Context {
return { prisma, request, pubsub }
}
You need to know that to use my PrismaSelect plugin you need to remove the nexus-prisma-plugin package and use my Pal.js CLI to create your CRUD and ObjectType for nexus and using #paljs/nexus plugin to add in mackSchema function
import { makeSchema } from '#nexus/schema';
import * as types from './graphql';
import { paljs } from '#paljs/nexus'; // import our plugin
export const schema = makeSchema({
types,
plugins: [paljs()],// here our plugin don't use nexus-prisma-plugin
outputs: {
schema: __dirname + '/generated/schema.graphql',
typegen: __dirname + '/generated/nexus.ts',
},
typegenAutoConfig: {
sources: [
{
source: require.resolve('./context'),
alias: 'Context',
},
],
contextType: 'Context.Context',
},
});
Now add this type to your Context
export interface Context {
prisma: PrismaClient
request: any
pubsub: PubSub
select: any // here our select type
}
export function createContext(request: any): Context {
// our paljs plugin will add select object before resolver
return { prisma, request, pubsub, select: {} }
}
after you add our plugin your query will log like this
extendType({
type: 'Query',
definition(t) {
t.field('findOneUser', {
type: 'User',
nullable: true,
args: {
where: arg({
type: 'UserWhereUniqueInput',
nullable: false,
}),
},
resolve(_, { where }, { prisma, select }) {
// our plugin add select object into context for you
return prisma.user.findOne({
where,
...select,
});
},
});
},
});
Can you please try to use my pal c command to start an example from my list and try your schema and make tests with it
It is working, thx Ahmed your plugin is AWESOME!!!!!
I changed my Post-Object from
const Post = objectType({
name: 'Post',
definition(t) {
t.model.id()
t.model.authorId()
t.model.tags()
t.model.games()
t.model.link()
t.model.report()
t.model.notifications()
t.model.author()
t.model.favoritedBy({
filtering: {
id: true,
},
})
t.model.likes({
filtering: {
user: true,
},
})
}
})
to
const Post = objectType({
name: 'Post',
definition(t) {
t.string('id')
t.field('tags', {
nullable: false,
list: [true],
type: 'Tag',
resolve(parent: any) {
return parent['tags']
},
})
t.field('games', {
list: [true],
type: 'Game',
resolve(parent: any) {
return parent['games']
},
})
t.field('link', {
type: 'Link',
nullable: true,
resolve(parent: any) {
return parent['link']
},
})
t.field('notifications', {
list: [true],
type: 'Notification',
resolve(parent: any) {
return parent['notifications']
},
})
t.field('author', {
nullable: false,
type: 'User',
resolve(parent: any) {
return parent['author']
},
})
t.field('favoritedBy', {
nullable: false,
list: [true],
type: 'User',
args: {
where: 'UserWhereInput',
},
resolve(parent: any) {
return parent['favoritedBy']
},
})
t.field('likes', {
list: [true],
type: 'Like',
args: {
where: 'LikeWhereInput',
},
resolve(parent: any) {
return parent['likes']
},
})
},
})
And I also used the nexus-prisma-plugin and paljs-plugin at the same time
I want to achieve the fields of one object type within another object type
Here is my schema file.
const Films = new GraphQLInterfaceType({
name: 'films',
fields: () => ({
id:{
type: GraphQLID
},
name: {
type: GraphQLString,
},
})
})
const MovieStream = new GraphQLObjectType({
name: 'MovieStream',
interfaces: () => [Films],
fields: () => ({
id: {
type: GraphQLID,
},
movie_id: {
type: GraphQLString,
},
})
})
Here I am trying to use the interface. But It shows error:
{
"errors": [
{
"message": "Query root type must be Object type, it cannot be { __validationErrors: undefined, __allowedLegacyNames: [], _queryType: undefined, _mutationType: undefined, _subscriptionType: undefined, _directives: [#include, #skip, #deprecated], astNode: undefined, extensionASTNodes: undefined, _typeMap: { __Schema: __Schema, __Type: __Type, __TypeKind: __TypeKind, String: String, Boolean: Boolean, __Field: __Field, __InputValue: __InputValue, __EnumValue: __EnumValue, __Directive: __Directive, __DirectiveLocation: __DirectiveLocation, films: films, ID: ID, Date: Date, JSON: JSON, MovieStream: MovieStream }, _possibleTypeMap: {}, _implementations: { films: [] } }."
},
{
"message": "Expected GraphQL named type but got: { __validationErrors: undefined, __allowedLegacyNames: [], _queryType: undefined, _mutationType: undefined, _subscriptionType: undefined, _directives: [#include, #skip, #deprecated], astNode: undefined, extensionASTNodes: undefined, _typeMap: { __Schema: __Schema, __Type: __Type, __TypeKind: __TypeKind, String: String, Boolean: Boolean, __Field: __Field, __InputValue: __InputValue, __EnumValue: __EnumValue, __Directive: __Directive, __DirectiveLocation: __DirectiveLocation, films: films, ID: ID, Date: Date, JSON: JSON, MovieStream: MovieStream }, _possibleTypeMap: {}, _implementations: { films: [] } }."
}
]
}
Here is Query type:
const QueryRoot = new GraphQLObjectType({
name: 'Query',
fields: () => ({
getContentList:{
type: new GraphQLList(contentCategory),
args: {
id: {
type: GraphQLInt
},
permalink: {
type: GraphQLString
},
language: {
type: GraphQLString
},
content_types_id: {
type: GraphQLString
},
oauth_token:{
type: GraphQLString
}
},
resolve: (parent, args, context, resolveInfo) => {
var category_flag = 0;
var menuItemInfo = '';
user_id = args.user_id ? args.user_id : 0;
// console.log("context"+context['oauth_token']);
return AuthDb.models.oauth_registration.findAll({attributes: ['oauth_token', 'studio_id'],where:{
// oauth_token:context['oauth_token'],
$or: [
{
oauth_token:
{
$eq: context['oauth_token']
}
},
{
oauth_token:
{
$eq: args.oauth_token
}
},
]
},limit:1}).then(oauth_registration => {
var oauthRegistration = oauth_registration[0]
// for(var i = 0;i<=oauth_registration.ength;i++){
if(oauth_registration && oauthRegistration && oauthRegistration.oauth_token == context['oauth_token'] || oauthRegistration.oauth_token == args.oauth_token){
studio_id = oauthRegistration.studio_id;
return joinMonster.default(resolveInfo,{}, sql => {
return contentCategoryDb.query(sql).then(function(result) {
return result[0];
});
} ,{dialect: 'mysql'});
}else{
throw new Error('Invalid OAuth Token');
}
})
},
where: (filmTable, args, context) => {
return getLanguage_id(args.language).then(language_id=>{
return ` ${filmTable}.permalink = "${args.permalink}" and ${filmTable}.studio_id = "${studio_id}" and (${filmTable}.language_id = "${language_id}" OR ${filmTable}.parent_id = 0 AND ${filmTable}.id NOT IN (SELECT ${filmTable}.parent_id FROM content_category WHERE ${filmTable}.permalink = "${args.permalink}" and ${filmTable}.language_id = "${language_id}" and ${filmTable}.studio_id = "${studio_id}"))`
})
},
}
})
})
module.exports = new GraphQLSchema({
query: QueryRoot
})
Please help me out. have i done something wrong in the use of interface?
I have found the answer through this post
Is it possible to fetch data from multiple tables using GraphQLList
Anyone please tell me the exact way to use the interface in my code.
Although the error you have printed does not really relate to interfaces implementations, in order for you to use interfaces, you have to implement the methods/types the interface references. So in your situation your object MovieStream is missing the type name that you refer in the object Films.
Your code should look something like:
const Films = new GraphQLInterfaceType({
name: 'films',
fields: () => ({
id:{
type: GraphQLID
},
name: {
type: GraphQLString,
},
})
})
const MovieStream = new GraphQLObjectType({
name: 'MovieStream',
interfaces: () => [Films],
fields: () => ({
id: {
type: GraphQLID,
},
name: {
type: GraphQLString // You're missing this!
},
movie_id: {
type: GraphQLString,
},
})
})
Now back to the error you have printed "message": "Query root type must be Object type, it cannot be...
This seems to be related to your QueryRoot object, it seems that GraphQLSchema is not recognizing the root object. If this issue is still there once you fix the interface, have a look at this answer here
I have an iOS app that needs to pass data to a vue front-end:
const customerStore = new Vuex.Store({
state: {
data: [
{ id:1, title: 'Foo' },
{ id:2, title: 'Bar' }
]
},
mutations: {
list (state, data) {
state.data = data
}
}
})
const ListCustomersPage = {
key: 'ListCustomersPage',
template: '#ListCustomersPage',
components: { toolbar, cellcustomer },
data() {
return {
title: 'Select Customer',
items: customerStore.state.data
}
},
methods: {
push() {
}
}
};
However, I need to mutate the store from an injection on the webview:
web.InjectJavascriptAsync("customerStore.commit('list', [])").Start()
But the list is not changed. No error is shown when calling the injection.
I've been following an online example of backbone validation online:
http://jsfiddle.net/thedersen/c3kK2/
So far so good, but now I'm getting into validating subviews and they're not working.
My code looks like this:
var ScheduleModel = Backbone.Model.extend({
validation: {
StartDate: {
required: true,
fn: "isDate"
},
StartTime: [{
required: true
},
{
pattern: /^([0-2]\d):([0-5]\d)$/,
msg: "Please provide a valid time in 24 hour format. ex: 23:45, 02:45"
}],
EndDate: {
required: true,
fn: "isDate"
}
},
isDate: function (value, attr, computed) {
if (isNaN(Date.parse(value))) {
return "Is not a valid Date";
}
}
});
var ScheduleView = Backbone.View.extend({
tagName: "div",
template: _.template($("#scheduleAddTemplate").html()),
render: function () {
// append the template to the element
this.$el.append(this.template(this.model.toJSON()));
// set the schedule type
var renderedInterval = SetScheduleType(this.model.attributes.ScheduleType.toLowerCase());
// append to the interval
$("#Interval", this.$el).append(renderedInterval.el);
this.stickit();
return this;
},
events: {
"submit #NewScheduleForm": function (e) {
e.preventDefault();
if (this.model.isValid(true)) {
this.model.save(null,
{
success: function (schedule) {
//do stuff
}
},
{ wait: true });
}
}
},
bindings: {
"[name=ScheduleType]": {
observe: "ScheduleType",
setOptions: {
validate: true
}
},
"[name=StartDate]": {
observe: "StartDate",
setOptions: {
validate: true
}
},
"[name=StartTime]": {
observe: "StartTime",
setOptions: {
validate: true
}
},
"[name=EndDate]": {
observe: "EndDate",
setOptions: {
validate: true
}
}
},
initialize: function () {
Backbone.Validation.bind(this);
},
remove: function () {
Backbone.Validation.unbind(this);
}
});
The possible interval I'm currently working with is the following:
var MonthModel = Backbone.Model.extend({
defaults: {
DayOfMonth: 1,
MonthsToSkip: 1
},
MonthsToSkip: {
required: true,
min: 1,
msg: "Number must be greater than 1"
},
DayOfMonth: {
required: function (val, attr, computed) {
console.log(computed.ScheduleType);
if (computed.ScheduleType === "monthly") {
return true;
}
return false;
}
}
});
var MonthlyView = Backbone.View.extend({
tagName: "div",
attributes: function () {
return { id: "Monthly", class: "inline co-xs-4" };
},
template: _.template($("#monthEditTemplate").html()),
render: function () {
// append the template to the element
this.$el.append(this.template(this.model.toJSON()));
this.stickit();
return this;
},
bindings: {
"[name=DayOfMonth]": {
observe: "DayOfMonth",
setOptions: {
validate: true
}
},
"[name=MonthsToSkip]": {
observe: "MonthsToSkip",
setOptions: {
validate: true
}
}
},
initialize: function () {
Backbone.Validation.bind(this);
},
remove: function () {
Backbone.Validation.unbind(this);
}
});
Does anyone have any idea why the subview isn't validating?
Found the way to do it. Posting how it's done in case anyone else ever finds this a problem. I'm only going to show the relevant bits of code without all the bindings, initializing ect.
var ScheduleView = Backbone.View.extend({
render: function () {
// this.Interval
this.Interval = SetScheduleType(this.model.attributes.ScheduleType.toLowerCase(), this.model);
// set the changed interval view
$("#Interval", this.$el).append(this.Interval.render().el);
this.stickit();
return this;
},
events: {
"change #NewScheduleForm": function (e) {
// validate the subview when changes are made
this.Interval.model.validate();
},
"change #ScheduleType": function (e) {
e.preventDefault();
var model = this.model;
var newSchedType = e.target.value;
this.model.attributes.ScheduleType = e.target.value;
this.Interval = SetScheduleType(newSchedType, model);
$("#Interval").html(this.Interval.render().el);
},
"submit #NewScheduleForm": function (e) {
e.preventDefault();
if ((this.model.isValid(true)) && (this.Interval.model.isValid(true))) {
console.log("Success");
this.model.save(null,
{
success: function (schedule) {
//do stuff
}
},
{ wait: true });
}
}
}
});
Essentially I turned the subview into an attribute on the master view. I manually call the validation for the subview on any changes to the master view and on submitting the form.
There are two popular and similar questions to mine, but the difference is that those only have to worry about deep populating associations for one object, whereas mine is about N objects.
Suppose I have 3 models defined as such (left out some attributes for clarity):
identity: 'room',
attributes: {
LocationId : { type: 'integer',
primaryKey: true,
required: true,
autoIncrement: true },
DisplayName : { type: 'string',
unique: true },
FloorId : { model: 'Floor' }
}
identity: 'floor',
attributes: {
FloorId : { type: 'integer',
primaryKey: true },
FloorName : { type: 'string' },
BuildingId : { model: 'Building' },
rooms: {collection:'room', via:'FloorId'}
}
identity: 'building',
attributes: {
BuildingId : { type: 'integer',
primaryKey: true },
BuildingName : { type: 'string' },
floors: {collection:'floor', via:'BuildingId'}
}
The end goal is to have an array of objects that has this basic structure:
[{
"LocationId": 555,
"DisplayName": 'SomeCoolName',
"Floor" : {
"FloorId": 1337,
"FloorName": '5',
"Building": {
"BuildingId": 4321,
"BuildingName": 'HQ'
}
}
}, {...}]
I've not got far due to not knowing the BlueBird library promises as well as I should:
showWithAssetGeo: function(req, res) {
room.find( { assetCount: { '>': 0 } } )
.populate('FloorId')
.each(function(room){
var Building = Building.find({ id: _.pluck(room.FloorId, 'BuildingId') })
.then(function(Building) {return Building;});
return [room, Building];
})
.spread(function(room, Building) {
//Something to combine it all?
})
.catch (function(err) {
if (err) { res.badRequest('reason' + err); }
}
}
UPDATE: Had to tweak the answer marked below. Here is the final working code.
You need to make sure to execute the find by calling then or exec (each won't do it).
Seems like you're trying to map across all the floors and then bring those promises back to one. Promise.all() is the way to do that.
Try something like the below:
showWithAssetGeo: function(req, res) {
room.find( { assetCount: { '>': 0 } } )
.populate('FloorId')
.then(function(rooms) {
return Promise.all(rooms.map(function(room) {
return Building.findOne({id: room.FloorId.BuildingId})
.then(function(building) {
room.FloorId.building = building;
});
})
})
.then(function(deeplyPopulatedRooms) {
res.json(deeplyPopulatedRooms);
})
.catch(function(error) {
if (err) { res.badRequest('reason' + err); }
});
}
However, it would probably be more performant to pluck all the id's of the possible buildings and do one find for all id's. But the above should work and seems to be consistent with the approach you were taking before.