prisma-labs/graphqlgen

help with the resolving connections

woss opened this issue ยท 12 comments

woss commented

hi, I've followed the examples and managed to get the most of the stuff working, but not the connections. How do you resolve edges, nodes, pageInfo and aggregate?

import {PhotoConnectionResolvers} from '../generated/graphqlgen'

export const PhotoConnection: PhotoConnectionResolvers.Type = {
  ...PhotoConnectionResolvers.defaultResolvers,

  pageInfo: (parent, args, ctx, info) => {
    throw new Error('do not know how to implement this')
  },
  edges: (parent, args, ctx, info) => {
    throw new Error('or this')
  },
  aggregate: (parent, args, ctx, info) => {
    throw new Error('or this')
  },
}
type Query {
  timeline(
    skip: Int
    orderBy: PhotoOrderByInput
    after: String
    before: String
    first: Int
    last: Int
  ): PhotoConnection
}
// resolver
import {QueryResolvers} from '../generated/graphqlgen'

export const Query: QueryResolvers.Type = {
  ...QueryResolvers.defaultResolvers,
  timeline: async (parent, args, ctx, info) => {
    const connection = ctx.db.photosConnection(args)

    console.log(
      await connection.edges(),
      await connection.pageInfo(),
      await connection.aggregate(),
    )
    return {
      edges: await connection.edges(),
      pageInfo: await connection.pageInfo(),
      aggregate: await connection.aggregate(),
    }
  },
  photos: (parent, args, ctx, info) => {
    return ctx.db.photos()
  },

}

thanks for the help. ps i've reached out via slack without much help. :(

I have a similar problem with subscriptions right now.
I want to pass all values to the client and have to resolve mutation, node, previousValues and updatedFields to do so.

woss commented

it seems that it works similar as the other resolvers with small difference, parent variable in resolver does not get the args and it returns whole set.
Check this gist for the example. https://gist.github.com/woss/24069d327c6f2b25e0a3b89ac8590f54
This returns the data but it doesn't work with subselections like paging.

Lurtt commented

I have same problem.. when I generate types I am missing where object in my Resolver type

export const DonorConnection: DonorConnectionResolvers.Type = {
  ...DonorConnectionResolvers.defaultResolvers,

  aggregate: (parent, { where }, context: Context) => {
    return context.prisma.donorsConnection({ where }).aggregate()
  },
}
export namespace DonorConnectionResolvers {
  export const defaultResolvers = {
    pageInfo: (parent: DonorConnection) => parent.pageInfo,
    edges: (parent: DonorConnection) => parent.edges
  };

  ....

    aggregate: (
      parent: DonorConnection,
      args: {},
      ctx: Context,
      info: GraphQLResolveInfo
    ) => AggregateDonor | Promise<AggregateDonor>;
  }
}

My workaround.

Query resolver

/* @flow */
import type { Query_Resolvers } from "../generated/graphqlgen";

export const Query: Query_Resolvers = {
  usersConnection: async (parent, args, ctx, info) => {   
    const connection = await ctx.prisma.usersConnection(args)

    return {
      ...connection,
      aggregate: ctx.prisma.usersConnection(args).aggregate(),
    }
  }
};

Child resolver

/* @flow */
import { UserConnection_defaultResolvers } from "../generated/graphqlgen";
import type { UserConnection_Resolvers } from "../generated/graphqlgen";

export const UserConnection: UserConnection_Resolvers = {
  ...UserConnection_defaultResolvers,

  aggregate: (parent, args, ctx, info) => {
    return parent.aggregate
  }
};

yckao commented

@khayong
Does flow contain type definition in parent object?
In typescript, parent doesn't represent additional field like aggregate.

My mistake, Flow does prompt missing property 'aggregate' from parent object.

I was just worked around the Typescript compiler error by using the bracket notation instead of dot notation property accessor:

export const connectionResolvers: UserConnectionResolvers.Type = {
  ...UserConnectionResolvers.defaultResolvers,

  aggregate(parent) {
    return parent["aggregate"];
  }
};

