Skip to content

Releases: n1ru4l/envelop

October 10, 2024

10 Oct 12:58
1a1dabc
Compare
Choose a tag to compare

@envelop/[email protected]

Patch Changes

  • c1720a8
    Thanks @n1ru4l! - Fix handling of primitive list values that was
    introduced in the last release.

October 09, 2024

09 Oct 12:08
4031c59
Compare
Choose a tag to compare

@envelop/[email protected]

Patch Changes

October 09, 2024

09 Oct 10:28
625fe9d
Compare
Choose a tag to compare

@envelop/[email protected]

Major Changes

@envelop/[email protected]

Patch Changes

  • #2309
    4fd5917
    Thanks @n1ru4l! - Strip __responseCacheId and
    __responseCacheTypeName from incremental delivery execution result.

@envelop/[email protected]

Major Changes

August 26, 2024

26 Aug 10:48
41c8e78
Compare
Choose a tag to compare

@envelop/[email protected]

Major Changes

  • #2281
    70d4d7a
    Thanks @UserType;! - Refactor Generic Auth plugin;

    • [BREAKING] - Now @auth directive is renamed to @authenticated. If you want to keep the old
      name you can configure the plugin to use the old name.
    useGenericAuth({
      // ...
      authDirectiveName: 'auth'
    })
    • [BREAKING] - Now directiveOrExtensionFieldName is renamed to authDirectiveName.
    useGenericAuth({
      // ...
    - directiveOrExtensionFieldName: 'auth',
    + authDirectiveName: 'auth',
    });
    • Now auth directives support OBJECT and INTERFACE locations, so you can use the auth
      directive on types as well.
    directive @authenticated on OBJECT | INTERFACE
    
    type User @authenticated {
      id: ID!
      name: String!
    }
    • validateUser function does not receive fieldAuthDirectiveNode and fieldAuthExtension
      anymore. Instead, it takes fieldAuthArgs which is an object that contains the arguments of the
      auth directive or extension. So you don't need to parse the arguments manually anymore.
    const validateUser: ValidateUserFn = params => {
      if (!params.fieldAuthArgs.roles.includes('admin')) {
        return createUnauthorizedError(params)
      }
    }
    • validateUser's objectType parameter is now renamed to parentType. And it takes the
      original composite type instead of the GraphQLObjectType instance. Now it can be
      GraphQLInterfaceType as well.
    • validateUser's current parameters are now;
    export type ValidateUserFnParams<UserType> = {
      /** The user object. */
    
      /** The field node from the operation that is being validated. */
      fieldNode: FieldNode
      /** The parent type which has the field that is being validated. */
      parentType: GraphQLObjectType | GraphQLInterfaceType
      /** The auth directive arguments for the type */
      typeAuthArgs?: Record<string, any>
      /** The directives for the type */
      typeDirectives?: ReturnType<typeof getDirectiveExtensions>
      /** Scopes that type requires */
      typeScopes?: string[][]
      /** Policies that type requires */
      typePolicies?: string[][]
      /** The object field */
      field: GraphQLField<any, any>
      /** The auth directive arguments for the field */
      fieldAuthArgs?: Record<string, any>
      /** The directives for the field */
      fieldDirectives?: ReturnType<typeof getDirectiveExtensions>
      /** Scopes that field requires */
      fieldScopes?: string[][]
      /** Policies that field requires */
      fieldPolicies?: string[][]
      /** Extracted scopes from the user object */
      userScopes: string[]
      /** Policies for the user */
      userPolicies: string[]
      /** The args passed to the execution function (including operation context and variables) **/
      executionArgs: ExecutionArgs
      /** Resolve path */
      path: ReadonlyArray<string | number>
    }
    • New directives for role-based auth are added @requiresScopes and @policy for more granular
      control over the auth logic.
    directive @requiresScopes(scopes: [String!]!) on OBJECT | INTERFACE | FIELD_DEFINITION
    
    directive @policy(policy: String!) on OBJECT | INTERFACE | FIELD_DEFINITION

    Check README for more information.

Patch Changes

August 26, 2024

26 Aug 10:44
e606ae6
Compare
Choose a tag to compare

@envelop/[email protected]

