Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How to enforce some custom logic after an object of a certain type is resolved? #3306

Open
WingT opened this issue Sep 27, 2024 · 0 comments

Comments

@WingT
Copy link

WingT commented Sep 27, 2024

What happened?

I tried to add a directive on an SomeType, which implements SomeInterface, the generated directive code behaves like this:

  1. If a resolver returns []*SomeType, the directive also receives a []*SomeType variable as argument
func (ec *executionContext) _Query_todos(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) {
	//...
	resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
		directive0 := func(rctx context.Context) (interface{}, error) {
			ctx = rctx // use context from middleware stack in children
			return ec.resolvers.Query().Todos(rctx)
		}
		directive1 := func(ctx context.Context) (interface{}, error) {
                         // ...
                         //  directive0 will return a slice of Todo, we cannot modify child context of each element
			return ec.directives.Scope(ctx, nil, directive0, scope)
		}
// ...
	})
// ...
	res := resTmp.([]*model.Todo)
	fc.Result = res
	return ec.marshalNTodo2ᚕᚖgqlgenᚑtodosᚋgraphᚋmodelᚐTodoᚄ(ctx, field.Selections, res)
}
  1. If a resolver's signature returns SomeInterface, and it returns SomeType , the directive is not called
func (ec *executionContext) _Query_node(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) {
	// ...
	resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
		ctx = rctx // use context from middleware stack in children
		return ec.resolvers.Query().Node(rctx, fc.Args["id"].(string))
        // directive "scope" on "Todo" is not called at all
	})
	// ...
	res := resTmp.(model.Node)
	fc.Result = res
	return ec.marshalONode2gqlgenᚑtodosᚋgraphᚋmodelᚐNode(ctx, field.Selections, res)
}

What did you expect?

The generated directive will be called if the concrete type of resolver result is SomeType, with exactly one element

Minimal graphql.schema and models to reproduce

# GraphQL schema example
#
# https://gqlgen.com/getting-started/

directive @scope(scope: String) on OBJECT | FIELD_DEFINITION

interface Node {
  id: ID!
}

type Todo implements Node @scope(scope: "todo:read") {
  id: ID!
  text: String!
  done: Boolean!
  user: User!
  secret: String @scope(scope: "todo.secret:read")
}

type User implements Node {
  id: ID! @scope(scope: "user:read")
  name: String! @scope(scope: "user:read")
}

type Query {
  todos: [Todo!]!
  todo(
    id: ID!
  ): Todo
  node(
    id: ID!
  ): Node
  nodes(ids: [ID!]!): [Node]!
}

input NewTodo {
  text: String!
  userId: String!
}

type Mutation {
  createTodo(input: NewTodo!): Todo!
}

versions

  • go run github.com/99designs/gqlgen version: v0.17.49
  • go version: go version go1.23.1 darwin/arm64
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant