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

Set operation name on request payload #103

Merged
merged 3 commits into from
Aug 3, 2023

Conversation

pontusntengnas
Copy link

This PR sets the operation name not only in the query string but also in the request payload.

My usecase is that the server (gqlgen) only recognises the operation name if it exists in the payload and on the server I need the name for logging purposes.

@hgiasac
Copy link

hgiasac commented Aug 2, 2023

Hi @pontusntengnas,

Thanks for contributing. Your idea makes sense. However, we shouldn't duplicate the logic twice. You can create private functions for constructQuery and constructMutation to return the constructOptionsOutput:

func constructQuery(v interface{}, variables map[string]interface{}, options ...Option) (string, *constructOptionsOutput, error) {
	//
}

// ConstructQuery build GraphQL query string from struct and variables
func ConstructQuery(v interface{}, variables map[string]interface{}, options ...Option) (string, error) {
	query, _, err := constructQuery(v, variables, options...)
	if err != nil {
		return "", err
	}

	return query, err
}

func constructMutation(v interface{}, variables map[string]interface{}, options ...Option) (string, *constructOptionsOutput, error) {
	// ...
}

// ConstructMutation build GraphQL mutation string from struct and variables
func ConstructMutation(v interface{}, variables map[string]interface{}, options ...Option) (string, error) {
	query, _, err := constructMutation(v, variables, options...)
	if err != nil {
		return "", err
	}

	return query, err
}

Then pass the output to request:

func (c *Client) buildAndRequest(ctx context.Context, op operationType, v interface{}, variables map[string]interface{}, options ...Option) ([]byte, *http.Response, io.Reader, Errors) {
	var query string
	var err error
	var optionOutput *constructOptionsOutput
	switch op {
	case queryOperation:
		query, optionOutput, err = constructQuery(v, variables, options...)
	case mutationOperation:
		query, optionOutput, err = constructMutation(v, variables, options...)
	}

	if err != nil {
		return nil, nil, nil, Errors{newError(ErrGraphQLEncode, err)}
	}

	return c.request(ctx, query, variables, optionOutput)
}

// Request the common method that send graphql request
func (c *Client) request(ctx context.Context, query string, variables map[string]interface{}, options *constructOptionsOutput) ([]byte, *http.Response, io.Reader, Errors) {
in := GraphQLRequestPayload{
		Query:     query,
		Variables: variables,
	}

	if options != nil {
		in.OperationName = options.operationName
	}
        // ...
}

For Exec and ExecRaw we need to build options before requesting

// Executes a pre-built query and unmarshals the response into v. Unlike the Query method you have to specify in the query the
// fields that you want to receive as they are not inferred from v. This method is useful if you need to build the query dynamically.
func (c *Client) Exec(ctx context.Context, query string, v interface{}, variables map[string]interface{}, options ...Option) error {

	optionsOutput, err := constructOptions(options)
	if err != nil {
		return err
	}
	data, resp, respBuf, errs := c.request(ctx, query, variables, optionsOutput)
	return c.processResponse(v, data, resp, respBuf, errs)
}

// Executes a pre-built query and returns the raw json message. Unlike the Query method you have to specify in the query the
// fields that you want to receive as they are not inferred from the interface. This method is useful if you need to build the query dynamically.
func (c *Client) ExecRaw(ctx context.Context, query string, variables map[string]interface{}, options ...Option) ([]byte, error) {
	optionsOutput, err := constructOptions(options)
	if err != nil {
		return nil, err
	}
	data, _, _, errs := c.request(ctx, query, variables, optionsOutput)
	if len(errs) > 0 {
		return data, errs
	}
	return data, nil
}

@pontusntengnas
Copy link
Author

Thank you for your feedback, it sounds great. I will come back with a more proper implementation according to your proposal.

query.go Outdated Show resolved Hide resolved
Copy link

@hgiasac hgiasac left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@hgiasac hgiasac merged commit 1956215 into hasura:master Aug 3, 2023
1 check passed
@pontusntengnas pontusntengnas deleted the operation-name branch August 3, 2023 11:11
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

Successfully merging this pull request may close these issues.

2 participants