Giter VIP home page Giter VIP logo

Comments (11)

kbrandwijk avatar kbrandwijk commented on May 23, 2024 4

I like the idea of introducing Type safety for the info object. Basically, all the information should already be available, because type definitions are generated for the entire schema, so it should be possible to use a type safe info object from it, which is basically an object for every root field with all it's scalar fields set to boolean.

from prisma-binding.

calummoore avatar calummoore commented on May 23, 2024 3

It would also be helpful to have an object to merge requested fields from the user, with required fields from the system. For example, when running a create mutation, you need the ID field, but the user may not have requested it

async function resolver (parent, { input }, ctx, info) {
  const resp = await ctx.db.mutation.createSite(
      {
        data: {
          name,
        },
      },
      info, // Info is provided by the user
  )
  // ID will only be available if the user requested it
  // but I may want to use the ID
  const id = resp.id
  return resp
}

from prisma-binding.

terion-name avatar terion-name commented on May 23, 2024 2

@jide sorry, it's my script, I didn't publish it yet.

import {merge} from "lodash";
import {parse} from "graphql";
import {EnumType, VariableType} from "json-to-graphql-query";

export default function graphQLQueryToJson() {
  let tree;
  let info = arguments[0];
  let keepRoot = arguments[1];
  let fieldNodes = info && (info.fieldASTs || info.fieldNodes);
  // let querySelections = info.operation?.selectionSet?.selections;
  if (fieldNodes) {
    // (info, keepRoot)
    tree = fieldTreeFromAST([...fieldNodes], info.fragments);
    if (!keepRoot) {
      let key = Object.keys(tree)[0];
      tree = tree[key]
    }
  } else if (typeof info === "string") {
    const ast = parse(info);
    tree = fieldTreeFromAST(ast.definitions[0].selectionSet.selections);
    if (!keepRoot) {
      let key = Object.keys(tree)[0];
      tree = tree[key]
    }
  } else {
    // (asts, fragments, fieldTree)
    tree = fieldTreeFromAST(...arguments)
  }
  return tree
}

function fieldTreeFromAST (asts, fragments = {}, init = {}) {
  asts = asts instanceof Array ? asts : [asts];
  return asts.reduce(function (tree, val) {
    // console.log(val);
    let kind = val.kind;
    let name = val.name && val.name.value;
    let fragment;
    if (kind === 'Field') {
      if (val.selectionSet) {
        tree[name] = tree[name] || {};
        fieldTreeFromAST(val.selectionSet.selections, fragments, tree[name]);
        if (val.arguments?.length) {
          tree[name].__args = graphQLArgumentsToJson(val.arguments);
        }
      } else {
        tree[name] = true
      }
    } else if (kind === 'FragmentSpread') {
      fragment = fragments[name];
      fieldTreeFromAST(fragment.selectionSet.selections, fragments, tree)
    } else if (kind === 'InlineFragment') {
      fragment = val;
      fieldTreeFromAST(fragment.selectionSet.selections, fragments, tree)
    } // else ignore
    return tree
  }, init)
}

function graphQLArgumentsToJson(args) {
  let tree = {};
  // this will ensure that it is a graphql ast collecton or an array
  if (!args.forEach) args = [args];
  args.forEach(arg => {
    let tmp = [];
    let fields = arg.value.fields;
    if (!fields) {
      tree[arg.name.value] = arg.value.kind === 'EnumValue' ? new EnumType(arg.value.value) : arg.value.value;
    } else {
      fields.forEach(f => {
        if (f.value.fields) {
          tree[arg.name.value] = merge(tree[arg.name.value] || {}, graphQLArgumentsToJson(f));
        } else {
          let value;
          switch (f.value.kind) {
            case 'Variable':
              value = new VariableType(f.value.name.value);
              break;
            case 'EnumValue':
              value = new EnumType(f.value.value);
              break;
            case 'ListValue':
              value = [];
              f.value.values.forEach(v => value.push(v.value));
              break;
            case 'ObjectValue':
              value = graphQLArgumentsToJson({value: f});
              break;
            default:
              value = f.value.value;
          }
          tmp.push({[f.name.value]: value});
          tree[arg.name.value] = merge(tree[arg.name.value] || {}, ...tmp);
        }
      });
    }
  });
  return tree;
}

from prisma-binding.

terion-name avatar terion-name commented on May 23, 2024 1

For this you can use a package: https://github.com/dupski/json-to-graphql-query

But this approach really don't handle lots of issues.
You still will have problems proxying variables or using fragments. Bindings semantics is very limited. It really should accept a GraphQLResolveInfo-like object (or sort of) that will take query/operation, fragments, variableValues. I need a lot of times to alter incoming request. For example to ensure return fields for usage in nested resolvers, or to change some props at query, keeping args and variables in place. Now it is VERY hard to do

from prisma-binding.

terion-name avatar terion-name commented on May 23, 2024 1

@calummoore yes, this is so common case that I don't understand why there is no method for this. I am going to try making sort of ensureSelection helper, but yet had no time for this

from prisma-binding.

terion-name avatar terion-name commented on May 23, 2024 1

@calummoore I've came along with such thing:

import {graphQLQueryToJson, jsonToGraphQLQuery} from "../graphql/query";
import {merge, head} from "lodash";
import {GraphQLResolveInfo, parse, print, visit, Kind, Source} from "graphql";

export function copyFieldNodes({fieldNodes, operation}) {
  return print(fieldNodes)
      .map(i => {
        const parsedSource = parse(new Source(`${operation.operation} { ${i} }`));
        return parsedSource.definitions[0].selectionSet.selections[0];
      })
}

export function copyResolverInfo(info: GraphQLResolveInfo, overrides = {}): GraphQLResolveInfo {
  return Object.assign({}, info,
      {
        fieldNodes: copyFieldNodes(info),
        fieldName: info.fieldName,
        ...overrides
      }
  )
}

export function transformResolverInfoSelection(info: GraphQLResolveInfo, actions: {forceSelection?: string, rootFieldOverride?: string}) {
  const {forceSelection, rootFieldOverride} = actions;
  const {fieldName} = info;
  let resultSelection = graphQLQueryToJson(info, true);

  if (forceSelection) {
    const requiredSelection = graphQLQueryToJson(`{ ${fieldName} ${forceSelection} }`, true);
    resultSelection = merge(resultSelection, requiredSelection);
  }

  if (rootFieldOverride) {
    resultSelection = {[rootFieldOverride]: head(Object.values(resultSelection))};
  }

  const parsed = parse(`{ ${jsonToGraphQLQuery(resultSelection)} }`);
  const infoCopy = copyResolverInfo(info);
  infoCopy.fieldNodes[0].selectionSet = parsed.definitions[0].selectionSet.selections[0].selectionSet;
  if (rootFieldOverride) {
    infoCopy.fieldNodes[0].name.value = rootFieldOverride;
  }
  return infoCopy;
}

This allows to minimally transform input query (reassign root field, add some selections) to pass down to prisma or other graphql endpoint. Minimal and far from perfect, but something.

But in general query transforms when working graphql-to-graphql are very needed and a library for this should be developed

from prisma-binding.

calummoore avatar calummoore commented on May 23, 2024

That would be helpful, not straightforward though! Basically, we need to merge two or more sets of fields together (which would be easiest if we could simply merge JS objects, as per this issue).

As a workaround for now, I am making the initial request with the system fields I require, and then a secondary request for fields the user wants. Not ideal obviously, as that's two round trips to the server.

from prisma-binding.

jide avatar jide commented on May 23, 2024

@terion-name Where does import {graphQLQueryToJson, jsonToGraphQLQuery} from "../graphql/query"; come from ? jsonToGraphQLQuery id probably https://www.npmjs.com/package/json-to-graphql-query but I could not find the opposite.

from prisma-binding.

berstend avatar berstend commented on May 23, 2024

I'm contemplating using the Prisma typescript bindings as ORM in my backend (non-resolver) code and type safety for the info object would be super useful here.

I second @kbrandwijk's notion of re-using the existing type definitions, when I have a little more time I'll try to wrap my head around a solution.

I feel there's huge potential here to ditch TypeORM, etc. altogether when using Prisma. 😄
Especially the powerful filtering/querying on data from relations is just plain amazing and very cumbersome to do in any of the typical ORMs.

from prisma-binding.

marktani avatar marktani commented on May 23, 2024

Hey @berstend, that sounds super interesting. We are working on improving exactly this use case, and I would love to hear your input on it. Could you ping me in Slack (my handle is @nilan)? 🙂

from prisma-binding.

maticzav avatar maticzav commented on May 23, 2024

Hey 👋, I believe this issue is no longer in sync with the current version of prisma-binding. In need for gaining a better overview of the issues facing the current version, I'll close it.

Feel free to reopen the issue if you believe we should further discuss its context. 🙂

from prisma-binding.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.