Minor Changes

  • #2281
    70d4d7a
    Thanks @UserType;! - Refactor Generic Auth plugin;

    • [BREAKING] - Now @auth directive is renamed to @authenticated. If you want to keep the old
      name you can configure the plugin to use the old name.
    useGenericAuth({
      // ...
      authDirectiveName: 'auth'
    })
    • [BREAKING] - Now directiveOrExtensionFieldName is renamed to authDirectiveName.
    useGenericAuth({
      // ...
    - directiveOrExtensionFieldName: 'auth',
    + authDirectiveName: 'auth',
    });
    • Now auth directives support OBJECT and INTERFACE locations, so you can use the auth
      directive on types as well.
    directive @authenticated on OBJECT | INTERFACE
    
    type User @authenticated {
      id: ID!
      name: String!
    }
    • validateUser function does not receive fieldAuthDirectiveNode and fieldAuthExtension
      anymore. Instead, it takes fieldAuthArgs which is an object that contains the arguments of the
      auth directive or extension. So you don't need to parse the arguments manually anymore.
    const validateUser: ValidateUserFn = params => {
      if (!params.fieldAuthArgs.roles.includes('admin')) {
        return createUnauthorizedError(params)
      }
    }
    • validateUser's objectType parameter is now renamed to parentType. And it takes the
      original composite type instead of the GraphQLObjectType instance. Now it can be
      GraphQLInterfaceType as well.
    • validateUser's current parameters are now;
    export type ValidateUserFnParams<UserType> = {
      /** The user object. */
    
      /** The field node from the operation that is being validated. */
      fieldNode: FieldNode
      /** The parent type which has the field that is being validated. */
      parentType: GraphQLObjectType | GraphQLInterfaceType
      /** The auth directive arguments for the type */
      typeAuthArgs?: Record<string, any>
      /** The directives for the type */
      typeDirectives?: ReturnType<typeof getDirectiveExtensions>
      /** Scopes that type requires */
      typeScopes?: string[][]
      /** Policies that type requires */
      typePolicies?: string[][]
      /** The object field */
      field: GraphQLField<any, any>
      /** The auth directive arguments for the field */
      fieldAuthArgs?: Record<string, any>
      /** The directives for the field */
      fieldDirectives?: ReturnType<typeof getDirectiveExtensions>
      /** Scopes that field requires */
      fieldScopes?: string[][]
      /** Policies that field requires */
      fieldPolicies?: string[][]
      /** Extracted scopes from the user object */
      userScopes: string[]
      /** Policies for the user */
      userPolicies: string[]
      /** The args passed to the execution function (including operation context and variables) **/
      executionArgs: ExecutionArgs
      /** Resolve path */
      path: ReadonlyArray<string | number>
    }
    • New directives for role-based auth are added @requiresScopes and @policy for more granular
      control over the auth logic.
    directive @requiresScopes(scopes: [String!]!) on OBJECT | INTERFACE | FIELD_DEFINITION
    
    directive @policy(policy: String!) on OBJECT | INTERFACE | FIELD_DEFINITION

    Check README for more information.

August 20, 2024

20 Aug 17:13
04cf1b0
Compare
Choose a tag to compare

@envelop/[email protected]

Patch Changes

@envelop/[email protected]

Patch Changes

@envelop/[email protected]

Minor Changes

  • #2292
    c3dd2c3
    Thanks @ardatan! - Now you can define a custom string interpolation
    function to be used in the rate limit message. This is useful when you want to include dynamic
    values in the message.

    useRateLimiter({
      configByField: [
        {
          type: 'Query',
          field: 'search', // You can also use glob patterns
          max: 10,
          window: '1m',
          message:
            'My custom message with interpolated values: ${args.searchTerm} and ${context.user.id}'
        }
      ],
      interpolateMessage: (message, args, context) => {
        return message.replace(/\${(.*?)}/g, (_, key) => {
          return key.split('.').reduce((acc, part) => acc[part], { args, context })
        })
      }
    })
  • #2292
    c3dd2c3
    Thanks @ardatan! - New directive SDL;

    directive @rateLimit(
      max: Int
      window: String
      message: String
      identityArgs: [String]
      arrayLengthField: String
      readOnly: Boolean
      uncountRejected: Boolean
    ) on FIELD_DEFINITION
  • #2292
    c3dd2c3
    Thanks @ardatan! - Programmatic API to define rate limit
    configuration in addition to directives

    useRateLimiter({
      configByField: [
        {
          type: 'Query',
          field: 'search', // You can also use glob patterns
          max: 10,
          window: '1m'
        }
      ]
    })

Patch Changes

August 13, 2024

13 Aug 09:25
b10a670
Compare
Choose a tag to compare

@envelop/[email protected]

Major Changes

  • #2270
    73eb69f
    Thanks @EmrysMyrddin! - Breaking Change: Rename all metrics
    options to their actual metric name to avoid confusion.

    All metric options have been moved under a mandatory metrics key, and the name of each options
    have been renamed to match the default metric name.

    The plugin option argument is also now mandatory.

    export const serveConfig = defineConfig({
      plugins: pluginCtx => [
        usePrometheus({
          ...pluginCtx,
    
          // Enable all available metrics
    
    -     requestSummary: true,
    -     parse: true,
    -     validate: true,
    -     contextBuilding: true,
    -     execute: true,
    -     subscribe: true,
    -     errors: true,
    -     deprecatedFields: true,
    -     requestTotalDuration: true,
    -     schemaChangeCount: true,
    
          // Warning: enabling resolvers level metrics will introduce significant overhead
    -     resolvers: true,
    +     metrics: {
    +       graphql_envelop_request_time_summary: true,
    +       graphql_envelop_phase_parse: true,
    +       graphql_envelop_phase_validate: true,
    +       graphql_envelop_phase_context: true,
    +       graphql_envelop_phase_execute: true,
    +       graphql_envelop_phase_subscribe: true,
    +       graphql_envelop_error_result: true,
    +       graphql_envelop_deprecated_field: true,
    +       graphql_envelop_request_duration: true,
    +       graphql_envelop_schema_change: true,
    
            // Warning: enabling resolvers level metrics will introduce significant overhead
    +       graphql_envelop_execute_resolver: true,
    +     }
        })
      ]
    })