That said, I have to imagine there's a way of doing this that doesn't resort to tricking the compiler. Would love to hear of a better solution if anyone knows.
(I suppose you could also use ts-ignore to get around the compiler complaining)

yckao commented

@dddicillo @khayong
I add another file into models like

models:
  files:
    - ./.../types.ts
    - ./.../generated/prisma-client/index.ts

and use connection definition like this

export interface DepartmentConnection {
  params: IDepartmentConnectionParams,
  pageInfo: prisma.PageInfo,
  edges: prisma.DepartmentEdge[]
};

than solve the problem

P.S. My params interface is the same type that I pass into
prisma.departmentConnection, not arguments that user input,
so I can directly use in aggregate resolver

woss commented

that seems like an unnecessary hack for a feature that should work.

woss commented

and why none of the members of the team gives no info or guideline on how to use connections? they must have tested it and made it work

Hey there,

The reason why there aren't guidelines at the moment on how to use connections is because we haven't yet fully solved the problem with connections in the "schema-first" world.

The best answer I can give you for now is to follow @khayong's solution.
Although this seems hacky, the reason why we do not include the aggregate field by default is because it is an expensive operation, and you might now always need it.

We might decide to always include aggregate in the future to make it easier, but for now that is the only solution.

Here are the steps:

1/
Reference your custom model definition that adds the aggregate field in your graphqlgen.yml file

// ./src/types.ts
import { UserEdge, PageInfo, AggregateUser } from '../prisma-client'

type UserConnection {
  edges: UserEdge[]
  pageInfo: PageInfo
  aggregate: AggregateUser
}
// graphqlgen.yml

models:
  files:
    # Make sure put this file BEFORE the prisma-client so that it takes the right `UserConnection` type
    - ./src/types.ts
    - ./src/generated/prisma-client

2/

Do the following in your connectionType resolver:

const Query: QueryResolvers = {
    usersConnection: async (parent, args, ctx) => {
    const { edges, pageInfo } = await ctx.prisma.usersConnection(args);
    const aggregate = await ctx.prisma.usersConnection(args).aggregate();

    return {
      edges,
      pageInfo,
      aggregate
    };
  }
}

Hopefully that was helpful ๐Ÿ™Œ

Hello

I have issue in nodeJS timeseries query.

Error: Unknown aggregation type: stringFirst

Node js Code:
var expReportQ = client.timeseries(); expReportQ.aggregation('stringFirst', 'campid', 'campid');

when I use raw query in druid end point it is working fine in postman.
here is query:

{ "queryType": "timeseries", "dataSource": "dsp_report_full_new", "granularity": "hour", "aggregations": [ { "type": "count", "name": "rows" }, { "type": "stringFirst", "name": "country", "fieldName": "country" }, { "type": "stringFirst", "name": "campid", "fieldName": "campid" }, { "type": "doubleSum", "name": "impressions", "fieldName": "impressions" }, { "type": "doubleSum", "name": "conversions", "fieldName": "conversions" }, { "type": "doubleSum", "name": "payout", "fieldName": "payout" }, { "type": "doubleSum", "name": "pixels", "fieldName": "pixels" }, { "type": "doubleSum", "name": "OneHplusOld", "fieldName": "OneHplus" }, { "type": "doubleSum", "name": "clicks", "fieldName": "clicks" } ], "postAggregations": [ { "type": "arithmetic", "name": "cr", "fn": "*", "fields": [ { "type": "constant", "name": "hundred", "value": 100 }, { "type": "arithmetic", "name": "cbyp", "fn": "/", "fields": [ { "type": "fieldAccess", "name": "conversions", "fieldName": "conversions" }, { "type": "fieldAccess", "name": "pixels", "fieldName": "pixels" } ] } ] } ], "intervals": [ "2019-11-11T00:00:00.000Z/2019-11-11T23:59:59.059Z" ], "filter": { "type": "and", "fields": [ { "type": "in", "dimension": "campid", "values": [ 2016 ] } ] } }

Please help me to fix issue