Minor Changes

  • #2270
    73eb69f
    Thanks @EmrysMyrddin! - Add missing labels path and phase
    of graphql_envelop_error_result metric to the configuration.

July 16, 2024

16 Jul 15:11
737f250
Compare
Choose a tag to compare

@envelop/[email protected]

Patch Changes

  • #2266
    389d5f6
    Thanks @EmrysMyrddin! - The plugin now try to reduce the size
    of the resulting query by not adding a __typename aliased selection if __typename is already
    selected.

May 30, 2024

30 May 09:49
de4d13b
Compare
Choose a tag to compare

@envelop/[email protected]

Minor Changes

  • #2238
    430ee7d
    Thanks @ardatan! - Accept a factory function to cache that takes
    the context and returns the cache implementation

@envelop/[email protected]

Minor Changes

  • #2238
    430ee7d
    Thanks @ardatan! - BREAKING: Now the cache implementation does not
    require the ExecutionContext or KVNamespace instance but only the name of the namespace

    import { createSchema, createYoga, YogaInitialContext } from 'graphql-yoga'
    import { useResponseCache } from '@envelop/response-cache'
    import { createKvCache } from '@envelop/response-cache-cloudflare-kv'
    import { resolvers } from './graphql-schema/resolvers.generated'
    import { typeDefs } from './graphql-schema/typeDefs.generated'
    
    export type Env = {
      GRAPHQL_RESPONSE_CACHE: KVNamespace
    }
    
    const graphqlServer = createYoga<Env & ExecutionContext>({
      schema: createSchema({ typeDefs, resolvers }),
      plugins: [
        useResponseCache({
          cache: createKvCache({
            KVName: 'GRAPHQL_RESPONSE_CACHE',
            keyPrefix: 'graphql' // optional
          }),
          session: () => null,
          includeExtensionMetadata: true,
          ttl: 1000 * 10 // 10 seconds
        })
      ]
    })
    
    export default {
      fetch: graphqlServer
    }

Patch Changes

May 08, 2024

08 May 07:42
cae6868
Compare
Choose a tag to compare

@envelop/[email protected]

Patch Changes

@envelop/[email protected]

Patch Changes

@envelop/[email protected]

Major Changes

  • #2217
    7ac1d3c
    Thanks @EmrysMyrddin! - Adds a cache for metrics definition
    (Summary, Histogram and Counter).

    Fixes an issue preventing this plugin to be initialized multiple times, leading to metrics
    duplication error (ardatan/graphql-mesh#6545).

    Behavior Breaking Change:

    Due to Prometheus client API limitations, a metric is only defined once for a given registry. This
    means that if the configuration of the metrics, it will be silently ignored on plugin
    re-initialization.

    This is to avoid potential loss of metrics data produced between the plugin re-initialization and
    the last pull by the prometheus agent.

    If you need to be sure metrics configuration is up to date after a plugin re-initialization, you
    can either:

    • restart the whole node process instead of just recreating a graphql server at runtime
    • clear the registry using registry.clear() before plugin re-initialization:
      function usePrometheusWithReset() {
        registry.clear()
        return usePrometheus({ ... })
      }
    • use a new registry for each plugin instance:
      function usePrometheusWithRegistry() {
        const registry = new Registry()
        return usePrometheus({
          registry,
          ...
        })
      }

    Keep in mind that this implies potential data loss in pull mode.

    API Breaking Change:

    To ensure metrics from being registered multiple times on the same registry, the signature of
    createHistogram, createSummary and createCounter have been changed to now include the
    registry as a mandatory parameter.

    If you were customizing metrics parameters, you will need to update the metric definitions

    usePrometheus({
      execute: createHistogram({
    +   registry: registry
        histogram: new Histogram({
          name: 'my_custom_name',
          help: 'HELP ME',
          labelNames: ['opText'] as const,
    -     registers: [registry],
        }),
        fillLabelsFn: () => {}
      }),
      requestCount: createCounter({
    +   registry: registry
        histogram: new Histogram({
          name: 'my_custom_name',
          help: 'HELP ME',
          labelNames: ['opText'] as const,
    -     registers: [registry],
        }),
        fillLabelsFn: () => {}
      }),
      requestSummary: createSummary({
    +   registry: registry
        histogram: new Histogram({
          name: 'my_custom_name',
          help: 'HELP ME',
          labelNames: ['opText'] as const,
    -     registers: [registry],
        }),
        fillLabelsFn: () => {}
      }),
    })

Patch Changes