pax_global_header00006660000000000000000000000064146330411700014511gustar00rootroot0000000000000052 comment=8fd42e3a99e381be0f6546426af31d54433edabe graphene-federation-3.2.0/000077500000000000000000000000001463304117000154225ustar00rootroot00000000000000graphene-federation-3.2.0/.coveragerc000066400000000000000000000000271463304117000175420ustar00rootroot00000000000000[run] omit = */tests/* graphene-federation-3.2.0/.github/000077500000000000000000000000001463304117000167625ustar00rootroot00000000000000graphene-federation-3.2.0/.github/workflows/000077500000000000000000000000001463304117000210175ustar00rootroot00000000000000graphene-federation-3.2.0/.github/workflows/deploy.yml000066400000000000000000000010631463304117000230360ustar00rootroot00000000000000name: 🚀 Deploy to PyPI on: push: tags: - '*' jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - name: Set up Python 3.10 uses: actions/setup-python@v3 with: python-version: '3.10' - name: Build wheel and source tarball run: | pip install wheel python setup.py sdist bdist_wheel - name: Publish a Python distribution to PyPI uses: pypa/gh-action-pypi-publish@v1.1.0 with: user: __token__ password: ${{ secrets.PYPI_API_TOKEN }} graphene-federation-3.2.0/.github/workflows/integration_tests.yml000066400000000000000000000004141463304117000253060ustar00rootroot00000000000000name: Integration Tests on: [push, pull_request] jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v1 - name: Build environment run: make integration-build - name: Run Integration Tests run: make integration-test graphene-federation-3.2.0/.github/workflows/lint.yml000066400000000000000000000007261463304117000225150ustar00rootroot00000000000000name: Lint on: [push, pull_request] jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - name: Set up Python 3.10 uses: actions/setup-python@v4 with: python-version: 3.10.5 - name: Install dependencies run: | python -m pip install --upgrade pip pip install -e ".[dev]" - name: Run lint 💅 run: | black graphene_federation --check mypy graphene_federation graphene-federation-3.2.0/.github/workflows/tests.yml000066400000000000000000000022311463304117000227020ustar00rootroot00000000000000name: Unit Tests on: [push, pull_request] jobs: test: runs-on: ubuntu-latest strategy: max-parallel: 4 matrix: python-version: ["3.9", "3.10", "3.11", "3.12"] steps: - uses: actions/checkout@v1 - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v1 with: python-version: ${{ matrix.python-version }} - name: Install dependencies run: | python -m pip install --upgrade pip pip install -e ".[test]" - name: Run Unit Tests run: py.test tests --cov=graphene_federation -vv - name: Upload coverage run: | pip install coveralls coveralls --service=github env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} COVERALLS_FLAG_NAME: ${{ matrix.python-version }} COVERALLS_PARALLEL: true coveralls: name: Indicate completion to coveralls.io needs: test runs-on: ubuntu-latest container: python:3-slim steps: - name: Finished run: | pip3 install --upgrade coveralls coveralls --service=github --finish env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} graphene-federation-3.2.0/.gitignore000066400000000000000000000023221463304117000174110ustar00rootroot00000000000000.DS_Store # Byte-compiled / optimized / DLL files __pycache__/ *.py[cod] *$py.class # C extensions *.so # Distribution / packaging .Python build/ develop-eggs/ dist/ downloads/ eggs/ .eggs/ lib/ lib64/ parts/ sdist/ var/ wheels/ *.egg-info/ .installed.cfg *.egg MANIFEST # PyInstaller # Usually these files are written by a python script from a template # before PyInstaller builds the exe, so as to inject date/other infos into it. *.manifest *.spec # Installer logs pip-log.txt pip-delete-this-directory.txt # Unit test / coverage reports htmlcov/ .tox/ .coverage .coverage.* .cache nosetests.xml coverage.xml *.cover .hypothesis/ .pytest_cache/ # Translations *.mo *.pot # Django stuff: *.log local_settings.py db.sqlite3 # Flask stuff: instance/ .webassets-cache # Scrapy stuff: .scrapy # Sphinx documentation docs/_build/ # PyBuilder target/ # Jupyter Notebook .ipynb_checkpoints # pyenv .python-version # celery beat schedule file celerybeat-schedule # SageMath parsed files *.sage.py # Environments .env .venv env/ venv/ ENV/ env.bak/ venv.bak/ # Spyder project settings .spyderproject .spyproject # Rope project settings .ropeproject # mkdocs documentation /site # mypy .mypy_cache/ .idea node_modules graphene-federation-3.2.0/Dockerfile000066400000000000000000000004011463304117000174070ustar00rootroot00000000000000FROM python:3.10-alpine # Disable Python buffering in order to see the logs immediatly ENV PYTHONUNBUFFERED=1 # Set the default working directory WORKDIR /workdir COPY . /workdir # Install dependencies RUN pip install -e ".[dev]" CMD tail -f /dev/null graphene-federation-3.2.0/LICENSE.txt000066400000000000000000000020511463304117000172430ustar00rootroot00000000000000MIT License Copyright 2019 Ihor Kasianov Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.graphene-federation-3.2.0/MANIFEST.in000066400000000000000000000001711463304117000171570ustar00rootroot00000000000000include README.md LICENSE.txt graft graphene_federation global-exclude __pycache__ global-exclude *.py[co] prune */.idea graphene-federation-3.2.0/Makefile000066400000000000000000000023301463304117000170600ustar00rootroot00000000000000# ------------------------- # Integration testing # ------------------------- integration-build: ## Build environment for integration tests cd integration_tests && docker-compose build .PHONY: integration-build integration-tests: ## Run integration tests cd integration_tests && docker-compose down && docker-compose run --rm tests .PHONY: integration-test # ------------------------- # Development and unit testing # ------------------------- dev-setup: ## Install development dependencies docker-compose up --build -d .PHONY: dev-setup tests: ## Run unit tests docker-compose run graphene_federation py.test tests --cov=graphene_federation -vv .PHONY: tests check-style: ## Run linting docker-compose run graphene_federation black graphene_federation --check .PHONY: check-style check-types: ## Run typing check docker-compose run graphene_federation mypy graphene_federation .PHONY: check-types # ------------------------- # Help # ------------------------- help: ## Show help @grep -E '^[0-9a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}' .PHONY: help .DEFAULT_GOAL := help .EXPORT_ALL_VARIABLES: DOCKER_BUILDKIT = 1 COMPOSE_DOCKER_CLI_BUILD = 1 graphene-federation-3.2.0/README.md000066400000000000000000000342061463304117000167060ustar00rootroot00000000000000# graphene-federation Federation support for ![Graphene Logo](http://graphene-python.org/favicon.png) [Graphene](http://graphene-python.org) following the [Apollo Federation specifications](https://www.apollographql.com/docs/federation/subgraph-spec). [![PyPI version][pypi-image]][pypi-url] [![Unit Tests Status][unit-tests-image]][unit-tests-url] [![Coverage Status][coveralls-image]][coveralls-url] [![Integration Tests Status][integration-tests-image]][integration-tests-url] [pypi-image]: https://badge.fury.io/py/graphene-federation.svg [pypi-url]: https://pypi.org/project/graphene-federation/ [unit-tests-image]: https://github.com/graphql-python/graphene-federation/workflows/Unit%20Tests/badge.svg?branch=main [unit-tests-url]: https://github.com/graphql-python/graphene-federation/actions?query=workflow%3A"Unit+Tests" [coveralls-image]: https://coveralls.io/repos/github/graphql-python/graphene-federation/badge.svg?branch=main [coveralls-url]: https://coveralls.io/github/graphql-python/graphene-federation?branch=main [integration-tests-image]: https://github.com/graphql-python/graphene-federation/workflows/Integration%20Tests/badge.svg?branch=main [integration-tests-url]: https://github.com/graphql-python/graphene-federation/actions?query=workflow%3A"Integration+Tests" This repository is heavily based on the repo it was forked from... Huge thanks to [Preply for setting up the foundations](https://medium.com/preply-engineering/apollo-federation-support-in-graphene-761a0512456d). WARNING: This version is not compatible with `graphene` version below v3. If you need to use a version compatible with `graphene` v2 I recommend using the version 1.0.0 of `graphene_federation`. ------------------------ ## Supported Features * `sdl` (`_service` on field): enable to add schema in federation (as is) ## Apollo Spec Supported - [x] v1.0 - [x] v2.0 - [x] v2.1 - [x] v2.2 - [x] v2.3 - [x] v2.4 - [x] v2.5 - [x] v2.6 `STABLE_VERSION` . Rover dev supports only upto v2.6 - [x] v2.7 `LATEST_VERSION` All directives could be easily integrated with the help of [graphene-directives](https://github.com/strollby/graphene-directives). Now every directive's values are validated at run time itself by [graphene-directives](https://github.com/strollby/graphene-directives). ### Directives (v2.7) ```graphql directive @composeDirective(name: String!) repeatable on SCHEMA directive @extends on OBJECT | INTERFACE directive @external on OBJECT | FIELD_DEFINITION directive @key(fields: FieldSet!, resolvable: Boolean = true) repeatable on OBJECT | INTERFACE directive @inaccessible on | FIELD_DEFINITION | OBJECT | INTERFACE | UNION | ENUM | ENUM_VALUE | SCALAR | INPUT_OBJECT | INPUT_FIELD_DEFINITION | ARGUMENT_DEFINITION directive @interfaceObject on OBJECT directive @override(from: String!, label: String) on FIELD_DEFINITION directive @provides(fields: FieldSet!) on FIELD_DEFINITION directive @requires(fields: FieldSet!) on FIELD_DEFINITION directive @shareable repeatable on FIELD_DEFINITION | OBJECT directive @tag(name: String!) repeatable on | FIELD_DEFINITION | INTERFACE | OBJECT | UNION | ARGUMENT_DEFINITION | SCALAR | ENUM | ENUM_VALUE | INPUT_OBJECT | INPUT_FIELD_DEFINITION directive @authenticated on FIELD_DEFINITION | OBJECT | INTERFACE | SCALAR | ENUM directive @requiresScopes(scopes: [[federation__Scope!]!]!) on FIELD_DEFINITION | OBJECT | INTERFACE | SCALAR | ENUM directive @policy(policies: [[federation__Policy!]!]!) on | FIELD_DEFINITION | OBJECT | INTERFACE | SCALAR | ENUM scalar federation__Policy scalar federation__Scope scalar FieldSet ``` Read about directives in [official documentation](https://www.apollographql.com/docs/federation/federated-types/federated-directives) Each type which is decorated with `@key` or `@extends` is added to the `_Entity` union. The [`__resolve_reference` method](https://www.apollographql.com/docs/federation/api/apollo-federation/#__resolvereference) can be defined for each type that is an entity. Note that since the notation with double underscores can be problematic in Python for model inheritance this resolver method can also be named `_resolve_reference` (the `__resolve_reference` method will take precedence if both are declared). This method is called whenever an entity is requested as part of the fulfilling a query plan. If not explicitly defined, the default resolver is used. The default resolver just creates instance of type with passed fieldset as kwargs, see [`entity.get_entity_query`](graphene_federation/entity.py) for more details * You should define `__resolve_reference`, if you need to extract object before passing it to fields resolvers (example: [FileNode](integration_tests/service_b/src/schema.py)) * You should not define `__resolve_reference`, if fields resolvers need only data passed in fieldset (example: [FunnyText](integration_tests/service_a/src/schema.py)) Read more in [official documentation](https://www.apollographql.com/docs/apollo-server/api/apollo-federation/#__resolvereference). ------------------------ ## Example Here is an example of implementation based on the [Apollo Federation introduction example](https://www.apollographql.com/docs/federation/). It implements a federation schema for a basic e-commerce application over three services: accounts, products, reviews. ### Accounts First add an account service that expose a `User` type that can then be referenced in other services by its `id` field: ```python from graphene import Field, Int, ObjectType, String from graphene_federation import LATEST_VERSION, build_schema, key @key("id") class User(ObjectType): id = Int(required=True) username = String(required=True) def __resolve_reference(self, info, **kwargs): """ Here we resolve the reference of the user entity referenced by its `id` field. """ return User(id=self.id, email=f"user_{self.id}@mail.com") class Query(ObjectType): me = Field(User) schema = build_schema(query=Query, federation_version=LATEST_VERSION) ``` ### Product The product service exposes a `Product` type that can be used by other services via the `upc` field: ```python from graphene import Argument, Int, List, ObjectType, String from graphene_federation import LATEST_VERSION, build_schema, key @key("upc") class Product(ObjectType): upc = String(required=True) name = String(required=True) price = Int() def __resolve_reference(self, info, **kwargs): """ Here we resolve the reference of the product entity referenced by its `upc` field. """ return Product(upc=self.upc, name=f"product {self.upc}") class Query(ObjectType): topProducts = List(Product, first=Argument(Int, default_value=5)) schema = build_schema(query=Query, federation_version=LATEST_VERSION) ``` ### Reviews The reviews service exposes a `Review` type which has a link to both the `User` and `Product` types. It also has the ability to provide the username of the `User`. On top of that it adds to the `User`/`Product` types (that are both defined in other services) the ability to get their reviews. ```python from graphene import Field, Int, List, ObjectType, String from graphene_federation import LATEST_VERSION, build_schema, external, key, provides @key("id") class User(ObjectType): id = external(Int(required=True)) reviews = List(lambda: Review) def resolve_reviews(self, info, *args, **kwargs): """ Get all the reviews of a given user. (not implemented here) """ return [] @key("upc") class Product(ObjectType): upc = external(String(required=True)) reviews = List(lambda: Review) class Review(ObjectType): body = String() author = provides(Field(User), fields="username") product = Field(Product) class Query(ObjectType): review = Field(Review) schema = build_schema(query=Query, federation_version=LATEST_VERSION) ``` ### Federation Note that each schema declaration for the services is a valid graphql schema (it only adds the `_Entity` and `_Service` types). The best way to check that the decorator are set correctly is to request the service sdl: ```python from graphql import graphql query = """ query { _service { sdl } } """ result = graphql(schema, query) print(result.data["_service"]["sdl"]) ``` Those can then be used in a federated schema. You can find more examples in the unit / integration tests and [examples folder](examples/). There is also a cool [example](https://github.com/preply/graphene-federation/issues/1) of integration with Mongoengine. ------------------------ ## Other Notes ### build_schema new arguments - `schema_directives` (`Collection[SchemaDirective]`): Directives that can be defined at `DIRECTIVE_LOCATION.SCHEMA` with their argument values. - `include_graphql_spec_directives` (`bool`): Includes directives defined by GraphQL spec (`@include`, `@skip`, `@deprecated`, `@specifiedBy`) - `federation_version` (`FederationVersion`): Specify the version explicit (default STABLE_VERSION) ### Directives Additional arguments - `federation_version`: (`FederationVersion` = `LATEST_VERSION`) : You can use this to take a directive from a particular federation version Note: The `federation_version` in `build_schema` is given higher priority. If the directive you have chosen is not compatible, it will raise an error ### Custom Directives You can define custom directives as follows ```python from graphene import Field, ObjectType, String from graphql import GraphQLArgument, GraphQLInt, GraphQLNonNull from graphene_federation import ComposableDirective, DirectiveLocation, LATEST_VERSION from graphene_federation import build_schema CacheDirective = ComposableDirective( name="cache", locations=[DirectiveLocation.FIELD_DEFINITION, DirectiveLocation.OBJECT], args={ "maxAge": GraphQLArgument( GraphQLNonNull(GraphQLInt), description="Specifies the maximum age for cache in seconds." ), }, description="Caching directive to control cache behavior.", spec_url="https://specs.example.dev/directives/v1.0", ) cache = CacheDirective.decorator() @cache(max_age=20) class Review(ObjectType): body = cache(field=String(), max_age=100) class Query(ObjectType): review = Field(Review) schema = build_schema( query=Query, directives=(CacheDirective,), federation_version=LATEST_VERSION , ) ``` This will automatically add @link and @composeDirective to schema ```graphql extend schema @link(url: "https://specs.apollo.dev/federation/v2.6", import: ["@composeDirective"]) @link(url: "https://specs.example.dev/directives/v1.0", import: ["@cache"]) @composeDirective(name: "@cache") """Caching directive to control cache behavior.""" directive @cache( """Specifies the maximum age for cache in seconds.""" maxAge: Int! ) on FIELD_DEFINITION | OBJECT type Query { review: Review _service: _Service! } type Review @cache(maxAge: 20) { body: String @cache(maxAge: 100) } ``` If you wish to add the schema_directives `@link` `@composeDirective` manually. You can pass the `add_to_schema_directives` as `False` ```python from graphene import Field, ObjectType, String from graphql import GraphQLArgument, GraphQLInt, GraphQLNonNull from graphene_federation import (ComposableDirective, DirectiveLocation, LATEST_VERSION, build_schema, compose_directive, link_directive) CacheDirective = ComposableDirective( name="cache", locations=[DirectiveLocation.FIELD_DEFINITION, DirectiveLocation.OBJECT], args={ "maxAge": GraphQLArgument( GraphQLNonNull(GraphQLInt), description="Specifies the maximum age for cache in seconds." ), }, description="Caching directive to control cache behavior.", add_to_schema_directives=False ) cache = CacheDirective.decorator() @cache(max_age=20) class Review(ObjectType): body = cache(field=String(), max_age=100) class Query(ObjectType): review = Field(Review) schema = build_schema( query=Query, directives=(CacheDirective,), schema_directives=( link_directive(url="https://specs.example.dev/directives/v1.0", import_=['@cache']), compose_directive(name='@cache'), ), federation_version=LATEST_VERSION, ) ``` ### Custom field name When using decorator on a field with custom name #### Case 1 (auto_camelcase=False) ```python @key("identifier") @key("validEmail") class User(ObjectType): identifier = ID() email = String(name="validEmail") class Query(ObjectType): user = Field(User) schema = build_schema(query=Query, federation_version=LATEST_VERSION, auto_camelcase=False) # Disable auto_camelcase ``` This works correctly. By default `fields` of `@key`,`@requires` and `@provides` are not converted to camel case if `auto_camelcase` is set to `False` #### Case 2 (auto_camelcase=True) ```python @key("identifier") @key("valid_email") class User(ObjectType): identifier = ID() email = String(name="valid_email") class Query(ObjectType): user = Field(User) schema = build_schema(query=Query, federation_version=LATEST_VERSION) # auto_camelcase Enabled ``` This will raise an error `@key, field "validEmail" does not exist on type "User"`. Because The decorator auto camel-cased the `field` value of key, as schema has `auto_camelcase=True` (default) To fix this, pass `auto_case=False` in the `@key`, `@requires` or `@provides` argument ```python @key("identifier") @key("valid_email", auto_case=False) class User(ObjectType): identifier = ID() email = String(name="valid_email") class Query(ObjectType): user = Field(User) schema = build_schema(query=Query, federation_version=LATEST_VERSION) # auto_camelcase=True ``` ------------------------ ## Known Issues - Using `@composeDirective` with `@link` in Federation `v2.6` shows error in rover, rover cli only supports upto `v2.5` as of 16/01/2024 ## Contributing * You can run the unit tests by doing: `make tests`. * You can run the integration tests by doing `make integration-build && make integration-test`. * You can get a development environment (on a Docker container) with `make dev-setup`. * You should use `black` to format your code. The tests are automatically run on Travis CI on push to GitHub. --------------------------- graphene-federation-3.2.0/docker-compose.yml000066400000000000000000000002271463304117000210600ustar00rootroot00000000000000version: '3.5' services: graphene_federation: container_name: graphene_federation build: context: . volumes: - ./:/workdir graphene-federation-3.2.0/examples/000077500000000000000000000000001463304117000172405ustar00rootroot00000000000000graphene-federation-3.2.0/examples/entities.py000066400000000000000000000027641463304117000214470ustar00rootroot00000000000000import graphene from graphene_federation import build_schema, key def get_file_by_id(id): return File(**{"id": id, "name": "test_name"}) class Author(graphene.ObjectType): id = graphene.ID(required=True) name = graphene.String(required=True) @key(fields="id") @key(fields="id author { name }") @key(fields="id author { id name }") class File(graphene.ObjectType): id = graphene.Int(required=True) name = graphene.String() author = graphene.Field(Author, required=True) def resolve_id(self, info, **kwargs): return 1 def resolve_name(self, info, **kwargs): return self.name def __resolve_reference(self, info, **kwargs): return get_file_by_id(self.id) class Query(graphene.ObjectType): file = graphene.Field(File) def resolve_file(self, **kwargs): return None # no direct access schema = build_schema(Query) query = """ query getSDL { _service { sdl } } """ result = schema.execute(query) print(result.data) # {'_service': {'sdl': 'type Query {\n file: File\n}\n\ntype File @key(fields: "id") {\n id: Int!\n name: String\n}'}} query = """ query entities($_representations: [_Any!]!) { _entities(representations: $_representations) { ... on File { id name } } } """ result = schema.execute( query, variables={"_representations": [{"__typename": "File", "id": 1}]} ) print(result.data) # {'_entities': [{'id': 1, 'name': 'test_name'}]} graphene-federation-3.2.0/examples/extend.py000066400000000000000000000012521463304117000211010ustar00rootroot00000000000000import graphene from graphene_federation import build_schema, extends, external, key @key("id") @extends class Message(graphene.ObjectType): id = external(graphene.Int(required=True)) def resolve_id(self, **kwargs): return 1 class Query(graphene.ObjectType): message = graphene.Field(Message) def resolve_file(self, **kwargs): return None # no direct access schema = build_schema(Query) query = """ query getSDL { _service { sdl } } """ result = schema.execute(query) print(result.data) # {'sdl': 'type Query {\n message: Message\n}\n\n type Message @key(fields: "id") @extends {\n id: Int! @external\n}'}} graphene-federation-3.2.0/examples/inaccessible.py000066400000000000000000000036141463304117000222420ustar00rootroot00000000000000import graphene from graphene_federation import ( LATEST_VERSION, inaccessible, external, provides, key, override, ) from graphene_federation import build_schema @key(fields="x") class Position(graphene.ObjectType): x = graphene.Int(required=True) y = external(graphene.Int(required=True)) z = inaccessible(graphene.Int(required=True)) a = provides(graphene.Int(), fields="x") b = override(graphene.Int(required=True), from_="h") @inaccessible class ReviewInterface(graphene.Interface): interfaced_body = graphene.String(required=True) @inaccessible class Review(graphene.ObjectType): class Meta: interfaces = (ReviewInterface,) id = graphene.Int(required=True) body = graphene.String(required=True) @inaccessible class Human(graphene.ObjectType): name = graphene.String() born_in = graphene.String() @inaccessible class Droid(graphene.ObjectType): name = graphene.String() primary_function = graphene.String() @inaccessible class Starship(graphene.ObjectType): name = graphene.String() length = graphene.Int() @inaccessible class SearchResult(graphene.Union): class Meta: types = (Human, Droid, Starship) class Query(graphene.ObjectType): position = graphene.Field(Position) schema = build_schema( Query, federation_version=LATEST_VERSION, types=(ReviewInterface, SearchResult, Review), ) query = """ query getSDL { _service { sdl } } """ result = schema.execute(query) print(result.data) # {'_service': {'sdl': 'extend schema @link(url: "https://specs.apollo.dev/federation/v2.0", import: ["@external", "@key", "@override", "@provides", "@inaccessible"])\ntype Query {\n position: Position\n}\n\ntype Position @key(fields: "x") {\n x: Int!\n y: Int! @external\n z: Int! @inaccessible\n a: Int @provides(fields: "x")\n b: Int! @override(from: "h")\n}'}} graphene-federation-3.2.0/examples/override.py000066400000000000000000000016601463304117000214340ustar00rootroot00000000000000import graphene from graphene_federation import ( LATEST_VERSION, build_schema, inaccessible, key, override, ) @key(fields="id") class Product(graphene.ObjectType): id = graphene.ID(required=True) in_stock = override(graphene.Boolean(required=True), "Products") out_stock = inaccessible(graphene.Boolean(required=True)) class Query(graphene.ObjectType): position = graphene.Field(Product) schema = build_schema(Query, federation_version=LATEST_VERSION) query = """ query getSDL { _service { sdl } } """ result = schema.execute(query) print(result.data) # {'_service': {'sdl': 'extend schema @link(url: "https://specs.apollo.dev/federation/v2.0", import: ["@key", "@override", "@inaccessible"])\ntype Query {\n position: Product\n}\n\ntype Product @key(fields: "id") {\n id: ID!\n inStock: Boolean! @override(from: "Products")\n outStock: Boolean! @inaccessible\n}'}} graphene-federation-3.2.0/examples/shareable.py000066400000000000000000000023741463304117000215460ustar00rootroot00000000000000import graphene from graphene import Interface from graphene_federation.shareable import shareable from graphene_federation import LATEST_VERSION, build_schema @shareable class Position(graphene.ObjectType): x = graphene.Int(required=True) y = shareable(graphene.Int(required=True)) @shareable class Human(graphene.ObjectType): name = graphene.String() born_in = graphene.String() @shareable class Droid(graphene.ObjectType): name = shareable(graphene.String()) primary_function = graphene.String() @shareable class Starship(graphene.ObjectType): name = graphene.String() length = shareable(graphene.Int()) @shareable class SearchResult(graphene.Union): class Meta: types = (Human, Droid, Starship) class Query(graphene.ObjectType): position = graphene.Field(Position) schema = build_schema(Query, federation_version=LATEST_VERSION, types=(SearchResult,)) query = """ query getSDL { _service { sdl } } """ result = schema.execute(query) print(result.data) # {'_service': {'sdl': 'extend schema @link(url: "https://specs.apollo.dev/federation/v2.0", import: ["@shareable"])\ntype Query {\n position: Position\n}\n\ntype Position @shareable {\n x: Int!\n y: Int! @shareable\n}'}} graphene-federation-3.2.0/examples/tag.py000066400000000000000000000017521463304117000203720ustar00rootroot00000000000000import graphene from graphene_federation.tag import tag from graphene_federation import LATEST_VERSION, build_schema, inaccessible, shareable class Product(graphene.ObjectType): id = graphene.ID(required=True) in_stock = tag(graphene.Boolean(required=True), "Products") out_stock = shareable(graphene.Boolean(required=True)) is_listed = inaccessible(graphene.Boolean(required=True)) class Query(graphene.ObjectType): position = graphene.Field(Product) schema = build_schema(Query, federation_version=LATEST_VERSION) query = """ query getSDL { _service { sdl } } """ result = schema.execute(query) print(result.data) # {'_service': {'sdl': 'extend schema @link(url: "https://specs.apollo.dev/federation/v2.0", import: ["@inaccessible", "@shareable", "@tag"])\ntype Query {\n position: Product\n}\n\ntype Product {\n id: ID!\n inStock: Boolean! @tag(name: "Products")\n outStock: Boolean! @shareable\n isListed: Boolean! @inaccessible\n}'}} graphene-federation-3.2.0/federation_spec/000077500000000000000000000000001463304117000205545ustar00rootroot00000000000000graphene-federation-3.2.0/federation_spec/federation-v1.0.graphql000066400000000000000000000006071463304117000247410ustar00rootroot00000000000000directive @key(fields: _FieldSet!) repeatable on OBJECT | INTERFACE directive @requires(fields: _FieldSet!) on FIELD_DEFINITION directive @provides(fields: _FieldSet!) on FIELD_DEFINITION directive @external on FIELD_DEFINITION scalar _FieldSet # this is an optional directive # used in frameworks that don't natively support GraphQL extend syntax directive @extends on OBJECT | INTERFACE graphene-federation-3.2.0/federation_spec/federation-v2.0.graphql000066400000000000000000000014541463304117000247430ustar00rootroot00000000000000directive @key(fields: FieldSet!, resolvable: Boolean = true) repeatable on OBJECT | INTERFACE directive @requires(fields: FieldSet!) on FIELD_DEFINITION directive @provides(fields: FieldSet!) on FIELD_DEFINITION directive @external on OBJECT | FIELD_DEFINITION directive @shareable on FIELD_DEFINITION | OBJECT directive @extends on OBJECT | INTERFACE directive @override(from: String!) on FIELD_DEFINITION directive @inaccessible on | FIELD_DEFINITION | OBJECT | INTERFACE | UNION | ENUM | ENUM_VALUE | SCALAR | INPUT_OBJECT | INPUT_FIELD_DEFINITION | ARGUMENT_DEFINITION directive @tag(name: String!) repeatable on | FIELD_DEFINITION | INTERFACE | OBJECT | UNION | ARGUMENT_DEFINITION | SCALAR | ENUM | ENUM_VALUE | INPUT_OBJECT | INPUT_FIELD_DEFINITION scalar FieldSetgraphene-federation-3.2.0/federation_spec/federation-v2.1.graphql000066400000000000000000000015551463304117000247460ustar00rootroot00000000000000directive @composeDirective(name: String!) repeatable on SCHEMA directive @extends on OBJECT | INTERFACE directive @external on OBJECT | FIELD_DEFINITION directive @key(fields: FieldSet!, resolvable: Boolean = true) repeatable on OBJECT | INTERFACE directive @inaccessible on | FIELD_DEFINITION | OBJECT | INTERFACE | UNION | ENUM | ENUM_VALUE | SCALAR | INPUT_OBJECT | INPUT_FIELD_DEFINITION | ARGUMENT_DEFINITION directive @override(from: String!) on FIELD_DEFINITION directive @provides(fields: FieldSet!) on FIELD_DEFINITION directive @requires(fields: FieldSet!) on FIELD_DEFINITION directive @shareable on FIELD_DEFINITION | OBJECT directive @tag(name: String!) repeatable on | FIELD_DEFINITION | INTERFACE | OBJECT | UNION | ARGUMENT_DEFINITION | SCALAR | ENUM | ENUM_VALUE | INPUT_OBJECT | INPUT_FIELD_DEFINITION scalar FieldSet graphene-federation-3.2.0/federation_spec/federation-v2.2.graphql000066400000000000000000000015701463304117000247440ustar00rootroot00000000000000directive @composeDirective(name: String!) repeatable on SCHEMA directive @extends on OBJECT | INTERFACE directive @external on OBJECT | FIELD_DEFINITION directive @key(fields: FieldSet!, resolvable: Boolean = true) repeatable on OBJECT | INTERFACE directive @inaccessible on | FIELD_DEFINITION | OBJECT | INTERFACE | UNION | ENUM | ENUM_VALUE | SCALAR | INPUT_OBJECT | INPUT_FIELD_DEFINITION | ARGUMENT_DEFINITION directive @override(from: String!) on FIELD_DEFINITION directive @provides(fields: FieldSet!) on FIELD_DEFINITION directive @requires(fields: FieldSet!) on FIELD_DEFINITION directive @shareable repeatable on FIELD_DEFINITION | OBJECT directive @tag(name: String!) repeatable on | FIELD_DEFINITION | INTERFACE | OBJECT | UNION | ARGUMENT_DEFINITION | SCALAR | ENUM | ENUM_VALUE | INPUT_OBJECT | INPUT_FIELD_DEFINITION scalar FieldSet graphene-federation-3.2.0/federation_spec/federation-v2.3.graphql000066400000000000000000000016351463304117000247470ustar00rootroot00000000000000directive @composeDirective(name: String!) repeatable on SCHEMA directive @extends on OBJECT | INTERFACE directive @external on OBJECT | FIELD_DEFINITION directive @key(fields: FieldSet!, resolvable: Boolean = true) repeatable on OBJECT | INTERFACE directive @inaccessible on | FIELD_DEFINITION | OBJECT | INTERFACE | UNION | ENUM | ENUM_VALUE | SCALAR | INPUT_OBJECT | INPUT_FIELD_DEFINITION | ARGUMENT_DEFINITION directive @interfaceObject on OBJECT directive @override(from: String!) on FIELD_DEFINITION directive @provides(fields: FieldSet!) on FIELD_DEFINITION directive @requires(fields: FieldSet!) on FIELD_DEFINITION directive @shareable repeatable on FIELD_DEFINITION | OBJECT directive @tag(name: String!) repeatable on | FIELD_DEFINITION | INTERFACE | OBJECT | UNION | ARGUMENT_DEFINITION | SCALAR | ENUM | ENUM_VALUE | INPUT_OBJECT | INPUT_FIELD_DEFINITION scalar FieldSet graphene-federation-3.2.0/federation_spec/federation-v2.4.graphql000066400000000000000000000016351463304117000247500ustar00rootroot00000000000000directive @composeDirective(name: String!) repeatable on SCHEMA directive @extends on OBJECT | INTERFACE directive @external on OBJECT | FIELD_DEFINITION directive @key(fields: FieldSet!, resolvable: Boolean = true) repeatable on OBJECT | INTERFACE directive @inaccessible on | FIELD_DEFINITION | OBJECT | INTERFACE | UNION | ENUM | ENUM_VALUE | SCALAR | INPUT_OBJECT | INPUT_FIELD_DEFINITION | ARGUMENT_DEFINITION directive @interfaceObject on OBJECT directive @override(from: String!) on FIELD_DEFINITION directive @provides(fields: FieldSet!) on FIELD_DEFINITION directive @requires(fields: FieldSet!) on FIELD_DEFINITION directive @shareable repeatable on FIELD_DEFINITION | OBJECT directive @tag(name: String!) repeatable on | FIELD_DEFINITION | INTERFACE | OBJECT | UNION | ARGUMENT_DEFINITION | SCALAR | ENUM | ENUM_VALUE | INPUT_OBJECT | INPUT_FIELD_DEFINITION scalar FieldSet graphene-federation-3.2.0/federation_spec/federation-v2.5.graphql000066400000000000000000000022251463304117000247450ustar00rootroot00000000000000directive @composeDirective(name: String!) repeatable on SCHEMA directive @extends on OBJECT | INTERFACE directive @external on OBJECT | FIELD_DEFINITION directive @key(fields: FieldSet!, resolvable: Boolean = true) repeatable on OBJECT | INTERFACE directive @inaccessible on | FIELD_DEFINITION | OBJECT | INTERFACE | UNION | ENUM | ENUM_VALUE | SCALAR | INPUT_OBJECT | INPUT_FIELD_DEFINITION | ARGUMENT_DEFINITION directive @interfaceObject on OBJECT directive @override(from: String!) on FIELD_DEFINITION directive @provides(fields: FieldSet!) on FIELD_DEFINITION directive @requires(fields: FieldSet!) on FIELD_DEFINITION directive @shareable repeatable on FIELD_DEFINITION | OBJECT directive @tag(name: String!) repeatable on | FIELD_DEFINITION | INTERFACE | OBJECT | UNION | ARGUMENT_DEFINITION | SCALAR | ENUM | ENUM_VALUE | INPUT_OBJECT | INPUT_FIELD_DEFINITION directive @authenticated on FIELD_DEFINITION | OBJECT | INTERFACE | SCALAR | ENUM directive @requiresScopes(scopes: [[federation__Scope!]!]!) on FIELD_DEFINITION | OBJECT | INTERFACE | SCALAR | ENUM scalar federation__Scope scalar FieldSet graphene-federation-3.2.0/federation_spec/federation-v2.6.graphql000066400000000000000000000024531463304117000247510ustar00rootroot00000000000000directive @composeDirective(name: String!) repeatable on SCHEMA directive @extends on OBJECT | INTERFACE directive @external on OBJECT | FIELD_DEFINITION directive @key(fields: FieldSet!, resolvable: Boolean = true) repeatable on OBJECT | INTERFACE directive @inaccessible on | FIELD_DEFINITION | OBJECT | INTERFACE | UNION | ENUM | ENUM_VALUE | SCALAR | INPUT_OBJECT | INPUT_FIELD_DEFINITION | ARGUMENT_DEFINITION directive @interfaceObject on OBJECT directive @override(from: String!) on FIELD_DEFINITION directive @provides(fields: FieldSet!) on FIELD_DEFINITION directive @requires(fields: FieldSet!) on FIELD_DEFINITION directive @shareable repeatable on FIELD_DEFINITION | OBJECT directive @tag(name: String!) repeatable on | FIELD_DEFINITION | INTERFACE | OBJECT | UNION | ARGUMENT_DEFINITION | SCALAR | ENUM | ENUM_VALUE | INPUT_OBJECT | INPUT_FIELD_DEFINITION directive @authenticated on FIELD_DEFINITION | OBJECT | INTERFACE | SCALAR | ENUM directive @requiresScopes(scopes: [[federation__Scope!]!]!) on FIELD_DEFINITION | OBJECT | INTERFACE | SCALAR | ENUM directive @policy(policies: [[federation__Policy!]!]!) on | FIELD_DEFINITION | OBJECT | INTERFACE | SCALAR | ENUM scalar federation__Policy scalar federation__Scope scalar FieldSet graphene-federation-3.2.0/federation_spec/federation-v2.7.graphql000066400000000000000000000024721463304117000247530ustar00rootroot00000000000000directive @composeDirective(name: String!) repeatable on SCHEMA directive @extends on OBJECT | INTERFACE directive @external on OBJECT | FIELD_DEFINITION directive @key(fields: FieldSet!, resolvable: Boolean = true) repeatable on OBJECT | INTERFACE directive @inaccessible on | FIELD_DEFINITION | OBJECT | INTERFACE | UNION | ENUM | ENUM_VALUE | SCALAR | INPUT_OBJECT | INPUT_FIELD_DEFINITION | ARGUMENT_DEFINITION directive @interfaceObject on OBJECT directive @override(from: String!, label: String) on FIELD_DEFINITION directive @provides(fields: FieldSet!) on FIELD_DEFINITION directive @requires(fields: FieldSet!) on FIELD_DEFINITION directive @shareable repeatable on FIELD_DEFINITION | OBJECT directive @tag(name: String!) repeatable on | FIELD_DEFINITION | INTERFACE | OBJECT | UNION | ARGUMENT_DEFINITION | SCALAR | ENUM | ENUM_VALUE | INPUT_OBJECT | INPUT_FIELD_DEFINITION directive @authenticated on FIELD_DEFINITION | OBJECT | INTERFACE | SCALAR | ENUM directive @requiresScopes(scopes: [[federation__Scope!]!]!) on FIELD_DEFINITION | OBJECT | INTERFACE | SCALAR | ENUM directive @policy(policies: [[federation__Policy!]!]!) on | FIELD_DEFINITION | OBJECT | INTERFACE | SCALAR | ENUM scalar federation__Policy scalar federation__Scope scalar FieldSet graphene-federation-3.2.0/graphene_federation/000077500000000000000000000000001463304117000214135ustar00rootroot00000000000000graphene-federation-3.2.0/graphene_federation/__init__.py000066400000000000000000000017111463304117000235240ustar00rootroot00000000000000from graphene_directives import DirectiveLocation from .apollo_versions import FederationVersion, LATEST_VERSION, STABLE_VERSION from .composable_directive import ComposableDirective from .directives import ( authenticated, extends, external, inaccessible, interface_object, key, override, policy, provides, requires, requires_scope, shareable, tag, ) from .schema import build_schema from .schema_directives import compose_directive, link_directive from .service import get_sdl __all__ = [ "FederationVersion", "LATEST_VERSION", "STABLE_VERSION", "build_schema", "ComposableDirective", "DirectiveLocation", "authenticated", "extends", "external", "inaccessible", "interface_object", "key", "override", "provides", "policy", "requires", "requires_scope", "shareable", "tag", "compose_directive", "link_directive", "get_sdl", ] graphene-federation-3.2.0/graphene_federation/apollo_versions/000077500000000000000000000000001463304117000246315ustar00rootroot00000000000000graphene-federation-3.2.0/graphene_federation/apollo_versions/__init__.py000066400000000000000000000046511463304117000267500ustar00rootroot00000000000000from graphql import GraphQLDirective from .v1_0 import get_directives as get_directives_v1_0 from .v2_0 import get_directives as get_directives_v2_0 from .v2_1 import get_directives as get_directives_v2_1 from .v2_2 import get_directives as get_directives_v2_2 from .v2_3 import get_directives as get_directives_v2_3 from .v2_4 import get_directives as get_directives_v2_4 from .v2_5 import get_directives as get_directives_v2_5 from .v2_6 import get_directives as get_directives_v2_6 from .v2_7 import get_directives as get_directives_v2_7 from .version import FederationVersion LATEST_VERSION = FederationVersion.VERSION_2_7 # Stable version is determined with the latest version that rover cli supports STABLE_VERSION = FederationVersion.VERSION_2_6 def get_directives_based_on_version( federation_version: FederationVersion, ) -> dict[str, GraphQLDirective]: """ Returns a dictionary of [directive_name, directive] for the specified federation version If no match is found for the specified federation version, the latest is taken """ if federation_version == FederationVersion.VERSION_1_0: return get_directives_v1_0() if federation_version == FederationVersion.VERSION_2_0: return get_directives_v2_0() if federation_version == FederationVersion.VERSION_2_1: return get_directives_v2_1() if federation_version == FederationVersion.VERSION_2_2: return get_directives_v2_2() if federation_version == FederationVersion.VERSION_2_3: return get_directives_v2_3() if federation_version == FederationVersion.VERSION_2_4: return get_directives_v2_4() if federation_version == FederationVersion.VERSION_2_5: return get_directives_v2_5() if federation_version == FederationVersion.VERSION_2_6: return get_directives_v2_6() if federation_version == FederationVersion.VERSION_2_7: return get_directives_v2_7() return get_directives_v2_7() def get_directive_from_name( directive_name: str, federation_version: FederationVersion ) -> GraphQLDirective: """ Get the GraphQL directive for the specified name with the given federation version """ directive = get_directives_based_on_version(federation_version).get( directive_name, None ) if directive is None: raise ValueError( f"@{directive_name} not supported on federation version {federation_version}" ) return directive graphene-federation-3.2.0/graphene_federation/apollo_versions/v1_0.py000066400000000000000000000043121463304117000257500ustar00rootroot00000000000000from graphene_directives import CustomDirective, DirectiveLocation from graphql import GraphQLArgument, GraphQLDirective, GraphQLNonNull from graphene_federation.scalars import _FieldSet from graphene_federation.transform import field_set_case_transform from graphene_federation.validators import ( validate_key, validate_provides, validate_requires, ) key_directive = CustomDirective( name="key", locations=[ DirectiveLocation.OBJECT, DirectiveLocation.INTERFACE, ], args={"fields": GraphQLArgument(GraphQLNonNull(_FieldSet))}, description="Federation @key directive", is_repeatable=True, add_definition_to_schema=False, non_field_validator=validate_key, input_transform=field_set_case_transform, ) requires_directive = CustomDirective( name="requires", locations=[ DirectiveLocation.FIELD_DEFINITION, ], args={"fields": GraphQLArgument(GraphQLNonNull(_FieldSet))}, description="Federation @requires directive", add_definition_to_schema=False, field_validator=validate_requires, input_transform=field_set_case_transform, ) provides_directive = CustomDirective( name="provides", locations=[ DirectiveLocation.FIELD_DEFINITION, ], args={"fields": GraphQLArgument(GraphQLNonNull(_FieldSet))}, description="Federation @provides directive", add_definition_to_schema=False, field_validator=validate_provides, input_transform=field_set_case_transform, ) external_directive = CustomDirective( name="external", locations=[ DirectiveLocation.FIELD_DEFINITION, ], description="Federation @external directive", add_definition_to_schema=False, ) extends_directive = CustomDirective( name="extends", locations=[ DirectiveLocation.OBJECT, DirectiveLocation.INTERFACE, ], description="Federation @extends directive", add_definition_to_schema=False, ) def get_directives() -> dict[str, GraphQLDirective]: return { directive.name: directive for directive in [ key_directive, requires_directive, provides_directive, external_directive, extends_directive, ] } graphene-federation-3.2.0/graphene_federation/apollo_versions/v2_0.py000066400000000000000000000101201463304117000257430ustar00rootroot00000000000000from graphene_directives import CustomDirective, DirectiveLocation from graphql import ( GraphQLArgument, GraphQLBoolean, GraphQLDirective, GraphQLNonNull, GraphQLString, ) from graphene_federation.scalars import FieldSet from graphene_federation.transform import field_set_case_transform from graphene_federation.validators import ( validate_key, validate_provides, validate_requires, ) from .v1_0 import extends_directive key_directive = CustomDirective( name="key", locations=[ DirectiveLocation.OBJECT, DirectiveLocation.INTERFACE, ], args={ "fields": GraphQLArgument(GraphQLNonNull(FieldSet)), # Changed from v1.0 "resolvable": GraphQLArgument(GraphQLBoolean, default_value=True), }, description="Federation @key directive", is_repeatable=True, add_definition_to_schema=False, non_field_validator=validate_key, input_transform=field_set_case_transform, ) requires_directive = CustomDirective( name="requires", locations=[ DirectiveLocation.FIELD_DEFINITION, ], args={ "fields": GraphQLArgument(GraphQLNonNull(FieldSet)) }, # Changed _FieldSet -> FieldSet description="Federation @requires directive", add_definition_to_schema=False, field_validator=validate_requires, input_transform=field_set_case_transform, ) provides_directive = CustomDirective( name="provides", locations=[ DirectiveLocation.FIELD_DEFINITION, ], args={ "fields": GraphQLArgument(GraphQLNonNull(FieldSet)) }, # Changed _FieldSet -> FieldSet description="Federation @provides directive", add_definition_to_schema=False, field_validator=validate_provides, input_transform=field_set_case_transform, ) external_directive = CustomDirective( name="external", locations=[ DirectiveLocation.OBJECT, # Changed from v1.0 DirectiveLocation.FIELD_DEFINITION, ], description="Federation @external directive", add_definition_to_schema=False, ) shareable_directive = CustomDirective( name="shareable", locations=[ DirectiveLocation.FIELD_DEFINITION, DirectiveLocation.OBJECT, ], description="Federation @shareable directive", add_definition_to_schema=False, ) override_directive = CustomDirective( name="override", locations=[ DirectiveLocation.FIELD_DEFINITION, ], args={ "from": GraphQLArgument(GraphQLNonNull(GraphQLString)), }, description="Federation @override directive", add_definition_to_schema=False, ) inaccessible_directive = CustomDirective( name="inaccessible", locations=[ DirectiveLocation.FIELD_DEFINITION, DirectiveLocation.OBJECT, DirectiveLocation.INTERFACE, DirectiveLocation.UNION, DirectiveLocation.ENUM, DirectiveLocation.ENUM_VALUE, DirectiveLocation.SCALAR, DirectiveLocation.INPUT_OBJECT, DirectiveLocation.INPUT_FIELD_DEFINITION, DirectiveLocation.ARGUMENT_DEFINITION, ], description="Federation @inaccessible directive", add_definition_to_schema=False, ) tag_directive = CustomDirective( name="tag", locations=[ DirectiveLocation.FIELD_DEFINITION, DirectiveLocation.INTERFACE, DirectiveLocation.OBJECT, DirectiveLocation.UNION, DirectiveLocation.ARGUMENT_DEFINITION, DirectiveLocation.SCALAR, DirectiveLocation.ENUM, DirectiveLocation.ENUM_VALUE, DirectiveLocation.INPUT_OBJECT, DirectiveLocation.INPUT_FIELD_DEFINITION, ], description="Federation @tag directive", add_definition_to_schema=False, ) def get_directives() -> dict[str, GraphQLDirective]: return { directive.name: directive for directive in [ key_directive, requires_directive, provides_directive, external_directive, shareable_directive, extends_directive, # From v1.0 override_directive, inaccessible_directive, tag_directive, ] } graphene-federation-3.2.0/graphene_federation/apollo_versions/v2_1.py000066400000000000000000000013401463304117000257500ustar00rootroot00000000000000from graphene_directives import CustomDirective, DirectiveLocation from graphql import ( GraphQLArgument, GraphQLDirective, GraphQLNonNull, GraphQLString, ) from .v2_0 import get_directives as get_directives_v2_0 compose_directive = CustomDirective( name="composeDirective", locations=[ DirectiveLocation.SCHEMA, ], args={ "name": GraphQLArgument(GraphQLNonNull(GraphQLString)), }, description="Federation @composeDirective directive", add_definition_to_schema=False, ) def get_directives() -> dict[str, GraphQLDirective]: directives = get_directives_v2_0() directives.update({directive.name: directive for directive in [compose_directive]}) return directives graphene-federation-3.2.0/graphene_federation/apollo_versions/v2_2.py000066400000000000000000000012461463304117000257560ustar00rootroot00000000000000from graphene_directives import CustomDirective, DirectiveLocation from graphql import GraphQLDirective from .v2_1 import get_directives as get_directives_v2_1 shareable_directive = CustomDirective( name="shareable", locations=[ DirectiveLocation.FIELD_DEFINITION, DirectiveLocation.OBJECT, ], description="Federation @shareable directive", add_definition_to_schema=False, is_repeatable=True, # Changed from v2.1 ) def get_directives() -> dict[str, GraphQLDirective]: directives = get_directives_v2_1() directives.update( {directive.name: directive for directive in [shareable_directive]} ) return directives graphene-federation-3.2.0/graphene_federation/apollo_versions/v2_3.py000066400000000000000000000011771463304117000257620ustar00rootroot00000000000000from graphene_directives import CustomDirective, DirectiveLocation from graphql import GraphQLDirective from .v2_2 import get_directives as get_directives_v2_2 interface_object_directive = CustomDirective( name="interfaceObject", locations=[ DirectiveLocation.OBJECT, ], description="Federation @interfaceObject directive", add_definition_to_schema=False, is_repeatable=True, ) def get_directives() -> dict[str, GraphQLDirective]: directives = get_directives_v2_2() directives.update( {directive.name: directive for directive in [interface_object_directive]} ) return directives graphene-federation-3.2.0/graphene_federation/apollo_versions/v2_4.py000066400000000000000000000003361463304117000257570ustar00rootroot00000000000000from graphql import GraphQLDirective from .v2_3 import get_directives as get_directives_v2_3 # No Change, Added Subscription Support def get_directives() -> dict[str, GraphQLDirective]: return get_directives_v2_3() graphene-federation-3.2.0/graphene_federation/apollo_versions/v2_5.py000066400000000000000000000030261463304117000257570ustar00rootroot00000000000000from graphene_directives import CustomDirective, DirectiveLocation from graphql import GraphQLArgument, GraphQLDirective, GraphQLList, GraphQLNonNull from .v2_4 import get_directives as get_directives_v2_4 from graphene_federation.scalars import FederationScope authenticated_directive = CustomDirective( name="authenticated", locations=[ DirectiveLocation.FIELD_DEFINITION, DirectiveLocation.OBJECT, DirectiveLocation.INTERFACE, DirectiveLocation.SCALAR, DirectiveLocation.ENUM, ], description="Federation @authenticated directive", add_definition_to_schema=False, ) requires_scope_directive = CustomDirective( name="requiresScopes", locations=[ DirectiveLocation.FIELD_DEFINITION, DirectiveLocation.OBJECT, DirectiveLocation.INTERFACE, DirectiveLocation.SCALAR, DirectiveLocation.ENUM, ], args={ "scopes": GraphQLArgument( GraphQLNonNull( GraphQLList( GraphQLNonNull(GraphQLList(GraphQLNonNull(FederationScope))) ) ) ), }, description="Federation @requiresScopes directive", add_definition_to_schema=False, ) # No Change, Added Subscription Support def get_directives() -> dict[str, GraphQLDirective]: directives = get_directives_v2_4() directives.update( { directive.name: directive for directive in [authenticated_directive, requires_scope_directive] } ) return directives graphene-federation-3.2.0/graphene_federation/apollo_versions/v2_6.py000066400000000000000000000021021463304117000257520ustar00rootroot00000000000000from graphene_directives import CustomDirective, DirectiveLocation from graphql import GraphQLArgument, GraphQLDirective, GraphQLList, GraphQLNonNull from .v2_5 import get_directives as get_directives_v2_5 from graphene_federation.scalars import FederationPolicy policy_directive = CustomDirective( name="policy", locations=[ DirectiveLocation.FIELD_DEFINITION, DirectiveLocation.OBJECT, DirectiveLocation.INTERFACE, DirectiveLocation.SCALAR, DirectiveLocation.ENUM, ], args={ "policies": GraphQLArgument( GraphQLNonNull( GraphQLList( GraphQLNonNull(GraphQLList(GraphQLNonNull(FederationPolicy))) ) ) ), }, description="Federation @policy directive", add_definition_to_schema=False, ) # No Change, Added Subscription Support def get_directives() -> dict[str, GraphQLDirective]: directives = get_directives_v2_5() directives.update({directive.name: directive for directive in [policy_directive]}) return directives graphene-federation-3.2.0/graphene_federation/apollo_versions/v2_7.py000066400000000000000000000014411463304117000257600ustar00rootroot00000000000000from graphene_directives import CustomDirective, DirectiveLocation from graphql import GraphQLArgument, GraphQLDirective, GraphQLNonNull, GraphQLString from .v2_6 import get_directives as get_directives_v2_6 override_directive = CustomDirective( name="override", locations=[ DirectiveLocation.FIELD_DEFINITION, ], args={ "from": GraphQLArgument(GraphQLNonNull(GraphQLString)), "label": GraphQLArgument(GraphQLString), }, description="Federation @override directive", add_definition_to_schema=False, ) # @override Change, Added label argument def get_directives() -> dict[str, GraphQLDirective]: directives = get_directives_v2_6() directives.update({directive.name: directive for directive in [override_directive]}) return directives graphene-federation-3.2.0/graphene_federation/apollo_versions/version.py000066400000000000000000000004171463304117000266720ustar00rootroot00000000000000from enum import Enum class FederationVersion(Enum): VERSION_1_0 = "1.0" VERSION_2_0 = "2.0" VERSION_2_1 = "2.1" VERSION_2_2 = "2.2" VERSION_2_3 = "2.3" VERSION_2_4 = "2.4" VERSION_2_5 = "2.5" VERSION_2_6 = "2.6" VERSION_2_7 = "2.7" graphene-federation-3.2.0/graphene_federation/composable_directive.py000066400000000000000000000046041463304117000261530ustar00rootroot00000000000000from typing import Any, Callable, Collection, Dict, Optional from graphene_directives import CustomDirective, DirectiveLocation, directive_decorator from graphql import ( DirectiveDefinitionNode, GraphQLArgument, GraphQLDirective, ) class ComposableDirective(GraphQLDirective): def __init__( self, name: str, locations: Collection[DirectiveLocation], args: Optional[Dict[str, GraphQLArgument]] = None, is_repeatable: bool = False, description: Optional[str] = None, extensions: Optional[Dict[str, Any]] = None, ast_node: Optional[DirectiveDefinitionNode] = None, spec_url: str = None, add_to_schema_directives: bool = True, ) -> None: """ Creates a Federation Supported GraphQLDirective :param name: (GraphQLDirective param) :param args: (GraphQLDirective param) :param is_repeatable: (GraphQLDirective param) :param description: (GraphQLDirective param) :param extensions: (GraphQLDirective param) :param ast_node: (GraphQLDirective param) :param spec_url: url of the directive to be set in url of @link :param add_to_schema_directives: Adds schema_directives @composeDirective and @link to schema automatically """ if add_to_schema_directives: assert spec_url is not None, "ComposableDirective requires spec_url" self.spec_url = spec_url self.add_to_schema_directives = add_to_schema_directives self.graphene_directive = CustomDirective( name=name, locations=locations, args=args, is_repeatable=is_repeatable, description=description, extensions=extensions, ast_node=ast_node, ) parent_attributes = { "name", "locations", "args", "is_repeatable", "description", "extensions", "ast_node", } parent_kwargs = {} # Copy attributes of graphene_directive for k, v in self.graphene_directive.__dict__.items(): if k not in parent_attributes: setattr(self, k, v) else: parent_kwargs[k] = v super().__init__(**parent_kwargs) def decorator(self) -> Callable: return directive_decorator(self.graphene_directive) graphene-federation-3.2.0/graphene_federation/directives/000077500000000000000000000000001463304117000235545ustar00rootroot00000000000000graphene-federation-3.2.0/graphene_federation/directives/__init__.py000066400000000000000000000006521463304117000256700ustar00rootroot00000000000000from .authenticated import authenticated from .extends import extends from .external import external from .inaccessible import inaccessible from .interface_object import interface_object from .key import key from .override import override from .policy import policy from .provides import provides from .requires import requires from .requires_scopes import requires_scope from .shareable import shareable from .tag import tag graphene-federation-3.2.0/graphene_federation/directives/authenticated.py000066400000000000000000000020431463304117000267470ustar00rootroot00000000000000from typing import Callable from graphene_directives import directive_decorator from graphene_federation.apollo_versions import ( FederationVersion, LATEST_VERSION, get_directive_from_name, ) from .utils import is_non_field def authenticated( graphene_type=None, *, federation_version: FederationVersion = LATEST_VERSION, ) -> Callable: """ Indicates to composition that the target element is accessible only to the authenticated supergraph users. For more granular access control, see the @requiresScopes directive. Reference: https://www.apollographql.com/docs/federation/federated-types/federated-directives/#authenticated """ directive = get_directive_from_name("authenticated", federation_version) decorator = directive_decorator(directive) def wrapper(field_or_type): if is_non_field(field_or_type): return decorator(field=None)(field_or_type) return decorator(field=field_or_type) if graphene_type: return wrapper(graphene_type) return wrapper graphene-federation-3.2.0/graphene_federation/directives/extends.py000066400000000000000000000024461463304117000256060ustar00rootroot00000000000000from typing import Callable from graphene_directives import directive_decorator from graphene_federation.apollo_versions import ( FederationVersion, LATEST_VERSION, get_directive_from_name, ) from .utils import is_non_field def extends( graphene_type=None, *, federation_version: FederationVersion = LATEST_VERSION, ) -> Callable: """ Indicates that an object or interface definition is an extension of another definition of that same type. Reference: https://www.apollographql.com/docs/federation/federated-types/federated-directives/#extends """ directive = get_directive_from_name("extends", federation_version) decorator = directive_decorator(directive) def wrapper(field_or_type): if is_non_field(field_or_type): return decorator(field=None)(field_or_type) raise TypeError( "\n".join( [ f"\nInvalid Usage of {directive}.", "Must be applied on a class of ObjectType|InterfaceType", "Example:", f"{directive}", "class Product(graphene.ObjectType)", "\t...", ] ) ) if graphene_type: return wrapper(graphene_type) return wrapper graphene-federation-3.2.0/graphene_federation/directives/external.py000066400000000000000000000021721463304117000257520ustar00rootroot00000000000000from typing import Callable from graphene_directives import directive_decorator from graphene_federation.apollo_versions import ( FederationVersion, LATEST_VERSION, get_directive_from_name, ) from .utils import is_non_field def external( graphene_type=None, *, federation_version: FederationVersion = LATEST_VERSION, ) -> Callable: """ Indicates that this subgraph usually can't resolve a particular object field, but it still needs to define that field for other purposes. This directive is always used in combination with another directive that references object fields, such as @provides or @requires. Reference: https://www.apollographql.com/docs/federation/federated-types/federated-directives/#external """ directive = get_directive_from_name("external", federation_version) decorator = directive_decorator(directive) def wrapper(field_or_type): if is_non_field(field_or_type): return decorator(field=None)(field_or_type) return decorator(field=field_or_type) if graphene_type: return wrapper(graphene_type) return wrapper graphene-federation-3.2.0/graphene_federation/directives/inaccessible.py000066400000000000000000000021201463304117000265450ustar00rootroot00000000000000from typing import Callable from graphene_directives import directive_decorator from graphene_federation.apollo_versions import ( FederationVersion, LATEST_VERSION, get_directive_from_name, ) from .utils import is_non_field def inaccessible( graphene_type=None, *, federation_version: FederationVersion = LATEST_VERSION, ) -> Callable: """ Indicates that a definition in the subgraph schema should be omitted from the router's API schema, even if that definition is also present in other subgraphs. This means that the field is not exposed to clients at all. Reference: https://www.apollographql.com/docs/federation/federated-types/federated-directives/#inaccessible """ directive = get_directive_from_name("inaccessible", federation_version) decorator = directive_decorator(directive) def wrapper(field_or_type): if is_non_field(field_or_type): return decorator(field=None)(field_or_type) return decorator(field=field_or_type) if graphene_type: return wrapper(graphene_type) return wrapper graphene-federation-3.2.0/graphene_federation/directives/interface_object.py000066400000000000000000000026731463304117000274240ustar00rootroot00000000000000from typing import Callable from graphene_directives import directive_decorator from graphene_federation.apollo_versions import ( FederationVersion, LATEST_VERSION, get_directive_from_name, ) from .utils import is_non_field def interface_object( graphene_type=None, *, federation_version: FederationVersion = LATEST_VERSION, ) -> Callable: """ Indicates that an object definition serves as an abstraction of another subgraph's entity interface. This abstraction enables a subgraph to automatically contribute fields to all entities that implement a particular entity interface. Reference: https://www.apollographql.com/docs/federation/federated-types/federated-directives/#interfaceobject """ directive = get_directive_from_name("interfaceObject", federation_version) decorator = directive_decorator(directive) def wrapper(field_or_type): if is_non_field(field_or_type): return decorator(field=None)(field_or_type) raise TypeError( "\n".join( [ f"\nInvalid Usage of {directive}.", "Must be applied on a class of ObjectType", "Example:", f"{directive}", "class Product(graphene.ObjectType)", "\t...", ] ) ) if graphene_type: return wrapper(graphene_type) return wrapper graphene-federation-3.2.0/graphene_federation/directives/key.py000066400000000000000000000036161463304117000247240ustar00rootroot00000000000000from typing import Any, Union from graphene_directives import directive_decorator from graphene_federation.apollo_versions import ( FederationVersion, LATEST_VERSION, get_directive_from_name, ) from graphene_federation.validators import InternalNamespace, ast_to_str, build_ast from .utils import is_non_field def key( fields: Union[str, list[str]], resolvable: bool = None, *, auto_case: bool = True, federation_version: FederationVersion = LATEST_VERSION, ) -> Any: """ Designates an object type as an entity and specifies its key fields (a set of fields that the subgraph can use to uniquely identify any instance of the entity). You can apply multiple @key directives to a single entity (to specify multiple valid sets of key fields) Reference: https://www.apollographql.com/docs/federation/federated-types/federated-directives/#key """ directive = get_directive_from_name("key", federation_version) decorator = directive_decorator(directive) fields = ast_to_str( build_ast( fields=fields if isinstance(fields, str) else " ".join(fields), directive_name=str(directive), ) ) if not auto_case: fields = f"{InternalNamespace.NO_AUTO_CASE.value} {fields}" def wrapper(field_or_type): if is_non_field(field_or_type): return decorator( field=None, fields=fields, resolvable=resolvable, )(field_or_type) raise TypeError( "\n".join( [ f"\nInvalid Usage of {directive}.", "Must be applied on a class of ObjectType|InterfaceType", "Example:", f"{directive}", "class Product(graphene.ObjectType)", "\t...", ] ) ) return wrapper graphene-federation-3.2.0/graphene_federation/directives/override.py000066400000000000000000000027641463304117000257560ustar00rootroot00000000000000from typing import Callable from graphene_directives import directive_decorator from graphene_federation.apollo_versions import ( FederationVersion, LATEST_VERSION, get_directive_from_name, ) from .utils import is_non_field def override( graphene_type, from_: str, label: str = None, *, federation_version: FederationVersion = LATEST_VERSION, ) -> Callable: """ Indicates that an object field is now resolved by this subgraph instead of another subgraph where it's also defined. This enables you to migrate a field from one subgraph to another. Reference: https://www.apollographql.com/docs/federation/federated-types/federated-directives/#override """ directive = get_directive_from_name( "override", federation_version=federation_version ) decorator = directive_decorator(directive) def wrapper(field_or_type): if is_non_field(field_or_type): raise TypeError( "\n".join( [ f"\nInvalid Usage of {directive}.", "Must be applied on a field level", "Example:", "class Product(graphene.ObjectType)", '\tname = override(graphene.Int(),from="Products")', ] ) ) return decorator(field=field_or_type, **{"from": from_, "label": label}) if graphene_type: return wrapper(graphene_type) return wrapper graphene-federation-3.2.0/graphene_federation/directives/policy.py000066400000000000000000000021111463304117000254200ustar00rootroot00000000000000from typing import Callable from graphene_directives import directive_decorator from graphene_federation.apollo_versions import ( FederationVersion, LATEST_VERSION, get_directive_from_name, ) from .utils import is_non_field def policy( graphene_type=None, *, policies: list[list[str]], federation_version: FederationVersion = LATEST_VERSION, ) -> Callable: """ Indicates to composition that the target element is restricted based on authorization policies that are evaluated in a Rhai script or coprocessor. Reference: https://www.apollographql.com/docs/federation/federated-types/federated-directives/#policy """ directive = get_directive_from_name("policy", federation_version=federation_version) decorator = directive_decorator(directive) def wrapper(field_or_type): if is_non_field(field_or_type): return decorator(field=None, policies=policies)(field_or_type) return decorator(field=field_or_type, policies=policies) if graphene_type: return wrapper(graphene_type) return wrapper graphene-federation-3.2.0/graphene_federation/directives/provides.py000066400000000000000000000037401463304117000257650ustar00rootroot00000000000000from typing import Callable from typing import Union from graphene_directives import directive_decorator from graphene_federation.apollo_versions import ( FederationVersion, LATEST_VERSION, get_directive_from_name, ) from graphene_federation.validators import InternalNamespace, ast_to_str, build_ast from .utils import is_non_field def provides( graphene_type, fields: Union[str, list[str]], *, auto_case: bool = True, federation_version: FederationVersion = LATEST_VERSION, ) -> Callable: """ Specifies a set of entity fields that a subgraph can resolve, but only at a particular schema path (at other paths, the subgraph can't resolve those fields). If a subgraph can always resolve a particular entity field, do not apply this directive. Reference: https://www.apollographql.com/docs/federation/federated-types/federated-directives/#provides """ directive = get_directive_from_name( "provides", federation_version=federation_version ) decorator = directive_decorator(directive) fields = ast_to_str( build_ast( fields=fields if isinstance(fields, str) else " ".join(fields), directive_name=str(directive), ) ) if not auto_case: fields = f"{InternalNamespace.NO_AUTO_CASE.value} {fields}" def wrapper(field_or_type): if is_non_field(field_or_type): raise TypeError( "\n".join( [ f"\nInvalid Usage of {directive}.", "Must be applied on a field level", "Example:", "class Product(graphene.ObjectType)", '\torders = provides(graphene.List(Order),fields="id")', ] ) ) return decorator( field=field_or_type, fields=fields, ) if graphene_type: return wrapper(graphene_type) return wrapper graphene-federation-3.2.0/graphene_federation/directives/requires.py000066400000000000000000000043751463304117000257760ustar00rootroot00000000000000from typing import Callable from typing import Union from graphene_directives import directive_decorator from graphene_federation.apollo_versions import ( FederationVersion, LATEST_VERSION, get_directive_from_name, ) from graphene_federation.validators import InternalNamespace, ast_to_str, build_ast from .utils import is_non_field def requires( graphene_type, fields: Union[str, list[str]], *, auto_case: bool = True, federation_version: FederationVersion = LATEST_VERSION, ) -> Callable: """ Indicates that the resolver for a particular entity field depends on the values of other entity fields that are resolved by other subgraphs. This tells the router that it needs to fetch the values of those externally defined fields first, even if the original client query didn't request them. Reference: https://www.apollographql.com/docs/federation/federated-types/federated-directives/#requires """ directive = get_directive_from_name("requires", federation_version) decorator = directive_decorator(directive) fields = ast_to_str( build_ast( fields=fields if isinstance(fields, str) else " ".join(fields), directive_name=str(directive), ), add_type_name=True, # When resolvers receive the data, it will be type-casted as __typename info is added ) if not auto_case: fields = f"{InternalNamespace.NO_AUTO_CASE.value} {fields}" def wrapper(field_or_type): if is_non_field(field_or_type): raise TypeError( "\n".join( [ f"\nInvalid Usage of {directive}.", "Must be applied on a field level", "Example:", "class Product(graphene.ObjectType)", "\tid = graphene.ID()", "\torders = graphene.List(Order)" '\torder_count = requires(graphene.Int(),fields="id orders { id }")', ] ) ) return decorator( field=field_or_type, fields=fields, auto_case=auto_case, ) if graphene_type: return wrapper(graphene_type) return wrapper graphene-federation-3.2.0/graphene_federation/directives/requires_scopes.py000066400000000000000000000021351463304117000273420ustar00rootroot00000000000000from typing import Callable from graphene_directives import directive_decorator from graphene_federation.apollo_versions import ( FederationVersion, LATEST_VERSION, get_directive_from_name, ) from .utils import is_non_field def requires_scope( graphene_type=None, *, scopes: list[list[str]], federation_version: FederationVersion = LATEST_VERSION, ) -> Callable: """ Indicates to composition that the target element is accessible only to the authenticated supergraph users with the appropriate JWT scopes. Reference: https://www.apollographql.com/docs/federation/federated-types/federated-directives/#requiresscopes """ directive = get_directive_from_name( "requiresScopes", federation_version=federation_version ) decorator = directive_decorator(directive) def wrapper(field_or_type): if is_non_field(field_or_type): return decorator(field=None, scopes=scopes)(field_or_type) return decorator(field=field_or_type, scopes=scopes) if graphene_type: return wrapper(graphene_type) return wrapper graphene-federation-3.2.0/graphene_federation/directives/shareable.py000066400000000000000000000022231463304117000260530ustar00rootroot00000000000000from typing import Callable from graphene_directives import directive_decorator from graphene_federation.apollo_versions import ( FederationVersion, LATEST_VERSION, get_directive_from_name, ) from .utils import is_non_field def shareable( graphene_type=None, *, federation_version: FederationVersion = LATEST_VERSION, ) -> Callable: """ Indicates that an object type's field is allowed to be resolved by multiple subgraphs (by default in Federation 2, object fields can be resolved by only one subgraph). If applied to an object type definition, all of that type's fields are considered @shareable Reference: https://www.apollographql.com/docs/federation/federated-types/federated-directives/#shareable """ directive = get_directive_from_name( "shareable", federation_version=federation_version ) decorator = directive_decorator(directive) def wrapper(field_or_type): if is_non_field(field_or_type): return decorator(field=None)(field_or_type) return decorator(field=field_or_type) if graphene_type: return wrapper(graphene_type) return wrapper graphene-federation-3.2.0/graphene_federation/directives/tag.py000066400000000000000000000021251463304117000247010ustar00rootroot00000000000000from typing import Callable from graphene_directives import directive_decorator from graphene_federation.apollo_versions import ( FederationVersion, LATEST_VERSION, get_directive_from_name, ) from .utils import is_non_field def tag( graphene_type=None, *, name: str, federation_version: FederationVersion = LATEST_VERSION, ) -> Callable: """ Applies arbitrary string metadata to a schema location. Custom tooling can use this metadata during any step of the schema delivery flow, including composition, static analysis, and documentation Reference: https://www.apollographql.com/docs/federation/federated-types/federated-directives/#tag """ directive = get_directive_from_name("tag", federation_version=federation_version) decorator = directive_decorator(directive) def wrapper(field_or_type): if is_non_field(field_or_type): return decorator(field=None, name=name)(field_or_type) return decorator(field=field_or_type, name=name) if graphene_type: return wrapper(graphene_type) return wrapper graphene-federation-3.2.0/graphene_federation/directives/utils.py000066400000000000000000000002601463304117000252640ustar00rootroot00000000000000import inspect from typing import Any def is_non_field(graphene_type: Any): """Check of a given graphene_type is a non-field""" return inspect.isclass(graphene_type) graphene-federation-3.2.0/graphene_federation/entity.py000066400000000000000000000127711463304117000233110ustar00rootroot00000000000000from __future__ import annotations from typing import Any from typing import Dict, Type from graphene import Enum, Field, List, NonNull, ObjectType, Scalar, Union from graphene.types.schema import TypeMap from graphene_directives import Schema from graphene_directives.utils import has_non_field_attribute from .apollo_versions import LATEST_VERSION, get_directive_from_name from .scalars import _Any def get_entities(schema: Schema) -> Dict[str, Any]: """ Find all the entities from the type map. They can be easily distinguished from the other type as the `@key` and `@extend` decorators adds a `_sdl` attribute to them. """ type_map: TypeMap = schema.graphql_schema.type_map entities = {} key_directive = get_directive_from_name("key", LATEST_VERSION) extends_directive = get_directive_from_name("extends", LATEST_VERSION) for type_name, type_ in type_map.items(): if not hasattr(type_, "graphene_type"): continue graphene_type = type_.graphene_type is_entity = any( [ has_non_field_attribute(graphene_type, key_directive), has_non_field_attribute(graphene_type, extends_directive), ] ) if is_entity: entities[type_name] = graphene_type return entities def get_entity_cls(entities: Dict[str, Any]) -> Type[Union]: """ Create _Entity type which is a union of all the entity types. """ class _Entity(Union): class Meta: types = tuple(entities.values()) return _Entity def get_entity_query(schema: Schema): """ Create Entity query. """ entities_dict = get_entities(schema) if not entities_dict: return entity_type = get_entity_cls(entities_dict) class EntityQuery: entities = List( entity_type, name="_entities", representations=NonNull(List(NonNull(_Any))), required=True, ) def resolve_entities(self, info, representations, sub_field_resolution=False): entities = [] for representation in representations: type_ = schema.graphql_schema.get_type(representation["__typename"]) model = type_.graphene_type model_arguments = representation.copy() model_arguments.pop("__typename") if schema.auto_camelcase: get_model_attr = schema.field_name_to_type_attribute(model) model_arguments = { get_model_attr(k): v for k, v in model_arguments.items() } # convert subfields of models from dict to a corresponding graphql type, # This will be useful when @requires is used for model_field, value in model_arguments.items(): if not hasattr(model, model_field): continue field = getattr(model, model_field) if isinstance(field, Field) and isinstance(value, dict): if value.get("__typename") is None: value["__typename"] = field.type.of_type._meta.name # noqa model_arguments[model_field] = EntityQuery.resolve_entities( self, info, representations=[value], sub_field_resolution=True, ).pop() elif all( [ isinstance(field, List), isinstance(value, list), any( [ ( hasattr(field, "of_type") and issubclass(field.of_type, ObjectType) ), ( hasattr(field, "of_type") and issubclass(field.of_type, Union) ), ] ), ] ): for sub_value in value: if sub_value.get("__typename") is None: sub_value[ "__typename" ] = field.of_type._meta.name # noqa model_arguments[model_field] = EntityQuery.resolve_entities( self, info, representations=value, sub_field_resolution=True ) elif isinstance(field, Scalar) and getattr( field, "parse_value", None ): model_arguments[model_field] = field.parse_value(value) elif isinstance(field, Enum): model_arguments[model_field] = field._meta.enum[value] # noqa model_instance = model(**model_arguments) resolver = getattr( model, "_%s__resolve_reference" % model.__name__, None ) or getattr(model, "_resolve_reference", None) if resolver and not sub_field_resolution: model_instance = resolver(model_instance, info) entities.append(model_instance) return entities return EntityQuery graphene-federation-3.2.0/graphene_federation/scalars/000077500000000000000000000000001463304117000230435ustar00rootroot00000000000000graphene-federation-3.2.0/graphene_federation/scalars/__init__.py000066400000000000000000000004101463304117000251470ustar00rootroot00000000000000from ._any import _Any from .federation_policy import FederationPolicy from .federation_scope import FederationScope from .field_set_v1 import _FieldSet from .field_set_v2 import FieldSet from .link_import import link_import from .link_purpose import link_purpose graphene-federation-3.2.0/graphene_federation/scalars/_any.py000066400000000000000000000007431463304117000243470ustar00rootroot00000000000000from graphene import Scalar, String # Reference: https://www.apollographql.com/docs/federation/subgraph-spec/ class _Any(Scalar): name = "_Any" __typename = String(required=True) description = "A JSON serialized used for entity representations" specified_by_url = None @staticmethod def serialize(dt): return dt @staticmethod def parse_literal(node): return node @staticmethod def parse_value(value): return value graphene-federation-3.2.0/graphene_federation/scalars/federation_policy.py000066400000000000000000000030671463304117000271220ustar00rootroot00000000000000from typing import Any from graphql import ( GraphQLError, GraphQLScalarType, StringValueNode, ValueNode, print_ast, ) from graphql.pyutils import inspect def _serialize_string(output_value: Any) -> str: if isinstance(output_value, str): return output_value # do not serialize builtin types as strings, but allow serialization of custom # types via their `__str__` method if type(output_value).__module__ == "builtins": raise GraphQLError( "federation__Policy cannot represent value: " + inspect(output_value) ) return str(output_value) def _coerce_string(input_value: Any) -> str: if not isinstance(input_value, str): raise GraphQLError( "federation__Policy cannot represent a non string value: " + inspect(input_value) ) return input_value def _parse_string_literal(value_node: ValueNode, _variables: Any = None) -> str: """Parse a string value node in the AST.""" if not isinstance(value_node, StringValueNode): raise GraphQLError( "federation__Policy cannot represent a non string value: " + print_ast(value_node), value_node, ) return value_node.value # Reference: https://www.apollographql.com/docs/federation/subgraph-spec/ FederationPolicy = GraphQLScalarType( name="federation__Policy", description="This string-serialized scalar represents an authorization policy.", serialize=_serialize_string, parse_value=_coerce_string, parse_literal=_parse_string_literal, ) graphene-federation-3.2.0/graphene_federation/scalars/federation_scope.py000066400000000000000000000027231463304117000267320ustar00rootroot00000000000000from typing import Any from graphql import ( GraphQLError, GraphQLScalarType, StringValueNode, ValueNode, print_ast, ) from graphql.pyutils import inspect def _serialize_string(output_value: Any) -> str: if isinstance(output_value, str): return output_value # do not serialize builtin types as strings, but allow serialization of custom # types via their `__str__` method if type(output_value).__module__ == "builtins": raise GraphQLError("Scope cannot represent value: " + inspect(output_value)) return str(output_value) def _coerce_string(input_value: Any) -> str: if not isinstance(input_value, str): raise GraphQLError( "Scope cannot represent a non string value: " + inspect(input_value) ) return input_value def _parse_string_literal(value_node: ValueNode, _variables: Any = None) -> str: """Parse a string value node in the AST.""" if not isinstance(value_node, StringValueNode): raise GraphQLError( "Scope cannot represent a non string value: " + print_ast(value_node), value_node, ) return value_node.value # Reference: https://www.apollographql.com/docs/federation/subgraph-spec/ FederationScope = GraphQLScalarType( name="federation__Scope", description="This string-serialized scalar represents a JWT scope", serialize=_serialize_string, parse_value=_coerce_string, parse_literal=_parse_string_literal, ) graphene-federation-3.2.0/graphene_federation/scalars/field_set_v1.py000066400000000000000000000005771463304117000257720ustar00rootroot00000000000000from graphql import GraphQLScalarType # Reference: https://www.apollographql.com/docs/federation/subgraph-spec/ _FieldSet = GraphQLScalarType( name="_FieldSet", description=" ".join( ( "A string-serialized scalar represents a set of fields that's passed to a federated directive,", "such as @key, @requires, or @provides", ) ), ) graphene-federation-3.2.0/graphene_federation/scalars/field_set_v2.py000066400000000000000000000005751463304117000257710ustar00rootroot00000000000000from graphql import GraphQLScalarType # Reference: https://www.apollographql.com/docs/federation/subgraph-spec/ FieldSet = GraphQLScalarType( name="FieldSet", description=" ".join( ( "A string-serialized scalar represents a set of fields that's passed to a federated directive,", "such as @key, @requires, or @provides", ) ), ) graphene-federation-3.2.0/graphene_federation/scalars/link_import.py000066400000000000000000000035451463304117000257530ustar00rootroot00000000000000from typing import Any from graphql import ( GraphQLError, GraphQLScalarType, StringValueNode, ValueNode, print_ast, ) from graphql.pyutils import inspect from math import isfinite def _serialize_string(output_value: Any) -> str: if isinstance(output_value, str): return output_value if isinstance(output_value, bool): return "true" if output_value else "false" if isinstance(output_value, int) or ( isinstance(output_value, float) and isfinite(output_value) ): return str(output_value) # do not serialize builtin types as strings, but allow serialization of custom # types via their `__str__` method if type(output_value).__module__ == "builtins": raise GraphQLError( "link__Import cannot represent value: " + inspect(output_value) ) return str(output_value) def _coerce_string(input_value: Any) -> str: if not isinstance(input_value, str): raise GraphQLError( "link__Import cannot represent a non string value: " + inspect(input_value) ) return input_value def _parse_string_literal(value_node: ValueNode, _variables: Any = None) -> str: """Parse a string value node in the AST.""" if not isinstance(value_node, StringValueNode): raise GraphQLError( "link__Import cannot represent a non string value: " + print_ast(value_node), value_node, ) return value_node.value link_import = GraphQLScalarType( name="link__Import", description=" ".join( ( "A string serialized scalar specify which directives from an external federation specification", "should be imported into the current schema when using @link", ) ), serialize=_serialize_string, parse_value=_coerce_string, parse_literal=_parse_string_literal, ) graphene-federation-3.2.0/graphene_federation/scalars/link_purpose.py000066400000000000000000000012441463304117000261300ustar00rootroot00000000000000from graphql import GraphQLEnumType, GraphQLEnumValue # Reference: https://www.apollographql.com/docs/federation/subgraph-spec/ link_purpose = GraphQLEnumType( name="link__Purpose", description="An Enum to clarify the type of directives (security, execution) in the specification", values={ "SECURITY": GraphQLEnumValue( value="SECURITY", description="`SECURITY` features provide metadata necessary to securely resolve fields.", ), "EXECUTION": GraphQLEnumValue( value="EXECUTION", description="`EXECUTION` features provide metadata necessary for operation execution.", ), }, ) graphene-federation-3.2.0/graphene_federation/schema.py000066400000000000000000000166411463304117000232350ustar00rootroot00000000000000from typing import Collection, Type, Union from typing import Optional from graphene import ObjectType, PageInfo from graphene_directives import ( SchemaDirective, build_schema as build_directive_schema, directive_decorator, ) from graphene_directives.schema import Schema from .apollo_versions import ( FederationVersion, STABLE_VERSION, get_directive_from_name, get_directives_based_on_version, ) from .apollo_versions.v2_1 import compose_directive as ComposeDirective from .composable_directive import ComposableDirective from .entity import get_entity_query from .schema_directives import compose_directive, link_directive from .service import get_service_query def _get_federation_query( schema: Schema, query_cls: Optional[ObjectType] = None ) -> Type[ObjectType]: """ Add Federation required _service and _entities to Query(ObjectType) """ type_name = "Query" bases = [get_service_query(schema)] entity_cls = get_entity_query(schema) if entity_cls: bases.append(entity_cls) if query_cls is not None: type_name = query_cls.__name__ bases.append(query_cls) federated_query_cls = type(type_name, tuple(bases), {}) return federated_query_cls # noqa def _add_sharable_to_page_info_type( schema: Schema, federation_version: FederationVersion, types: list[Union[ObjectType, Type[ObjectType]]], ): """ Add @sharable directive to PageInfo type """ if page_info := schema.graphql_schema.type_map.get(PageInfo.__name__): try: # PageInfo needs @sharable directive sharable = get_directive_from_name("shareable", federation_version) types.append( directive_decorator(target_directive=sharable)(field=None)( page_info.graphene_type ) ) except ValueError: # Federation Version does not support @sharable pass def build_schema( query: Union[ObjectType, Type[ObjectType]] = None, mutation: Union[ObjectType, Type[ObjectType]] = None, subscription: Union[ObjectType, Type[ObjectType]] = None, types: Collection[Union[ObjectType, Type[ObjectType]]] = None, directives: Union[Collection[ComposableDirective], None] = None, include_graphql_spec_directives: bool = True, schema_directives: Collection[SchemaDirective] = None, auto_camelcase: bool = True, federation_version: FederationVersion = None, ) -> Schema: """ Build Schema. Args: query (Type[ObjectType]): Root query *ObjectType*. Describes entry point for fields to *read* data in your Schema. mutation (Optional[Type[ObjectType]]): Root mutation *ObjectType*. Describes entry point for fields to *create, update or delete* data in your API. subscription (Optional[Type[ObjectType]]): Root subscription *ObjectType*. Describes entry point for fields to receive continuous updates. types (Optional[Collection[Type[ObjectType]]]): List of any types to include in schema that may not be introspected through root types. directives (List[GraphQLDirective], optional): List of custom directives to include in the GraphQL schema. auto_camelcase (bool): Fieldnames will be transformed in Schema's TypeMap from snake_case to camelCase (preferred by GraphQL standard). Default True. schema_directives (Collection[SchemaDirective]): Directives that can be defined at DIRECTIVE_LOCATION.SCHEMA with their argument values. include_graphql_spec_directives (bool): Includes directives defined by GraphQL spec (@include, @skip, @deprecated, @specifiedBy) federation_version (FederationVersion): Specify the version explicit (default STABLE_VERSION) """ federation_version = federation_version if federation_version else STABLE_VERSION federation_2_enabled = ( federation_version.value > FederationVersion.VERSION_1_0.value ) _types = list(types) if types is not None else [] _directives = get_directives_based_on_version(federation_version) federation_directives = set(_directives.keys()) if directives is not None: # Add custom directives _directives.update({directive.name: directive for directive in directives}) schema_args = { "mutation": mutation, "subscription": subscription, "types": _types, "directives": _directives.values(), "auto_camelcase": auto_camelcase, "include_graphql_spec_directives": include_graphql_spec_directives, } schema: Schema = build_directive_schema(query=query, **schema_args) _add_sharable_to_page_info_type( schema=schema, federation_version=federation_version, types=_types ) _schema_directives = [] directives_used = schema.get_directives_used() if schema_directives or directives: if not federation_2_enabled: raise ValueError( f"Schema Directives & Directives are not supported on {federation_version=}. Use >=2.0 " ) # Check if @ComposeDirective needs to be added to schema if ( any( schema_directive.target_directive == ComposeDirective for schema_directive in schema_directives or [] ) or directives ): directives_used.append(ComposeDirective) if directives_used and federation_2_enabled: imports = [ str(directive) for directive in directives_used if directive.name in federation_directives ] if imports: _schema_directives.append( link_directive( url=f"https://specs.apollo.dev/federation/v{federation_version.value}", import_=sorted(imports), ) ) # Add @link directive for Custom Directives provided if directives: url__imports: dict[str, list[str]] = {} for directive in directives: assert isinstance( directive, ComposableDirective ), "directives must be of instance ComposableDirective" if not directive.add_to_schema_directives: continue if not directive.spec_url: continue _imports = url__imports.get(directive.spec_url) if _imports: _imports.append(str(directive)) else: url__imports[directive.spec_url] = [str(directive)] # Add @link schema directives for spec, imports in url__imports.items(): _schema_directives.append(link_directive(url=spec, import_=sorted(imports))) # Add @ComposeDirective to schema directives for directive in directives: if not directive.add_to_schema_directives: continue _schema_directives.append(compose_directive(name=str(directive))) if schema_directives: _schema_directives.extend(list(schema_directives)) schema_args["schema_directives"] = ( _schema_directives if federation_2_enabled else [] ) # Call it again to rebuild the schema using the schema directives schema = build_directive_schema(query=query, **schema_args) # Add Federation required _service and _entities to Query return build_directive_schema( query=_get_federation_query(schema, schema.query), **schema_args, ) graphene-federation-3.2.0/graphene_federation/schema_directives/000077500000000000000000000000001463304117000250745ustar00rootroot00000000000000graphene-federation-3.2.0/graphene_federation/schema_directives/__init__.py000066400000000000000000000001341463304117000272030ustar00rootroot00000000000000from .compose_directive import compose_directive from .link_directive import link_directive graphene-federation-3.2.0/graphene_federation/schema_directives/compose_directive.py000066400000000000000000000021421463304117000311500ustar00rootroot00000000000000from graphene_directives import SchemaDirective from graphene_federation.apollo_versions import ( FederationVersion, LATEST_VERSION, get_directive_from_name, ) def compose_directive( name: str, federation_version: FederationVersion = LATEST_VERSION, ) -> SchemaDirective: """ Indicates to composition that all uses of a particular custom type system directive in the subgraph schema should be preserved in the supergraph schema (by default, composition omits most directives from the supergraph schema). Use this in the `schema_directives` argument of `build_schema` It is not recommended to use this directive directly, instead use the ComposableDirective class to build a custom directive. It will automatically add the compose and link directive to schema Reference: https://www.apollographql.com/docs/federation/federated-types/federated-directives/#composedirective """ directive = get_directive_from_name("composeDirective", federation_version) return SchemaDirective( target_directive=directive, arguments={"name": name}, ) graphene-federation-3.2.0/graphene_federation/schema_directives/link_directive.py000066400000000000000000000032211463304117000304370ustar00rootroot00000000000000from typing import Optional from graphene_directives import CustomDirective, DirectiveLocation, SchemaDirective from graphql import ( GraphQLArgument, GraphQLList, GraphQLNonNull, GraphQLString, ) from graphene_federation.scalars import link_import, link_purpose _link_directive = CustomDirective( name="link", locations=[ DirectiveLocation.SCHEMA, ], args={ "url": GraphQLArgument(GraphQLNonNull(GraphQLString)), "as": GraphQLArgument(GraphQLString), "for": GraphQLArgument(link_purpose), "import": GraphQLArgument(GraphQLList(link_import)), }, description="Federation @link directive", add_definition_to_schema=False, is_repeatable=True, ) def link_directive( url: str, as_: Optional[str] = None, for_: Optional[str] = None, import_: Optional[list[str]] = None, ) -> SchemaDirective: """ It's used to link types and fields from external subgraphs, creating a unified GraphQL schema across multiple services Use this in the `schema_directives` argument of `build_schema` It is not recommended to use this directive directly, instead use the ComposableDirective class to build a custom directive. It will automatically add the compose and link directive to schema Also, the apollo directives such as @key, @external, ... are automatically added to the schema via the link directive Reference: https://www.apollographql.com/docs/federation/federated-types/federated-directives/ """ return SchemaDirective( target_directive=_link_directive, arguments={"url": url, "as": as_, "for": for_, "import": import_}, ) graphene-federation-3.2.0/graphene_federation/service.py000066400000000000000000000013731463304117000234310ustar00rootroot00000000000000from graphene import Field, ObjectType, String from graphene_directives.schema import Schema def get_sdl(schema) -> str: """ Add all needed decorators to the string representation of the schema. """ string_schema = str(schema) return string_schema.strip() def get_service_query(schema: Schema): """ Gets the Service Query for federation """ sdl_str = get_sdl(schema) class _Service(ObjectType): sdl = String() def resolve_sdl(self, _) -> str: # noqa return sdl_str class ServiceQuery(ObjectType): _service = Field(_Service, name="_service", required=True) def resolve__service(self, info) -> _Service: # noqa return _Service() return ServiceQuery graphene-federation-3.2.0/graphene_federation/transform/000077500000000000000000000000001463304117000234265ustar00rootroot00000000000000graphene-federation-3.2.0/graphene_federation/transform/__init__.py000066400000000000000000000000771463304117000255430ustar00rootroot00000000000000from .field_set_case_transform import field_set_case_transform graphene-federation-3.2.0/graphene_federation/transform/field_set_case_transform.py000066400000000000000000000014761463304117000310340ustar00rootroot00000000000000from graphene_directives import Schema from graphene_federation.validators import InternalNamespace, to_case def field_set_case_transform(inputs: dict, schema: Schema) -> dict: """ Transform the fields from internal representation to schema representation Internal representation uses __union__ for representing ... on __arg__ for representing (arg1: value1, arg2: value2) """ fields = inputs.get("fields") auto_case = InternalNamespace.NO_AUTO_CASE.value not in inputs.get("fields", ()) if fields: inputs["fields"] = ( to_case(fields, schema, auto_case) .replace(InternalNamespace.UNION.value, "... on") .replace(InternalNamespace.ARG.value, "") .replace(InternalNamespace.NO_AUTO_CASE.value, "") ) return inputs graphene-federation-3.2.0/graphene_federation/validators/000077500000000000000000000000001463304117000235635ustar00rootroot00000000000000graphene-federation-3.2.0/graphene_federation/validators/__init__.py000066400000000000000000000003051463304117000256720ustar00rootroot00000000000000from .key import validate_key from .provides import validate_provides from .requires import validate_requires from .utils import InternalNamespace from .utils import ast_to_str, build_ast, to_case graphene-federation-3.2.0/graphene_federation/validators/key.py000066400000000000000000000016501463304117000247270ustar00rootroot00000000000000from typing import Union from graphene import Field, Interface, ObjectType from graphene_directives import Schema from .utils import InternalNamespace, build_ast, evaluate_ast, to_case def validate_key( graphene_type: Union[ObjectType, Interface, Field], inputs: dict, schema: Schema ) -> bool: """ Used to validate the inputs and graphene_type of @key """ errors: list[str] = [] auto_case = InternalNamespace.NO_AUTO_CASE.value not in inputs.get("fields", ()) ast_node = build_ast( fields=to_case(inputs.get("fields"), schema, auto_case), directive_name="@key" ) evaluate_ast( directive_name="@key", ast=ast_node, graphene_type=graphene_type, ignore_fields=[InternalNamespace.NO_AUTO_CASE.value], errors=errors, entity_types=schema.graphql_schema.type_map, ) if errors: raise ValueError("\n".join(errors)) return True graphene-federation-3.2.0/graphene_federation/validators/provides.py000066400000000000000000000030721463304117000257720ustar00rootroot00000000000000from typing import Union from graphene import Field, Interface, ObjectType from graphene_directives import Schema from .utils import ( InternalNamespace, build_ast, evaluate_ast, to_case, ) def validate_provides( _parent_type: Union[ObjectType, Interface], field: Field, inputs: dict, schema: Schema, ) -> bool: """ Used to validate the inputs and graphene_type of @provides """ errors: list[str] = [] auto_case = InternalNamespace.NO_AUTO_CASE.value not in inputs.get("fields", ()) ast_node = build_ast( fields=to_case(inputs.get("fields"), schema, auto_case), directive_name="@provides", ) # Get the parent type of the field field_parent_type = field while hasattr(field_parent_type, "type") or hasattr(field_parent_type, "of_type"): if hasattr(field_parent_type, "of_type"): field_parent_type = field_parent_type.of_type elif hasattr(field_parent_type, "type"): field_parent_type = field_parent_type.type else: raise ValueError( f"@provides could not find parent for the field {field} at {_parent_type}" ) evaluate_ast( directive_name="@provides", ast=ast_node, graphene_type=field_parent_type, ignore_fields=[ InternalNamespace.UNION.value, InternalNamespace.NO_AUTO_CASE.value, ], errors=errors, entity_types=schema.graphql_schema.type_map, ) if errors: raise ValueError("\n".join(errors)) return True graphene-federation-3.2.0/graphene_federation/validators/requires.py000066400000000000000000000020621463304117000257740ustar00rootroot00000000000000from typing import Union from graphene import Field, Interface, ObjectType from graphene_directives import Schema from .utils import InternalNamespace, build_ast, evaluate_ast, to_case def validate_requires( parent_type: Union[ObjectType, Interface], _field: Field, inputs: dict, schema: Schema, ) -> bool: """ Used to validate the inputs and graphene_type of @requires """ errors: list[str] = [] auto_case = InternalNamespace.NO_AUTO_CASE.value not in inputs.get("fields", ()) ast_node = build_ast( fields=to_case(inputs.get("fields"), schema, auto_case), directive_name="@requires", ) evaluate_ast( directive_name="@requires", ast=ast_node, graphene_type=parent_type, ignore_fields=[ "__typename", InternalNamespace.NO_AUTO_CASE.value, InternalNamespace.UNION.value, ], errors=errors, entity_types=schema.graphql_schema.type_map, ) if errors: raise ValueError("\n".join(errors)) return True graphene-federation-3.2.0/graphene_federation/validators/utils.py000066400000000000000000000265141463304117000253050ustar00rootroot00000000000000from enum import Enum from typing import Union from graphene import Field, Interface, NonNull, ObjectType from graphene.types.definitions import ( GrapheneEnumType, GrapheneInterfaceType, GrapheneObjectType, GrapheneScalarType, GrapheneUnionType, ) from graphene.utils.str_converters import to_camel_case from graphene_directives import Schema from graphql import ( GraphQLField, GraphQLList, GraphQLNonNull, GraphQLScalarType, GraphQLType, ) """ @requires, @key, @provides 's field is represented internally in a different way A field definition "id currency(curreny_value: usd) products{ ... on Bag { id } ... on Cloth { id } }" is internally represented as "id currency __arg__(curreny_value: usd) products{ __union__ Bag { id } __union__ Cloth { id } }" """ class InternalNamespace(Enum): UNION = "__union__" ARG = "__arg__" NO_AUTO_CASE = "__no_auto_case__" def check_fields_exist_on_type( field: str, graphene_type: Union[ObjectType, Interface, Field, NonNull], ignore_fields: list[str], entity_types: dict[str, ObjectType], ) -> Union[GraphQLType, GraphQLField, bool]: """ Checks if the given field exists on the graphene_type :param field: field that needs to be checked for existence :param graphene_type: Union[ObjectType, Interface, Field, NonNull] :param ignore_fields: fields that can be ignored for checking example __typename :param entity_types: A dictionary of [entity_name, graphene_type] """ if field in ignore_fields or field.startswith( "__arg__" # todo handle argument type validations ): return True if isinstance(graphene_type, GraphQLField): return check_fields_exist_on_type( field, graphene_type.type, # noqa ignore_fields, entity_types, ) if isinstance(graphene_type, GraphQLNonNull): return check_fields_exist_on_type( field, graphene_type.of_type, ignore_fields, entity_types ) if isinstance(graphene_type, GrapheneObjectType): if field in graphene_type.fields: return graphene_type.fields[field] if isinstance(graphene_type, GraphQLList): return check_fields_exist_on_type( field, graphene_type.of_type, ignore_fields, entity_types ) if isinstance(graphene_type, GrapheneUnionType): for union_type in graphene_type.types: if union_type.name.lower() == field.lower(): return union_type try: if issubclass( graphene_type, # noqa ObjectType, ) or issubclass( graphene_type, # noqa Interface, ): entity_fields = entity_types.get(graphene_type._meta.name) # noqa if entity_fields is not None: entity_fields = entity_fields.fields # noqa if field in entity_fields: return entity_fields[field] except TypeError: return False return False def get_type_for_field( graphene_field, ) -> tuple[ Union[ GrapheneObjectType, GrapheneInterfaceType, GrapheneUnionType, GrapheneScalarType, GrapheneEnumType, ], bool, ]: """ Finds the base type for a given graphene_field Returns the graphene_field_type, is_selectable (indicates whether the type has sub selections) """ if isinstance(graphene_field, GraphQLField): return get_type_for_field(graphene_field.type) if isinstance(graphene_field, GraphQLNonNull): return get_type_for_field(graphene_field.of_type) if isinstance(graphene_field, GraphQLList): return get_type_for_field(graphene_field.of_type) if ( isinstance(graphene_field, GrapheneObjectType) or isinstance(graphene_field, GrapheneInterfaceType) or isinstance(graphene_field, GrapheneUnionType) ): return graphene_field, True if isinstance(graphene_field, GraphQLScalarType) or isinstance( graphene_field, GrapheneEnumType ): return graphene_field, False raise NotImplementedError("get_type_for_field", graphene_field) """" AST FUNCTIONS For @key, @provides, @requires FieldSet Parsing """ def _tokenize_field_set(fields: str, directive_name: str) -> list[str]: """ Splits the fields string to tokens """ fields = fields.strip() tokens = [] current_token = "" open_braces_count = 0 if fields.startswith("{"): raise ValueError(f"{directive_name} cannot start with " + "{") index = 0 while index < len(fields): char = fields[index] if char.isalnum(): current_token += char elif char == "{": if current_token: tokens.append(current_token) tokens.append(char) current_token = "" open_braces_count += 1 elif char == "}": if current_token: tokens.append(current_token) tokens.append(char) current_token = "" open_braces_count -= 1 elif char == ",": if current_token: tokens.append(current_token) current_token = "" elif char == "_": current_token += char elif char == "(": tokens.append(current_token) current_token = f"{char}" index += 1 mismatched_parenthesis = True while index < len(fields): char = fields[index] if char.isalnum() or char == ",": current_token += char elif char.isspace(): index += 1 continue elif char == ":": current_token += ": " elif char == ")": current_token += char mismatched_parenthesis = False tokens.append( ", ".join(current_token.split(",")).replace("(", "__arg__(") ) current_token = "" break else: ValueError( f"{directive_name}({fields}) has unknown character {char} at argument {current_token}" ) index += 1 if mismatched_parenthesis: raise ValueError( f"{directive_name}({fields}) has mismatched parenthesis" ) elif char == ")": raise ValueError(f"{directive_name}({fields}) has mismatched parenthesis") elif char.isspace(): if current_token == "on": tokens.append("__union__") elif current_token: tokens.append(current_token) current_token = "" else: if current_token: tokens.append(current_token) current_token = "" index += 1 if current_token: tokens.append(current_token) if open_braces_count != 0: raise ValueError(f"{directive_name}({fields}) has mismatched brackets") return tokens def evaluate_ast( directive_name: str, ast: dict, graphene_type: ObjectType, ignore_fields: list[str], errors: list[str], entity_types: dict[str, ObjectType], ) -> None: """ Checks if the given AST is valid for the graphene_type It recursively checks if the fields at a node exist on the graphene_type """ for field_name, value in ast.items(): field_type = check_fields_exist_on_type( field_name, graphene_type, ignore_fields, entity_types, ) has_selections = len(value) != 0 if not field_type: errors.append( f'{directive_name}, field "{field_name}" does not exist on type "{graphene_type}"' ) continue field_type, is_selectable = ( get_type_for_field(field_type) if not isinstance(field_type, bool) else ( field_type, False, ) ) if is_selectable and not has_selections: errors.append( f'{directive_name}, type {graphene_type}, field "{field_name}" needs sub selections.' ) continue if not is_selectable and has_selections: errors.append( f'{directive_name}, type {graphene_type}, field "{field_name}" cannot have sub selections.' ) continue if len(value) != 0: evaluate_ast( directive_name, value, field_type, ignore_fields, errors, entity_types, ) def build_ast(fields: str, directive_name: str) -> dict: """ Converts the fields string to an AST tree :param fields: string fields :param directive_name: name of the directive """ cleaned_fields = _tokenize_field_set(fields, directive_name) parent: dict[str, dict] = {} field_stack: list[str] = [] field_level = [parent] for index, field in enumerate(cleaned_fields): if field == "{": field_level.append(field_level[-1][field_stack[-1]]) elif field == "}": field_level.pop() else: field_stack.append(field) field_level[-1][field] = {} return parent def ast_to_str(fields: dict, add_type_name: bool = False, level: int = 0) -> str: """ Converts the AST of fields to the original string :param fields: AST of fields :param add_type_name: adds __typename to sub ast nodes (for @requires) :param level: for internal use only """ new_fields = [] union_type = False if level != 0 and add_type_name: new_fields.append("__typename") for field, value in fields.items(): if "typename" in field.lower(): continue if "__union__" in field.lower(): union_type = True elif len(value) == 0: new_fields.append(field) else: inner_fields = [ field, "{", ast_to_str(value, add_type_name, level + 1), "}", ] if union_type: inner_fields.insert(0, "... on") new_fields.extend(inner_fields) return " ".join(new_fields) """" String Helpers For Schema Field Casing Parsing """ def to_case(fields: Union[str, None], schema: Schema, auto_case: bool = True) -> str: """ Converts field str to correct casing according to the schema.auto_camelcase value """ if not fields: return "" skip_next = False if schema.auto_camelcase and auto_case: data_fields = [] for field in fields.split(): if field == InternalNamespace.UNION.value: data_fields.append(field) skip_next = True elif field == "__typename": data_fields.append(field) elif field.startswith(InternalNamespace.ARG.value): data_fields.append(field) else: if skip_next: data_fields.append(field) skip_next = False else: data_fields.append(to_camel_case(field)) return " ".join(data_fields) return fields graphene-federation-3.2.0/integration_tests/000077500000000000000000000000001463304117000211675ustar00rootroot00000000000000graphene-federation-3.2.0/integration_tests/README.md000066400000000000000000000011501463304117000224430ustar00rootroot00000000000000# Integration Test This is an integration test that have for purpose to check that the library creates schemas that are compatible between each other and can be federated in a single gateway schema. It first try to bring up the four separate services (a, b, c, d) and check that they are brought up successfully by polling the `/graphql` url to retrieve the schema. Once those are up and running the federation service is brought up and we check that it manages to aggregates all schemas into one. Finally the test service is brought up and does a few requests to the few services to check they act as intended. graphene-federation-3.2.0/integration_tests/docker-compose.yml000066400000000000000000000047361463304117000246360ustar00rootroot00000000000000version: '2.1' services: service_a: build: service_a/. container_name: federation_service_a volumes: - ./service_a/src:/project/src - ../:/project/federation_deps command: sh -c "pip install -e ./federation_deps && python ./src/app.py" healthcheck: test: curl http://0.0.0.0:5000/graphql -H "Content-Type:application/json" --data '{"query":"{__schema { types {name} }}"}' interval: 1s timeout: 10s retries: 20 service_b: build: service_b/. container_name: federation_service_b volumes: - ./service_b/src:/project/src - ../:/project/federation_deps command: sh -c "pip install -e ./federation_deps && python ./src/app.py" healthcheck: test: curl http://0.0.0.0:5000/graphql -H "Content-Type:application/json" --data '{"query":"{__schema { types {name} }}"}' interval: 1s timeout: 10s retries: 20 service_c: build: service_c/. container_name: federation_service_c volumes: - ./service_c/src:/project/src - ../:/project/federation_deps command: sh -c "pip install -e ./federation_deps && python ./src/app.py" healthcheck: test: curl http://0.0.0.0:5000/graphql -H "Content-Type:application/json" --data '{"query":"{__schema { types {name} }}"}' interval: 1s timeout: 10s retries: 20 service_d: build: service_d/. container_name: federation_service_d volumes: - ./service_d/src:/project/src - ../:/project/federation_deps command: sh -c "pip install -e ./federation_deps && python ./src/app.py" healthcheck: test: curl http://0.0.0.0:5000/graphql -H "Content-Type:application/json" --data '{"query":"{__schema { types {name} }}"}' interval: 1s timeout: 10s retries: 20 gateway: build: gateway/. container_name: federation_gateway volumes: - ./gateway/src:/project/src ports: - 3000:3000 healthcheck: test: curl -f http://localhost:3000/.well-known/apollo/server-health interval: 3s timeout: 10s retries: 5 depends_on: service_a: condition: service_healthy service_b: condition: service_healthy service_c: condition: service_healthy service_d: condition: service_healthy proxy_dep: image: busybox depends_on: gateway: condition: service_healthy tests: build: tests/. volumes: - ./tests:/project/tests depends_on: - proxy_dep graphene-federation-3.2.0/integration_tests/gateway/000077500000000000000000000000001463304117000226305ustar00rootroot00000000000000graphene-federation-3.2.0/integration_tests/gateway/Dockerfile000066400000000000000000000003061463304117000246210ustar00rootroot00000000000000FROM node:18-alpine WORKDIR /project COPY package.json package-lock.json ./ ENV NPM_CONFIG_LOGLEVEL warn RUN npm install -g npm@9.6.1 RUN npm clean-install RUN apk add curl CMD [ "npm", "start"] graphene-federation-3.2.0/integration_tests/gateway/package-lock.json000066400000000000000000004201151463304117000260470ustar00rootroot00000000000000{ "name": "federation", "version": "1.0.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "federation", "version": "1.0.0", "license": "ISC", "dependencies": { "@apollo/gateway": "^2.3.4", "apollo-server": "^3.12.0", "graphql": "^16.6.0" }, "devDependencies": { "nodemon": "^2.0.21", "ts-node": "^10.9.1", "typescript": "^4.9.5" } }, "node_modules/@apollo/composition": { "version": "2.3.4", "resolved": "https://registry.npmjs.org/@apollo/composition/-/composition-2.3.4.tgz", "integrity": "sha512-anee8tTrBrQY2uZySBdff8WWLtexoMC60NlgNt5/0JDitTetuDr5JhvP5JgHairoNsmcBM4I6FFQYxd/pJD7dg==", "dependencies": { "@apollo/federation-internals": "2.3.4", "@apollo/query-graphs": "2.3.4" }, "engines": { "node": ">=14.15.0" }, "peerDependencies": { "graphql": "^16.5.0" } }, "node_modules/@apollo/federation-internals": { "version": "2.3.4", "resolved": "https://registry.npmjs.org/@apollo/federation-internals/-/federation-internals-2.3.4.tgz", "integrity": "sha512-xeBv6ASrOfYuOk/mbpWBThq1oIK95tjPVYgTNt2Axg0HB77o4uqhUVE+saNPvlVdqmfm4E6GafDf1BvvhoAOcA==", "dependencies": { "chalk": "^4.1.0", "js-levenshtein": "^1.1.6" }, "engines": { "node": ">=14.15.0" }, "peerDependencies": { "graphql": "^16.5.0" } }, "node_modules/@apollo/gateway": { "version": "2.3.4", "resolved": "https://registry.npmjs.org/@apollo/gateway/-/gateway-2.3.4.tgz", "integrity": "sha512-UBB91SZuR5t7b3zFoyb3yiyJeGQ+7YLaJGRoYLWrQsAXchmwEn9AXY+emjDqm0tMNlOT0RRWOQEhB62zodRNDg==", "dependencies": { "@apollo/composition": "2.3.4", "@apollo/federation-internals": "2.3.4", "@apollo/query-planner": "2.3.4", "@apollo/server-gateway-interface": "^1.1.0", "@apollo/usage-reporting-protobuf": "^4.0.0", "@apollo/utils.createhash": "^2.0.0", "@apollo/utils.fetcher": "^2.0.0", "@apollo/utils.isnodelike": "^2.0.0", "@apollo/utils.logger": "^2.0.0", "@josephg/resolvable": "^1.0.1", "@opentelemetry/api": "^1.0.1", "@types/node-fetch": "^2.6.2", "async-retry": "^1.3.3", "loglevel": "^1.6.1", "lru-cache": "^7.13.1", "make-fetch-happen": "^11.0.0", "node-abort-controller": "^3.0.1", "node-fetch": "^2.6.7" }, "engines": { "node": ">=14.15.0" }, "peerDependencies": { "graphql": "^16.5.0" } }, "node_modules/@apollo/protobufjs": { "version": "1.2.7", "resolved": "https://registry.npmjs.org/@apollo/protobufjs/-/protobufjs-1.2.7.tgz", "integrity": "sha512-Lahx5zntHPZia35myYDBRuF58tlwPskwHc5CWBZC/4bMKB6siTBWwtMrkqXcsNwQiFSzSx5hKdRPUmemrEp3Gg==", "hasInstallScript": true, "dependencies": { "@protobufjs/aspromise": "^1.1.2", "@protobufjs/base64": "^1.1.2", "@protobufjs/codegen": "^2.0.4", "@protobufjs/eventemitter": "^1.1.0", "@protobufjs/fetch": "^1.1.0", "@protobufjs/float": "^1.0.2", "@protobufjs/inquire": "^1.1.0", "@protobufjs/path": "^1.1.2", "@protobufjs/pool": "^1.1.0", "@protobufjs/utf8": "^1.1.0", "@types/long": "^4.0.0", "long": "^4.0.0" }, "bin": { "apollo-pbjs": "bin/pbjs", "apollo-pbts": "bin/pbts" } }, "node_modules/@apollo/query-graphs": { "version": "2.3.4", "resolved": "https://registry.npmjs.org/@apollo/query-graphs/-/query-graphs-2.3.4.tgz", "integrity": "sha512-0i7u0wibidSp/EAGU9sHKG8NwY0lqKXQ+O+H4S5rLHAmE1bJlxsaRuZ004eRzTBIUdxR0PoMGqyEukwUrbPAmA==", "dependencies": { "@apollo/federation-internals": "2.3.4", "deep-equal": "^2.0.5", "ts-graphviz": "^1.5.4", "uuid": "^9.0.0" }, "engines": { "node": ">=14.15.0" }, "peerDependencies": { "graphql": "^16.5.0" } }, "node_modules/@apollo/query-planner": { "version": "2.3.4", "resolved": "https://registry.npmjs.org/@apollo/query-planner/-/query-planner-2.3.4.tgz", "integrity": "sha512-4YzXT3w0W2mGOH5xwd0ILYx2Hey/h87P6I4Bb3CM9BbLjPokxcmiIOuNx+R2fI/RyeX60x4EbnVxWFW56IWYoA==", "dependencies": { "@apollo/federation-internals": "2.3.4", "@apollo/query-graphs": "2.3.4", "chalk": "^4.1.0", "deep-equal": "^2.0.5", "pretty-format": "^29.0.0" }, "engines": { "node": ">=14.15.0" }, "peerDependencies": { "graphql": "^16.5.0" } }, "node_modules/@apollo/server-gateway-interface": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@apollo/server-gateway-interface/-/server-gateway-interface-1.1.0.tgz", "integrity": "sha512-0rhG++QtGfr4YhhIHgxZ9BdMFthaPY6LbhI9Au90osbfLMiZ7f8dmZsEX1mp7O1h8MJwCu6Dp0I/KcGbSvfUGA==", "dependencies": { "@apollo/usage-reporting-protobuf": "^4.0.0", "@apollo/utils.fetcher": "^2.0.0", "@apollo/utils.keyvaluecache": "^2.1.0", "@apollo/utils.logger": "^2.0.0" }, "peerDependencies": { "graphql": "14.x || 15.x || 16.x" } }, "node_modules/@apollo/usage-reporting-protobuf": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/@apollo/usage-reporting-protobuf/-/usage-reporting-protobuf-4.1.0.tgz", "integrity": "sha512-hXouMuw5pQVkzi8dgMybmr6Y11+eRmMQVoB5TF0HyTwAg9SOq/v3OCuiYqcVUKdBcskU9Msp+XvjAk0GKpWCwQ==", "dependencies": { "@apollo/protobufjs": "1.2.7" } }, "node_modules/@apollo/utils.createhash": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/@apollo/utils.createhash/-/utils.createhash-2.0.0.tgz", "integrity": "sha512-9GhGGD3J0HJF/VC+odwYpKi3Cg1NWrsO8GQvyGwDS5v/78I3154Hn8s4tpW+nqoaQ/lAvxdQQr3HM1b5HLM6Ww==", "dependencies": { "@apollo/utils.isnodelike": "^2.0.0", "sha.js": "^2.4.11" }, "engines": { "node": ">=14" } }, "node_modules/@apollo/utils.dropunuseddefinitions": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@apollo/utils.dropunuseddefinitions/-/utils.dropunuseddefinitions-1.1.0.tgz", "integrity": "sha512-jU1XjMr6ec9pPoL+BFWzEPW7VHHulVdGKMkPAMiCigpVIT11VmCbnij0bWob8uS3ODJ65tZLYKAh/55vLw2rbg==", "engines": { "node": ">=12.13.0" }, "peerDependencies": { "graphql": "14.x || 15.x || 16.x" } }, "node_modules/@apollo/utils.fetcher": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/@apollo/utils.fetcher/-/utils.fetcher-2.0.0.tgz", "integrity": "sha512-RC0twEwwBKbhk/y4B2X4YEciRG1xoKMgiPy5xQqNMd3pG78sR+ybctG/m7c/8+NaaQOS22UPUCBd6yS6WihBIg==", "engines": { "node": ">=14" } }, "node_modules/@apollo/utils.isnodelike": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/@apollo/utils.isnodelike/-/utils.isnodelike-2.0.0.tgz", "integrity": "sha512-77CiAM2qDXn0haQYrgX0UgrboQykb+bOHaz5p3KKItMwUZ/EFphzuB2vqHvubneIc9dxJcTx2L7MFDswRw/JAQ==", "engines": { "node": ">=14" } }, "node_modules/@apollo/utils.keyvaluecache": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/@apollo/utils.keyvaluecache/-/utils.keyvaluecache-2.1.0.tgz", "integrity": "sha512-WBNI4H1dGX2fHMk5j4cJo7mlXWn1X6DYCxQ50IvmI7Xv7Y4QKiA5EwbLOCITh9OIZQrVX7L0ASBSgTt6jYx/cg==", "dependencies": { "@apollo/utils.logger": "^2.0.0", "lru-cache": "^7.14.1" }, "engines": { "node": ">=14" } }, "node_modules/@apollo/utils.logger": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/@apollo/utils.logger/-/utils.logger-2.0.0.tgz", "integrity": "sha512-o8qYwgV2sYg+PcGKIfwAZaZsQOTEfV8q3mH7Pw8GB/I/Uh2L9iaHdpiKuR++j7oe1K87lFm0z/JAezMOR9CGhg==", "engines": { "node": ">=14" } }, "node_modules/@apollo/utils.printwithreducedwhitespace": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@apollo/utils.printwithreducedwhitespace/-/utils.printwithreducedwhitespace-1.1.0.tgz", "integrity": "sha512-GfFSkAv3n1toDZ4V6u2d7L4xMwLA+lv+6hqXicMN9KELSJ9yy9RzuEXaX73c/Ry+GzRsBy/fdSUGayGqdHfT2Q==", "engines": { "node": ">=12.13.0" }, "peerDependencies": { "graphql": "14.x || 15.x || 16.x" } }, "node_modules/@apollo/utils.removealiases": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/@apollo/utils.removealiases/-/utils.removealiases-1.0.0.tgz", "integrity": "sha512-6cM8sEOJW2LaGjL/0vHV0GtRaSekrPQR4DiywaApQlL9EdROASZU5PsQibe2MWeZCOhNrPRuHh4wDMwPsWTn8A==", "engines": { "node": ">=12.13.0" }, "peerDependencies": { "graphql": "14.x || 15.x || 16.x" } }, "node_modules/@apollo/utils.sortast": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@apollo/utils.sortast/-/utils.sortast-1.1.0.tgz", "integrity": "sha512-VPlTsmUnOwzPK5yGZENN069y6uUHgeiSlpEhRnLFYwYNoJHsuJq2vXVwIaSmts015WTPa2fpz1inkLYByeuRQA==", "dependencies": { "lodash.sortby": "^4.7.0" }, "engines": { "node": ">=12.13.0" }, "peerDependencies": { "graphql": "14.x || 15.x || 16.x" } }, "node_modules/@apollo/utils.stripsensitiveliterals": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/@apollo/utils.stripsensitiveliterals/-/utils.stripsensitiveliterals-1.2.0.tgz", "integrity": "sha512-E41rDUzkz/cdikM5147d8nfCFVKovXxKBcjvLEQ7bjZm/cg9zEcXvS6vFY8ugTubI3fn6zoqo0CyU8zT+BGP9w==", "engines": { "node": ">=12.13.0" }, "peerDependencies": { "graphql": "14.x || 15.x || 16.x" } }, "node_modules/@apollo/utils.usagereporting": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/@apollo/utils.usagereporting/-/utils.usagereporting-1.0.1.tgz", "integrity": "sha512-6dk+0hZlnDbahDBB2mP/PZ5ybrtCJdLMbeNJD+TJpKyZmSY6bA3SjI8Cr2EM9QA+AdziywuWg+SgbWUF3/zQqQ==", "dependencies": { "@apollo/usage-reporting-protobuf": "^4.0.0", "@apollo/utils.dropunuseddefinitions": "^1.1.0", "@apollo/utils.printwithreducedwhitespace": "^1.1.0", "@apollo/utils.removealiases": "1.0.0", "@apollo/utils.sortast": "^1.1.0", "@apollo/utils.stripsensitiveliterals": "^1.2.0" }, "engines": { "node": ">=12.13.0" }, "peerDependencies": { "graphql": "14.x || 15.x || 16.x" } }, "node_modules/@apollographql/apollo-tools": { "version": "0.5.4", "resolved": "https://registry.npmjs.org/@apollographql/apollo-tools/-/apollo-tools-0.5.4.tgz", "integrity": "sha512-shM3q7rUbNyXVVRkQJQseXv6bnYM3BUma/eZhwXR4xsuM+bqWnJKvW7SAfRjP7LuSCocrexa5AXhjjawNHrIlw==", "engines": { "node": ">=8", "npm": ">=6" }, "peerDependencies": { "graphql": "^14.2.1 || ^15.0.0 || ^16.0.0" } }, "node_modules/@apollographql/graphql-playground-html": { "version": "1.6.29", "resolved": "https://registry.npmjs.org/@apollographql/graphql-playground-html/-/graphql-playground-html-1.6.29.tgz", "integrity": "sha512-xCcXpoz52rI4ksJSdOCxeOCn2DLocxwHf9dVT/Q90Pte1LX+LY+91SFtJF3KXVHH8kEin+g1KKCQPKBjZJfWNA==", "dependencies": { "xss": "^1.0.8" } }, "node_modules/@cspotcode/source-map-support": { "version": "0.8.1", "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", "dev": true, "dependencies": { "@jridgewell/trace-mapping": "0.3.9" }, "engines": { "node": ">=12" } }, "node_modules/@graphql-tools/merge": { "version": "8.3.1", "resolved": "https://registry.npmjs.org/@graphql-tools/merge/-/merge-8.3.1.tgz", "integrity": "sha512-BMm99mqdNZbEYeTPK3it9r9S6rsZsQKtlqJsSBknAclXq2pGEfOxjcIZi+kBSkHZKPKCRrYDd5vY0+rUmIHVLg==", "dependencies": { "@graphql-tools/utils": "8.9.0", "tslib": "^2.4.0" }, "peerDependencies": { "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" } }, "node_modules/@graphql-tools/merge/node_modules/@graphql-tools/utils": { "version": "8.9.0", "resolved": "https://registry.npmjs.org/@graphql-tools/utils/-/utils-8.9.0.tgz", "integrity": "sha512-pjJIWH0XOVnYGXCqej8g/u/tsfV4LvLlj0eATKQu5zwnxd/TiTHq7Cg313qUPTFFHZ3PP5wJ15chYVtLDwaymg==", "dependencies": { "tslib": "^2.4.0" }, "peerDependencies": { "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" } }, "node_modules/@graphql-tools/mock": { "version": "8.7.19", "resolved": "https://registry.npmjs.org/@graphql-tools/mock/-/mock-8.7.19.tgz", "integrity": "sha512-LT2boYM+Y1vGFEhzmC7xDFRL8RPG20FbNcuk2/hHGH0Kh8K1hkItvL89tul3Pl7N6xerOnDZ3c3fx7Ls5GuFxA==", "dependencies": { "@graphql-tools/schema": "9.0.17", "@graphql-tools/utils": "9.2.1", "fast-json-stable-stringify": "^2.1.0", "tslib": "^2.4.0" }, "peerDependencies": { "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" } }, "node_modules/@graphql-tools/mock/node_modules/@graphql-tools/merge": { "version": "8.4.0", "resolved": "https://registry.npmjs.org/@graphql-tools/merge/-/merge-8.4.0.tgz", "integrity": "sha512-3XYCWe0d3I4F1azNj1CdShlbHfTIfiDgj00R9uvFH8tHKh7i1IWN3F7QQYovcHKhayaR6zPok3YYMESYQcBoaA==", "dependencies": { "@graphql-tools/utils": "9.2.1", "tslib": "^2.4.0" }, "peerDependencies": { "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" } }, "node_modules/@graphql-tools/mock/node_modules/@graphql-tools/schema": { "version": "9.0.17", "resolved": "https://registry.npmjs.org/@graphql-tools/schema/-/schema-9.0.17.tgz", "integrity": "sha512-HVLq0ecbkuXhJlpZ50IHP5nlISqH2GbNgjBJhhRzHeXhfwlUOT4ISXGquWTmuq61K0xSaO0aCjMpxe4QYbKTng==", "dependencies": { "@graphql-tools/merge": "8.4.0", "@graphql-tools/utils": "9.2.1", "tslib": "^2.4.0", "value-or-promise": "1.0.12" }, "peerDependencies": { "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" } }, "node_modules/@graphql-tools/mock/node_modules/value-or-promise": { "version": "1.0.12", "resolved": "https://registry.npmjs.org/value-or-promise/-/value-or-promise-1.0.12.tgz", "integrity": "sha512-Z6Uz+TYwEqE7ZN50gwn+1LCVo9ZVrpxRPOhOLnncYkY1ZzOYtrX8Fwf/rFktZ8R5mJms6EZf5TqNOMeZmnPq9Q==", "engines": { "node": ">=12" } }, "node_modules/@graphql-tools/schema": { "version": "8.5.1", "resolved": "https://registry.npmjs.org/@graphql-tools/schema/-/schema-8.5.1.tgz", "integrity": "sha512-0Esilsh0P/qYcB5DKQpiKeQs/jevzIadNTaT0jeWklPMwNbT7yMX4EqZany7mbeRRlSRwMzNzL5olyFdffHBZg==", "dependencies": { "@graphql-tools/merge": "8.3.1", "@graphql-tools/utils": "8.9.0", "tslib": "^2.4.0", "value-or-promise": "1.0.11" }, "peerDependencies": { "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" } }, "node_modules/@graphql-tools/schema/node_modules/@graphql-tools/utils": { "version": "8.9.0", "resolved": "https://registry.npmjs.org/@graphql-tools/utils/-/utils-8.9.0.tgz", "integrity": "sha512-pjJIWH0XOVnYGXCqej8g/u/tsfV4LvLlj0eATKQu5zwnxd/TiTHq7Cg313qUPTFFHZ3PP5wJ15chYVtLDwaymg==", "dependencies": { "tslib": "^2.4.0" }, "peerDependencies": { "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" } }, "node_modules/@graphql-tools/utils": { "version": "9.2.1", "resolved": "https://registry.npmjs.org/@graphql-tools/utils/-/utils-9.2.1.tgz", "integrity": "sha512-WUw506Ql6xzmOORlriNrD6Ugx+HjVgYxt9KCXD9mHAak+eaXSwuGGPyE60hy9xaDEoXKBsG7SkG69ybitaVl6A==", "dependencies": { "@graphql-typed-document-node/core": "^3.1.1", "tslib": "^2.4.0" }, "peerDependencies": { "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" } }, "node_modules/@graphql-typed-document-node/core": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/@graphql-typed-document-node/core/-/core-3.1.2.tgz", "integrity": "sha512-9anpBMM9mEgZN4wr2v8wHJI2/u5TnnggewRN6OlvXTTnuVyoY19X6rOv9XTqKRw6dcGKwZsBi8n0kDE2I5i4VA==", "peerDependencies": { "graphql": "^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" } }, "node_modules/@jest/schemas": { "version": "29.4.3", "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.4.3.tgz", "integrity": "sha512-VLYKXQmtmuEz6IxJsrZwzG9NvtkQsWNnWMsKxqWNu3+CnfzJQhp0WDDKWLVV9hLKr0l3SLLFRqcYHjhtyuDVxg==", "dependencies": { "@sinclair/typebox": "^0.25.16" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/@josephg/resolvable": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/@josephg/resolvable/-/resolvable-1.0.1.tgz", "integrity": "sha512-CtzORUwWTTOTqfVtHaKRJ0I1kNQd1bpn3sUh8I3nJDVY+5/M/Oe1DnEWzPQvqq/xPIIkzzzIP7mfCoAjFRvDhg==" }, "node_modules/@jridgewell/resolve-uri": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz", "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==", "dev": true, "engines": { "node": ">=6.0.0" } }, "node_modules/@jridgewell/sourcemap-codec": { "version": "1.4.14", "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==", "dev": true }, "node_modules/@jridgewell/trace-mapping": { "version": "0.3.9", "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", "dev": true, "dependencies": { "@jridgewell/resolve-uri": "^3.0.3", "@jridgewell/sourcemap-codec": "^1.4.10" } }, "node_modules/@npmcli/fs": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/@npmcli/fs/-/fs-3.1.0.tgz", "integrity": "sha512-7kZUAaLscfgbwBQRbvdMYaZOWyMEcPTH/tJjnyAWJ/dvvs9Ef+CERx/qJb9GExJpl1qipaDGn7KqHnFGGixd0w==", "dependencies": { "semver": "^7.3.5" }, "engines": { "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, "node_modules/@opentelemetry/api": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.4.1.tgz", "integrity": "sha512-O2yRJce1GOc6PAy3QxFM4NzFiWzvScDC1/5ihYBL6BUEVdq0XMWN01sppE+H6bBXbaFYipjwFLEWLg5PaSOThA==", "engines": { "node": ">=8.0.0" } }, "node_modules/@protobufjs/aspromise": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", "integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==" }, "node_modules/@protobufjs/base64": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz", "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==" }, "node_modules/@protobufjs/codegen": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz", "integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==" }, "node_modules/@protobufjs/eventemitter": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz", "integrity": "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==" }, "node_modules/@protobufjs/fetch": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz", "integrity": "sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==", "dependencies": { "@protobufjs/aspromise": "^1.1.1", "@protobufjs/inquire": "^1.1.0" } }, "node_modules/@protobufjs/float": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz", "integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==" }, "node_modules/@protobufjs/inquire": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz", "integrity": "sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==" }, "node_modules/@protobufjs/path": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz", "integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==" }, "node_modules/@protobufjs/pool": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz", "integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==" }, "node_modules/@protobufjs/utf8": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz", "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==" }, "node_modules/@sinclair/typebox": { "version": "0.25.24", "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.25.24.tgz", "integrity": "sha512-XJfwUVUKDHF5ugKwIcxEgc9k8b7HbznCp6eUfWgu710hMPNIO4aw4/zB5RogDQz8nd6gyCDpU9O/m6qYEWY6yQ==" }, "node_modules/@tootallnate/once": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz", "integrity": "sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==", "engines": { "node": ">= 10" } }, "node_modules/@tsconfig/node10": { "version": "1.0.9", "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz", "integrity": "sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==", "dev": true }, "node_modules/@tsconfig/node12": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", "dev": true }, "node_modules/@tsconfig/node14": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", "dev": true }, "node_modules/@tsconfig/node16": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.3.tgz", "integrity": "sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ==", "dev": true }, "node_modules/@types/accepts": { "version": "1.3.5", "resolved": "https://registry.npmjs.org/@types/accepts/-/accepts-1.3.5.tgz", "integrity": "sha512-jOdnI/3qTpHABjM5cx1Hc0sKsPoYCp+DP/GJRGtDlPd7fiV9oXGGIcjW/ZOxLIvjGz8MA+uMZI9metHlgqbgwQ==", "dependencies": { "@types/node": "*" } }, "node_modules/@types/body-parser": { "version": "1.19.2", "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.2.tgz", "integrity": "sha512-ALYone6pm6QmwZoAgeyNksccT9Q4AWZQ6PvfwR37GT6r6FWUPguq6sUmNGSMV2Wr761oQoBxwGGa6DR5o1DC9g==", "dependencies": { "@types/connect": "*", "@types/node": "*" } }, "node_modules/@types/connect": { "version": "3.4.35", "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.35.tgz", "integrity": "sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ==", "dependencies": { "@types/node": "*" } }, "node_modules/@types/cors": { "version": "2.8.12", "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.12.tgz", "integrity": "sha512-vt+kDhq/M2ayberEtJcIN/hxXy1Pk+59g2FV/ZQceeaTyCtCucjL2Q7FXlFjtWn4n15KCr1NE2lNNFhp0lEThw==" }, "node_modules/@types/express": { "version": "4.17.14", "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.14.tgz", "integrity": "sha512-TEbt+vaPFQ+xpxFLFssxUDXj5cWCxZJjIcB7Yg0k0GMHGtgtQgpvx/MUQUeAkNbA9AAGrwkAsoeItdTgS7FMyg==", "dependencies": { "@types/body-parser": "*", "@types/express-serve-static-core": "^4.17.18", "@types/qs": "*", "@types/serve-static": "*" } }, "node_modules/@types/express-serve-static-core": { "version": "4.17.33", "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.33.tgz", "integrity": "sha512-TPBqmR/HRYI3eC2E5hmiivIzv+bidAfXofM+sbonAGvyDhySGw9/PQZFt2BLOrjUUR++4eJVpx6KnLQK1Fk9tA==", "dependencies": { "@types/node": "*", "@types/qs": "*", "@types/range-parser": "*" } }, "node_modules/@types/long": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/@types/long/-/long-4.0.2.tgz", "integrity": "sha512-MqTGEo5bj5t157U6fA/BiDynNkn0YknVdh48CMPkTSpFTVmvao5UQmm7uEF6xBEo7qIMAlY/JSleYaE6VOdpaA==" }, "node_modules/@types/mime": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/@types/mime/-/mime-3.0.1.tgz", "integrity": "sha512-Y4XFY5VJAuw0FgAqPNd6NNoV44jbq9Bz2L7Rh/J6jLTiHBSBJa9fxqQIvkIld4GsoDOcCbvzOUAbLPsSKKg+uA==" }, "node_modules/@types/node": { "version": "18.15.3", "resolved": "https://registry.npmjs.org/@types/node/-/node-18.15.3.tgz", "integrity": "sha512-p6ua9zBxz5otCmbpb5D3U4B5Nanw6Pk3PPyX05xnxbB/fRv71N7CPmORg7uAD5P70T0xmx1pzAx/FUfa5X+3cw==" }, "node_modules/@types/node-fetch": { "version": "2.6.2", "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.6.2.tgz", "integrity": "sha512-DHqhlq5jeESLy19TYhLakJ07kNumXWjcDdxXsLUMJZ6ue8VZJj4kLPQVE/2mdHh3xZziNF1xppu5lwmS53HR+A==", "dependencies": { "@types/node": "*", "form-data": "^3.0.0" } }, "node_modules/@types/qs": { "version": "6.9.7", "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.7.tgz", "integrity": "sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw==" }, "node_modules/@types/range-parser": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.4.tgz", "integrity": "sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw==" }, "node_modules/@types/serve-static": { "version": "1.15.1", "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.1.tgz", "integrity": "sha512-NUo5XNiAdULrJENtJXZZ3fHtfMolzZwczzBbnAeBbqBwG+LaG6YaJtuwzwGSQZ2wsCrxjEhNNjAkKigy3n8teQ==", "dependencies": { "@types/mime": "*", "@types/node": "*" } }, "node_modules/abbrev": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", "dev": true }, "node_modules/accepts": { "version": "1.3.8", "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", "dependencies": { "mime-types": "~2.1.34", "negotiator": "0.6.3" }, "engines": { "node": ">= 0.6" } }, "node_modules/acorn": { "version": "8.8.2", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.2.tgz", "integrity": "sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==", "dev": true, "bin": { "acorn": "bin/acorn" }, "engines": { "node": ">=0.4.0" } }, "node_modules/acorn-walk": { "version": "8.2.0", "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==", "dev": true, "engines": { "node": ">=0.4.0" } }, "node_modules/agent-base": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", "dependencies": { "debug": "4" }, "engines": { "node": ">= 6.0.0" } }, "node_modules/agent-base/node_modules/debug": { "version": "4.3.4", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", "dependencies": { "ms": "2.1.2" }, "engines": { "node": ">=6.0" }, "peerDependenciesMeta": { "supports-color": { "optional": true } } }, "node_modules/agent-base/node_modules/ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, "node_modules/agentkeepalive": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.3.0.tgz", "integrity": "sha512-7Epl1Blf4Sy37j4v9f9FjICCh4+KAQOyXgHEwlyBiAQLbhKdq/i2QQU3amQalS/wPhdPzDXPL5DMR5bkn+YeWg==", "dependencies": { "debug": "^4.1.0", "depd": "^2.0.0", "humanize-ms": "^1.2.1" }, "engines": { "node": ">= 8.0.0" } }, "node_modules/agentkeepalive/node_modules/debug": { "version": "4.3.4", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", "dependencies": { "ms": "2.1.2" }, "engines": { "node": ">=6.0" }, "peerDependenciesMeta": { "supports-color": { "optional": true } } }, "node_modules/agentkeepalive/node_modules/ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, "node_modules/aggregate-error": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", "dependencies": { "clean-stack": "^2.0.0", "indent-string": "^4.0.0" }, "engines": { "node": ">=8" } }, "node_modules/ansi-styles": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dependencies": { "color-convert": "^2.0.1" }, "engines": { "node": ">=8" }, "funding": { "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, "node_modules/anymatch": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", "dev": true, "dependencies": { "normalize-path": "^3.0.0", "picomatch": "^2.0.4" }, "engines": { "node": ">= 8" } }, "node_modules/apollo-datasource": { "version": "3.3.2", "resolved": "https://registry.npmjs.org/apollo-datasource/-/apollo-datasource-3.3.2.tgz", "integrity": "sha512-L5TiS8E2Hn/Yz7SSnWIVbZw0ZfEIXZCa5VUiVxD9P53JvSrf4aStvsFDlGWPvpIdCR+aly2CfoB79B9/JjKFqg==", "deprecated": "The `apollo-datasource` package is part of Apollo Server v2 and v3, which are now deprecated (end-of-life October 22nd 2023). See https://www.apollographql.com/docs/apollo-server/previous-versions/ for more details.", "dependencies": { "@apollo/utils.keyvaluecache": "^1.0.1", "apollo-server-env": "^4.2.1" }, "engines": { "node": ">=12.0" } }, "node_modules/apollo-datasource/node_modules/@apollo/utils.keyvaluecache": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/@apollo/utils.keyvaluecache/-/utils.keyvaluecache-1.0.2.tgz", "integrity": "sha512-p7PVdLPMnPzmXSQVEsy27cYEjVON+SH/Wb7COyW3rQN8+wJgT1nv9jZouYtztWW8ZgTkii5T6tC9qfoDREd4mg==", "dependencies": { "@apollo/utils.logger": "^1.0.0", "lru-cache": "7.10.1 - 7.13.1" } }, "node_modules/apollo-datasource/node_modules/@apollo/utils.logger": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/@apollo/utils.logger/-/utils.logger-1.0.1.tgz", "integrity": "sha512-XdlzoY7fYNK4OIcvMD2G94RoFZbzTQaNP0jozmqqMudmaGo2I/2Jx71xlDJ801mWA/mbYRihyaw6KJii7k5RVA==" }, "node_modules/apollo-datasource/node_modules/lru-cache": { "version": "7.13.1", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.13.1.tgz", "integrity": "sha512-CHqbAq7NFlW3RSnoWXLJBxCWaZVBrfa9UEHId2M3AW8iEBurbqduNexEUCGc3SHc6iCYXNJCDi903LajSVAEPQ==", "engines": { "node": ">=12" } }, "node_modules/apollo-reporting-protobuf": { "version": "3.4.0", "resolved": "https://registry.npmjs.org/apollo-reporting-protobuf/-/apollo-reporting-protobuf-3.4.0.tgz", "integrity": "sha512-h0u3EbC/9RpihWOmcSsvTW2O6RXVaD/mPEjfrPkxRPTEPWqncsgOoRJw+wih4OqfH3PvTJvoEIf4LwKrUaqWog==", "deprecated": "The `apollo-reporting-protobuf` package is part of Apollo Server v2 and v3, which are now deprecated (end-of-life October 22nd 2023). This package's functionality is now found in the `@apollo/usage-reporting-protobuf` package. See https://www.apollographql.com/docs/apollo-server/previous-versions/ for more details.", "dependencies": { "@apollo/protobufjs": "1.2.6" } }, "node_modules/apollo-reporting-protobuf/node_modules/@apollo/protobufjs": { "version": "1.2.6", "resolved": "https://registry.npmjs.org/@apollo/protobufjs/-/protobufjs-1.2.6.tgz", "integrity": "sha512-Wqo1oSHNUj/jxmsVp4iR3I480p6qdqHikn38lKrFhfzcDJ7lwd7Ck7cHRl4JE81tWNArl77xhnG/OkZhxKBYOw==", "hasInstallScript": true, "dependencies": { "@protobufjs/aspromise": "^1.1.2", "@protobufjs/base64": "^1.1.2", "@protobufjs/codegen": "^2.0.4", "@protobufjs/eventemitter": "^1.1.0", "@protobufjs/fetch": "^1.1.0", "@protobufjs/float": "^1.0.2", "@protobufjs/inquire": "^1.1.0", "@protobufjs/path": "^1.1.2", "@protobufjs/pool": "^1.1.0", "@protobufjs/utf8": "^1.1.0", "@types/long": "^4.0.0", "@types/node": "^10.1.0", "long": "^4.0.0" }, "bin": { "apollo-pbjs": "bin/pbjs", "apollo-pbts": "bin/pbts" } }, "node_modules/apollo-reporting-protobuf/node_modules/@types/node": { "version": "10.17.60", "resolved": "https://registry.npmjs.org/@types/node/-/node-10.17.60.tgz", "integrity": "sha512-F0KIgDJfy2nA3zMLmWGKxcH2ZVEtCZXHHdOQs2gSaQ27+lNeEfGxzkIw90aXswATX7AZ33tahPbzy6KAfUreVw==" }, "node_modules/apollo-server": { "version": "3.12.0", "resolved": "https://registry.npmjs.org/apollo-server/-/apollo-server-3.12.0.tgz", "integrity": "sha512-wZHLgBoIdGxr/YpPTG5RwNnS+B2y70T/nCegCnU6Yl+H3PXB92OIguLMhdJIZVjukIOhiQT12dNIehqLQ+1hMQ==", "deprecated": "The `apollo-server` package is part of Apollo Server v2 and v3, which are now deprecated (end-of-life October 22nd 2023). This package's functionality is now found in the `@apollo/server` package. See https://www.apollographql.com/docs/apollo-server/previous-versions/ for more details.", "dependencies": { "@types/express": "4.17.14", "apollo-server-core": "^3.12.0", "apollo-server-express": "^3.12.0", "express": "^4.17.1" }, "peerDependencies": { "graphql": "^15.3.0 || ^16.0.0" } }, "node_modules/apollo-server-core": { "version": "3.12.0", "resolved": "https://registry.npmjs.org/apollo-server-core/-/apollo-server-core-3.12.0.tgz", "integrity": "sha512-hq7iH6Cgldgmnjs9FVSZeKWRpi0/ZR+iJ1arzeD2VXGxxgk1mAm/cz1Tx0TYgegZI+FvvrRl0UhKEx7sLnIxIg==", "deprecated": "The `apollo-server-core` package is part of Apollo Server v2 and v3, which are now deprecated (end-of-life October 22nd 2023). This package's functionality is now found in the `@apollo/server` package. See https://www.apollographql.com/docs/apollo-server/previous-versions/ for more details.", "dependencies": { "@apollo/utils.keyvaluecache": "^1.0.1", "@apollo/utils.logger": "^1.0.0", "@apollo/utils.usagereporting": "^1.0.0", "@apollographql/apollo-tools": "^0.5.3", "@apollographql/graphql-playground-html": "1.6.29", "@graphql-tools/mock": "^8.1.2", "@graphql-tools/schema": "^8.0.0", "@josephg/resolvable": "^1.0.0", "apollo-datasource": "^3.3.2", "apollo-reporting-protobuf": "^3.4.0", "apollo-server-env": "^4.2.1", "apollo-server-errors": "^3.3.1", "apollo-server-plugin-base": "^3.7.2", "apollo-server-types": "^3.8.0", "async-retry": "^1.2.1", "fast-json-stable-stringify": "^2.1.0", "graphql-tag": "^2.11.0", "loglevel": "^1.6.8", "lru-cache": "^6.0.0", "node-abort-controller": "^3.0.1", "sha.js": "^2.4.11", "uuid": "^9.0.0", "whatwg-mimetype": "^3.0.0" }, "engines": { "node": ">=12.0" }, "peerDependencies": { "graphql": "^15.3.0 || ^16.0.0" } }, "node_modules/apollo-server-core/node_modules/@apollo/utils.keyvaluecache": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/@apollo/utils.keyvaluecache/-/utils.keyvaluecache-1.0.2.tgz", "integrity": "sha512-p7PVdLPMnPzmXSQVEsy27cYEjVON+SH/Wb7COyW3rQN8+wJgT1nv9jZouYtztWW8ZgTkii5T6tC9qfoDREd4mg==", "dependencies": { "@apollo/utils.logger": "^1.0.0", "lru-cache": "7.10.1 - 7.13.1" } }, "node_modules/apollo-server-core/node_modules/@apollo/utils.keyvaluecache/node_modules/lru-cache": { "version": "7.13.1", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.13.1.tgz", "integrity": "sha512-CHqbAq7NFlW3RSnoWXLJBxCWaZVBrfa9UEHId2M3AW8iEBurbqduNexEUCGc3SHc6iCYXNJCDi903LajSVAEPQ==", "engines": { "node": ">=12" } }, "node_modules/apollo-server-core/node_modules/@apollo/utils.logger": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/@apollo/utils.logger/-/utils.logger-1.0.1.tgz", "integrity": "sha512-XdlzoY7fYNK4OIcvMD2G94RoFZbzTQaNP0jozmqqMudmaGo2I/2Jx71xlDJ801mWA/mbYRihyaw6KJii7k5RVA==" }, "node_modules/apollo-server-core/node_modules/lru-cache": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", "dependencies": { "yallist": "^4.0.0" }, "engines": { "node": ">=10" } }, "node_modules/apollo-server-env": { "version": "4.2.1", "resolved": "https://registry.npmjs.org/apollo-server-env/-/apollo-server-env-4.2.1.tgz", "integrity": "sha512-vm/7c7ld+zFMxibzqZ7SSa5tBENc4B0uye9LTfjJwGoQFY5xsUPH5FpO5j0bMUDZ8YYNbrF9SNtzc5Cngcr90g==", "deprecated": "The `apollo-server-env` package is part of Apollo Server v2 and v3, which are now deprecated (end-of-life October 22nd 2023). This package's functionality is now found in the `@apollo/utils.fetcher` package. See https://www.apollographql.com/docs/apollo-server/previous-versions/ for more details.", "dependencies": { "node-fetch": "^2.6.7" }, "engines": { "node": ">=12.0" } }, "node_modules/apollo-server-errors": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/apollo-server-errors/-/apollo-server-errors-3.3.1.tgz", "integrity": "sha512-xnZJ5QWs6FixHICXHxUfm+ZWqqxrNuPlQ+kj5m6RtEgIpekOPssH/SD9gf2B4HuWV0QozorrygwZnux8POvyPA==", "deprecated": "The `apollo-server-errors` package is part of Apollo Server v2 and v3, which are now deprecated (end-of-life October 22nd 2023). This package's functionality is now found in the `@apollo/server` package. See https://www.apollographql.com/docs/apollo-server/previous-versions/ for more details.", "engines": { "node": ">=12.0" }, "peerDependencies": { "graphql": "^15.3.0 || ^16.0.0" } }, "node_modules/apollo-server-express": { "version": "3.12.0", "resolved": "https://registry.npmjs.org/apollo-server-express/-/apollo-server-express-3.12.0.tgz", "integrity": "sha512-m8FaGPUfDOEGSm7QRWRmUUGjG/vqvpQoorkId9/FXkC57fz/A59kEdrzkMt9538Xgsa5AV+X4MEWLJhTvlW3LQ==", "deprecated": "The `apollo-server-express` package is part of Apollo Server v2 and v3, which are now deprecated (end-of-life October 22nd 2023). This package's functionality is now found in the `@apollo/server` package. See https://www.apollographql.com/docs/apollo-server/previous-versions/ for more details.", "dependencies": { "@types/accepts": "^1.3.5", "@types/body-parser": "1.19.2", "@types/cors": "2.8.12", "@types/express": "4.17.14", "@types/express-serve-static-core": "4.17.31", "accepts": "^1.3.5", "apollo-server-core": "^3.12.0", "apollo-server-types": "^3.8.0", "body-parser": "^1.19.0", "cors": "^2.8.5", "parseurl": "^1.3.3" }, "engines": { "node": ">=12.0" }, "peerDependencies": { "express": "^4.17.1", "graphql": "^15.3.0 || ^16.0.0" } }, "node_modules/apollo-server-express/node_modules/@types/express-serve-static-core": { "version": "4.17.31", "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.31.tgz", "integrity": "sha512-DxMhY+NAsTwMMFHBTtJFNp5qiHKJ7TeqOo23zVEM9alT1Ml27Q3xcTH0xwxn7Q0BbMcVEJOs/7aQtUWupUQN3Q==", "dependencies": { "@types/node": "*", "@types/qs": "*", "@types/range-parser": "*" } }, "node_modules/apollo-server-plugin-base": { "version": "3.7.2", "resolved": "https://registry.npmjs.org/apollo-server-plugin-base/-/apollo-server-plugin-base-3.7.2.tgz", "integrity": "sha512-wE8dwGDvBOGehSsPTRZ8P/33Jan6/PmL0y0aN/1Z5a5GcbFhDaaJCjK5cav6npbbGL2DPKK0r6MPXi3k3N45aw==", "deprecated": "The `apollo-server-plugin-base` package is part of Apollo Server v2 and v3, which are now deprecated (end-of-life October 22nd 2023). This package's functionality is now found in the `@apollo/server` package. See https://www.apollographql.com/docs/apollo-server/previous-versions/ for more details.", "dependencies": { "apollo-server-types": "^3.8.0" }, "engines": { "node": ">=12.0" }, "peerDependencies": { "graphql": "^15.3.0 || ^16.0.0" } }, "node_modules/apollo-server-types": { "version": "3.8.0", "resolved": "https://registry.npmjs.org/apollo-server-types/-/apollo-server-types-3.8.0.tgz", "integrity": "sha512-ZI/8rTE4ww8BHktsVpb91Sdq7Cb71rdSkXELSwdSR0eXu600/sY+1UXhTWdiJvk+Eq5ljqoHLwLbY2+Clq2b9A==", "deprecated": "The `apollo-server-types` package is part of Apollo Server v2 and v3, which are now deprecated (end-of-life October 22nd 2023). This package's functionality is now found in the `@apollo/server` package. See https://www.apollographql.com/docs/apollo-server/previous-versions/ for more details.", "dependencies": { "@apollo/utils.keyvaluecache": "^1.0.1", "@apollo/utils.logger": "^1.0.0", "apollo-reporting-protobuf": "^3.4.0", "apollo-server-env": "^4.2.1" }, "engines": { "node": ">=12.0" }, "peerDependencies": { "graphql": "^15.3.0 || ^16.0.0" } }, "node_modules/apollo-server-types/node_modules/@apollo/utils.keyvaluecache": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/@apollo/utils.keyvaluecache/-/utils.keyvaluecache-1.0.2.tgz", "integrity": "sha512-p7PVdLPMnPzmXSQVEsy27cYEjVON+SH/Wb7COyW3rQN8+wJgT1nv9jZouYtztWW8ZgTkii5T6tC9qfoDREd4mg==", "dependencies": { "@apollo/utils.logger": "^1.0.0", "lru-cache": "7.10.1 - 7.13.1" } }, "node_modules/apollo-server-types/node_modules/@apollo/utils.logger": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/@apollo/utils.logger/-/utils.logger-1.0.1.tgz", "integrity": "sha512-XdlzoY7fYNK4OIcvMD2G94RoFZbzTQaNP0jozmqqMudmaGo2I/2Jx71xlDJ801mWA/mbYRihyaw6KJii7k5RVA==" }, "node_modules/apollo-server-types/node_modules/lru-cache": { "version": "7.13.1", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.13.1.tgz", "integrity": "sha512-CHqbAq7NFlW3RSnoWXLJBxCWaZVBrfa9UEHId2M3AW8iEBurbqduNexEUCGc3SHc6iCYXNJCDi903LajSVAEPQ==", "engines": { "node": ">=12" } }, "node_modules/arg": { "version": "4.1.3", "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", "dev": true }, "node_modules/array-flatten": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==" }, "node_modules/async-retry": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/async-retry/-/async-retry-1.3.3.tgz", "integrity": "sha512-wfr/jstw9xNi/0teMHrRW7dsz3Lt5ARhYNZ2ewpadnhaIp5mbALhOAP+EAdsC7t4Z6wqsDVv9+W6gm1Dk9mEyw==", "dependencies": { "retry": "0.13.1" } }, "node_modules/asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" }, "node_modules/available-typed-arrays": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz", "integrity": "sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==", "engines": { "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" }, "node_modules/binary-extensions": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", "dev": true, "engines": { "node": ">=8" } }, "node_modules/body-parser": { "version": "1.20.2", "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.2.tgz", "integrity": "sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA==", "dependencies": { "bytes": "3.1.2", "content-type": "~1.0.5", "debug": "2.6.9", "depd": "2.0.0", "destroy": "1.2.0", "http-errors": "2.0.0", "iconv-lite": "0.4.24", "on-finished": "2.4.1", "qs": "6.11.0", "raw-body": "2.5.2", "type-is": "~1.6.18", "unpipe": "1.0.0" }, "engines": { "node": ">= 0.8", "npm": "1.2.8000 || >= 1.4.16" } }, "node_modules/brace-expansion": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", "dependencies": { "balanced-match": "^1.0.0" } }, "node_modules/braces": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", "dev": true, "dependencies": { "fill-range": "^7.0.1" }, "engines": { "node": ">=8" } }, "node_modules/bytes": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", "engines": { "node": ">= 0.8" } }, "node_modules/cacache": { "version": "17.0.4", "resolved": "https://registry.npmjs.org/cacache/-/cacache-17.0.4.tgz", "integrity": "sha512-Z/nL3gU+zTUjz5pCA5vVjYM8pmaw2kxM7JEiE0fv3w77Wj+sFbi70CrBruUWH0uNcEdvLDixFpgA2JM4F4DBjA==", "dependencies": { "@npmcli/fs": "^3.1.0", "fs-minipass": "^3.0.0", "glob": "^8.0.1", "lru-cache": "^7.7.1", "minipass": "^4.0.0", "minipass-collect": "^1.0.2", "minipass-flush": "^1.0.5", "minipass-pipeline": "^1.2.4", "p-map": "^4.0.0", "promise-inflight": "^1.0.1", "ssri": "^10.0.0", "tar": "^6.1.11", "unique-filename": "^3.0.0" }, "engines": { "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, "node_modules/call-bind": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", "dependencies": { "function-bind": "^1.1.1", "get-intrinsic": "^1.0.2" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" }, "engines": { "node": ">=10" }, "funding": { "url": "https://github.com/chalk/chalk?sponsor=1" } }, "node_modules/chokidar": { "version": "3.5.3", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", "dev": true, "funding": [ { "type": "individual", "url": "https://paulmillr.com/funding/" } ], "dependencies": { "anymatch": "~3.1.2", "braces": "~3.0.2", "glob-parent": "~5.1.2", "is-binary-path": "~2.1.0", "is-glob": "~4.0.1", "normalize-path": "~3.0.0", "readdirp": "~3.6.0" }, "engines": { "node": ">= 8.10.0" }, "optionalDependencies": { "fsevents": "~2.3.2" } }, "node_modules/chownr": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", "engines": { "node": ">=10" } }, "node_modules/clean-stack": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", "engines": { "node": ">=6" } }, "node_modules/color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dependencies": { "color-name": "~1.1.4" }, "engines": { "node": ">=7.0.0" } }, "node_modules/color-name": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" }, "node_modules/combined-stream": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", "dependencies": { "delayed-stream": "~1.0.0" }, "engines": { "node": ">= 0.8" } }, "node_modules/commander": { "version": "2.20.3", "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==" }, "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", "dev": true }, "node_modules/content-disposition": { "version": "0.5.4", "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", "dependencies": { "safe-buffer": "5.2.1" }, "engines": { "node": ">= 0.6" } }, "node_modules/content-type": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", "engines": { "node": ">= 0.6" } }, "node_modules/cookie": { "version": "0.5.0", "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==", "engines": { "node": ">= 0.6" } }, "node_modules/cookie-signature": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==" }, "node_modules/cors": { "version": "2.8.5", "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", "dependencies": { "object-assign": "^4", "vary": "^1" }, "engines": { "node": ">= 0.10" } }, "node_modules/create-require": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", "dev": true }, "node_modules/cssfilter": { "version": "0.0.10", "resolved": "https://registry.npmjs.org/cssfilter/-/cssfilter-0.0.10.tgz", "integrity": "sha512-FAaLDaplstoRsDR8XGYH51znUN0UY7nMc6Z9/fvE8EXGwvJE9hu7W2vHwx1+bd6gCYnln9nLbzxFTrcO9YQDZw==" }, "node_modules/debug": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", "dependencies": { "ms": "2.0.0" } }, "node_modules/deep-equal": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-2.2.0.tgz", "integrity": "sha512-RdpzE0Hv4lhowpIUKKMJfeH6C1pXdtT1/it80ubgWqwI3qpuxUBpC1S4hnHg+zjnuOoDkzUtUCEEkG+XG5l3Mw==", "dependencies": { "call-bind": "^1.0.2", "es-get-iterator": "^1.1.2", "get-intrinsic": "^1.1.3", "is-arguments": "^1.1.1", "is-array-buffer": "^3.0.1", "is-date-object": "^1.0.5", "is-regex": "^1.1.4", "is-shared-array-buffer": "^1.0.2", "isarray": "^2.0.5", "object-is": "^1.1.5", "object-keys": "^1.1.1", "object.assign": "^4.1.4", "regexp.prototype.flags": "^1.4.3", "side-channel": "^1.0.4", "which-boxed-primitive": "^1.0.2", "which-collection": "^1.0.1", "which-typed-array": "^1.1.9" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/define-properties": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.0.tgz", "integrity": "sha512-xvqAVKGfT1+UAvPwKTVw/njhdQ8ZhXK4lI0bCIuCMrp2up9nPnaDftrLtmpTazqd1o+UY4zgzU+avtMbDP+ldA==", "dependencies": { "has-property-descriptors": "^1.0.0", "object-keys": "^1.1.1" }, "engines": { "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/delayed-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", "engines": { "node": ">=0.4.0" } }, "node_modules/depd": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", "engines": { "node": ">= 0.8" } }, "node_modules/destroy": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", "engines": { "node": ">= 0.8", "npm": "1.2.8000 || >= 1.4.16" } }, "node_modules/diff": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", "dev": true, "engines": { "node": ">=0.3.1" } }, "node_modules/ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" }, "node_modules/encodeurl": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", "engines": { "node": ">= 0.8" } }, "node_modules/encoding": { "version": "0.1.13", "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.13.tgz", "integrity": "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==", "optional": true, "dependencies": { "iconv-lite": "^0.6.2" } }, "node_modules/encoding/node_modules/iconv-lite": { "version": "0.6.3", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", "optional": true, "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" }, "engines": { "node": ">=0.10.0" } }, "node_modules/err-code": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/err-code/-/err-code-2.0.3.tgz", "integrity": "sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==" }, "node_modules/es-get-iterator": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/es-get-iterator/-/es-get-iterator-1.1.3.tgz", "integrity": "sha512-sPZmqHBe6JIiTfN5q2pEi//TwxmAFHwj/XEuYjTuse78i8KxaqMTTzxPoFKuzRpDpTJ+0NAbpfenkmH2rePtuw==", "dependencies": { "call-bind": "^1.0.2", "get-intrinsic": "^1.1.3", "has-symbols": "^1.0.3", "is-arguments": "^1.1.1", "is-map": "^2.0.2", "is-set": "^2.0.2", "is-string": "^1.0.7", "isarray": "^2.0.5", "stop-iteration-iterator": "^1.0.0" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/escape-html": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==" }, "node_modules/etag": { "version": "1.8.1", "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", "engines": { "node": ">= 0.6" } }, "node_modules/express": { "version": "4.18.2", "resolved": "https://registry.npmjs.org/express/-/express-4.18.2.tgz", "integrity": "sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==", "dependencies": { "accepts": "~1.3.8", "array-flatten": "1.1.1", "body-parser": "1.20.1", "content-disposition": "0.5.4", "content-type": "~1.0.4", "cookie": "0.5.0", "cookie-signature": "1.0.6", "debug": "2.6.9", "depd": "2.0.0", "encodeurl": "~1.0.2", "escape-html": "~1.0.3", "etag": "~1.8.1", "finalhandler": "1.2.0", "fresh": "0.5.2", "http-errors": "2.0.0", "merge-descriptors": "1.0.1", "methods": "~1.1.2", "on-finished": "2.4.1", "parseurl": "~1.3.3", "path-to-regexp": "0.1.7", "proxy-addr": "~2.0.7", "qs": "6.11.0", "range-parser": "~1.2.1", "safe-buffer": "5.2.1", "send": "0.18.0", "serve-static": "1.15.0", "setprototypeof": "1.2.0", "statuses": "2.0.1", "type-is": "~1.6.18", "utils-merge": "1.0.1", "vary": "~1.1.2" }, "engines": { "node": ">= 0.10.0" } }, "node_modules/express/node_modules/body-parser": { "version": "1.20.1", "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz", "integrity": "sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==", "dependencies": { "bytes": "3.1.2", "content-type": "~1.0.4", "debug": "2.6.9", "depd": "2.0.0", "destroy": "1.2.0", "http-errors": "2.0.0", "iconv-lite": "0.4.24", "on-finished": "2.4.1", "qs": "6.11.0", "raw-body": "2.5.1", "type-is": "~1.6.18", "unpipe": "1.0.0" }, "engines": { "node": ">= 0.8", "npm": "1.2.8000 || >= 1.4.16" } }, "node_modules/express/node_modules/raw-body": { "version": "2.5.1", "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz", "integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==", "dependencies": { "bytes": "3.1.2", "http-errors": "2.0.0", "iconv-lite": "0.4.24", "unpipe": "1.0.0" }, "engines": { "node": ">= 0.8" } }, "node_modules/fast-json-stable-stringify": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==" }, "node_modules/fill-range": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", "dev": true, "dependencies": { "to-regex-range": "^5.0.1" }, "engines": { "node": ">=8" } }, "node_modules/finalhandler": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz", "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==", "dependencies": { "debug": "2.6.9", "encodeurl": "~1.0.2", "escape-html": "~1.0.3", "on-finished": "2.4.1", "parseurl": "~1.3.3", "statuses": "2.0.1", "unpipe": "~1.0.0" }, "engines": { "node": ">= 0.8" } }, "node_modules/for-each": { "version": "0.3.3", "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", "dependencies": { "is-callable": "^1.1.3" } }, "node_modules/form-data": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.1.tgz", "integrity": "sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==", "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", "mime-types": "^2.1.12" }, "engines": { "node": ">= 6" } }, "node_modules/forwarded": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", "engines": { "node": ">= 0.6" } }, "node_modules/fresh": { "version": "0.5.2", "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", "engines": { "node": ">= 0.6" } }, "node_modules/fs-minipass": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-3.0.1.tgz", "integrity": "sha512-MhaJDcFRTuLidHrIttu0RDGyyXs/IYHVmlcxfLAEFIWjc1vdLAkdwT7Ace2u7DbitWC0toKMl5eJZRYNVreIMw==", "dependencies": { "minipass": "^4.0.0" }, "engines": { "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, "node_modules/fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" }, "node_modules/fsevents": { "version": "2.3.2", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", "dev": true, "hasInstallScript": true, "optional": true, "os": [ "darwin" ], "engines": { "node": "^8.16.0 || ^10.6.0 || >=11.0.0" } }, "node_modules/function-bind": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" }, "node_modules/functions-have-names": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/get-intrinsic": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.0.tgz", "integrity": "sha512-L049y6nFOuom5wGyRc3/gdTLO94dySVKRACj1RmJZBQXlbTMhtNIgkWkUHq+jYmZvKf14EW1EoJnnjbmoHij0Q==", "dependencies": { "function-bind": "^1.1.1", "has": "^1.0.3", "has-symbols": "^1.0.3" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/glob": { "version": "8.1.0", "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz", "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==", "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", "inherits": "2", "minimatch": "^5.0.1", "once": "^1.3.0" }, "engines": { "node": ">=12" }, "funding": { "url": "https://github.com/sponsors/isaacs" } }, "node_modules/glob-parent": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", "dev": true, "dependencies": { "is-glob": "^4.0.1" }, "engines": { "node": ">= 6" } }, "node_modules/gopd": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", "dependencies": { "get-intrinsic": "^1.1.3" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/graphql": { "version": "16.6.0", "resolved": "https://registry.npmjs.org/graphql/-/graphql-16.6.0.tgz", "integrity": "sha512-KPIBPDlW7NxrbT/eh4qPXz5FiFdL5UbaA0XUNz2Rp3Z3hqBSkbj0GVjwFDztsWVauZUWsbKHgMg++sk8UX0bkw==", "engines": { "node": "^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0" } }, "node_modules/graphql-tag": { "version": "2.12.6", "resolved": "https://registry.npmjs.org/graphql-tag/-/graphql-tag-2.12.6.tgz", "integrity": "sha512-FdSNcu2QQcWnM2VNvSCCDCVS5PpPqpzgFT8+GXzqJuoDd0CBncxCY278u4mhRO7tMgo2JjgJA5aZ+nWSQ/Z+xg==", "dependencies": { "tslib": "^2.1.0" }, "engines": { "node": ">=10" }, "peerDependencies": { "graphql": "^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0" } }, "node_modules/has": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", "dependencies": { "function-bind": "^1.1.1" }, "engines": { "node": ">= 0.4.0" } }, "node_modules/has-bigints": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==", "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "engines": { "node": ">=8" } }, "node_modules/has-property-descriptors": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz", "integrity": "sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==", "dependencies": { "get-intrinsic": "^1.1.1" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/has-symbols": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", "engines": { "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/has-tostringtag": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", "dependencies": { "has-symbols": "^1.0.2" }, "engines": { "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/http-cache-semantics": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz", "integrity": "sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==" }, "node_modules/http-errors": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", "dependencies": { "depd": "2.0.0", "inherits": "2.0.4", "setprototypeof": "1.2.0", "statuses": "2.0.1", "toidentifier": "1.0.1" }, "engines": { "node": ">= 0.8" } }, "node_modules/http-proxy-agent": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz", "integrity": "sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==", "dependencies": { "@tootallnate/once": "2", "agent-base": "6", "debug": "4" }, "engines": { "node": ">= 6" } }, "node_modules/http-proxy-agent/node_modules/debug": { "version": "4.3.4", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", "dependencies": { "ms": "2.1.2" }, "engines": { "node": ">=6.0" }, "peerDependenciesMeta": { "supports-color": { "optional": true } } }, "node_modules/http-proxy-agent/node_modules/ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, "node_modules/https-proxy-agent": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", "dependencies": { "agent-base": "6", "debug": "4" }, "engines": { "node": ">= 6" } }, "node_modules/https-proxy-agent/node_modules/debug": { "version": "4.3.4", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", "dependencies": { "ms": "2.1.2" }, "engines": { "node": ">=6.0" }, "peerDependenciesMeta": { "supports-color": { "optional": true } } }, "node_modules/https-proxy-agent/node_modules/ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, "node_modules/humanize-ms": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz", "integrity": "sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==", "dependencies": { "ms": "^2.0.0" } }, "node_modules/iconv-lite": { "version": "0.4.24", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", "dependencies": { "safer-buffer": ">= 2.1.2 < 3" }, "engines": { "node": ">=0.10.0" } }, "node_modules/ignore-by-default": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz", "integrity": "sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==", "dev": true }, "node_modules/imurmurhash": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", "engines": { "node": ">=0.8.19" } }, "node_modules/indent-string": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", "engines": { "node": ">=8" } }, "node_modules/inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", "dependencies": { "once": "^1.3.0", "wrappy": "1" } }, "node_modules/inherits": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" }, "node_modules/internal-slot": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.5.tgz", "integrity": "sha512-Y+R5hJrzs52QCG2laLn4udYVnxsfny9CpOhNhUvk/SSSVyF6T27FzRbF0sroPidSu3X8oEAkOn2K804mjpt6UQ==", "dependencies": { "get-intrinsic": "^1.2.0", "has": "^1.0.3", "side-channel": "^1.0.4" }, "engines": { "node": ">= 0.4" } }, "node_modules/ip": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ip/-/ip-2.0.0.tgz", "integrity": "sha512-WKa+XuLG1A1R0UWhl2+1XQSi+fZWMsYKffMZTTYsiZaUD8k2yDAj5atimTUD2TZkyCkNEeYE5NhFZmupOGtjYQ==" }, "node_modules/ipaddr.js": { "version": "1.9.1", "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", "engines": { "node": ">= 0.10" } }, "node_modules/is-arguments": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz", "integrity": "sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==", "dependencies": { "call-bind": "^1.0.2", "has-tostringtag": "^1.0.0" }, "engines": { "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/is-array-buffer": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.2.tgz", "integrity": "sha512-y+FyyR/w8vfIRq4eQcM1EYgSTnmHXPqaF+IgzgraytCFq5Xh8lllDVmAZolPJiZttZLeFSINPYMaEJ7/vWUa1w==", "dependencies": { "call-bind": "^1.0.2", "get-intrinsic": "^1.2.0", "is-typed-array": "^1.1.10" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/is-bigint": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", "dependencies": { "has-bigints": "^1.0.1" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/is-binary-path": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", "dev": true, "dependencies": { "binary-extensions": "^2.0.0" }, "engines": { "node": ">=8" } }, "node_modules/is-boolean-object": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", "dependencies": { "call-bind": "^1.0.2", "has-tostringtag": "^1.0.0" }, "engines": { "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/is-callable": { "version": "1.2.7", "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", "engines": { "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/is-date-object": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", "dependencies": { "has-tostringtag": "^1.0.0" }, "engines": { "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", "dev": true, "engines": { "node": ">=0.10.0" } }, "node_modules/is-glob": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", "dev": true, "dependencies": { "is-extglob": "^2.1.1" }, "engines": { "node": ">=0.10.0" } }, "node_modules/is-lambda": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/is-lambda/-/is-lambda-1.0.1.tgz", "integrity": "sha512-z7CMFGNrENq5iFB9Bqo64Xk6Y9sg+epq1myIcdHaGnbMTYOxvzsEtdYqQUylB7LxfkvgrrjP32T6Ywciio9UIQ==" }, "node_modules/is-map": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.2.tgz", "integrity": "sha512-cOZFQQozTha1f4MxLFzlgKYPTyj26picdZTx82hbc/Xf4K/tZOOXSCkMvU4pKioRXGDLJRn0GM7Upe7kR721yg==", "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/is-number": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", "dev": true, "engines": { "node": ">=0.12.0" } }, "node_modules/is-number-object": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz", "integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==", "dependencies": { "has-tostringtag": "^1.0.0" }, "engines": { "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/is-regex": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", "dependencies": { "call-bind": "^1.0.2", "has-tostringtag": "^1.0.0" }, "engines": { "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/is-set": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.2.tgz", "integrity": "sha512-+2cnTEZeY5z/iXGbLhPrOAaK/Mau5k5eXq9j14CpRTftq0pAJu2MwVRSZhyZWBzx3o6X795Lz6Bpb6R0GKf37g==", "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/is-shared-array-buffer": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz", "integrity": "sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==", "dependencies": { "call-bind": "^1.0.2" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/is-string": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", "dependencies": { "has-tostringtag": "^1.0.0" }, "engines": { "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/is-symbol": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", "dependencies": { "has-symbols": "^1.0.2" }, "engines": { "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/is-typed-array": { "version": "1.1.10", "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.10.tgz", "integrity": "sha512-PJqgEHiWZvMpaFZ3uTc8kHPM4+4ADTlDniuQL7cU/UDA0Ql7F70yGfHph3cLNe+c9toaigv+DFzTJKhc2CtO6A==", "dependencies": { "available-typed-arrays": "^1.0.5", "call-bind": "^1.0.2", "for-each": "^0.3.3", "gopd": "^1.0.1", "has-tostringtag": "^1.0.0" }, "engines": { "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/is-weakmap": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.1.tgz", "integrity": "sha512-NSBR4kH5oVj1Uwvv970ruUkCV7O1mzgVFO4/rev2cLRda9Tm9HrL70ZPut4rOHgY0FNrUu9BCbXA2sdQ+x0chA==", "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/is-weakset": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.2.tgz", "integrity": "sha512-t2yVvttHkQktwnNNmBQ98AhENLdPUTDTE21uPqAQ0ARwQfGeQKRVS0NNurH7bTf7RrvcVn1OOge45CnBeHCSmg==", "dependencies": { "call-bind": "^1.0.2", "get-intrinsic": "^1.1.1" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/isarray": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==" }, "node_modules/js-levenshtein": { "version": "1.1.6", "resolved": "https://registry.npmjs.org/js-levenshtein/-/js-levenshtein-1.1.6.tgz", "integrity": "sha512-X2BB11YZtrRqY4EnQcLX5Rh373zbK4alC1FW7D7MBhL2gtcC17cTnr6DmfHZeS0s2rTHjUTMMHfG7gO8SSdw+g==", "engines": { "node": ">=0.10.0" } }, "node_modules/lodash.sortby": { "version": "4.7.0", "resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz", "integrity": "sha512-HDWXG8isMntAyRF5vZ7xKuEvOhT4AhlRt/3czTSjvGUxjYCBVRQY48ViDHyfYz9VIoBkW4TMGQNapx+l3RUwdA==" }, "node_modules/loglevel": { "version": "1.8.1", "resolved": "https://registry.npmjs.org/loglevel/-/loglevel-1.8.1.tgz", "integrity": "sha512-tCRIJM51SHjAayKwC+QAg8hT8vg6z7GSgLJKGvzuPb1Wc+hLzqtuVLxp6/HzSPOozuK+8ErAhy7U/sVzw8Dgfg==", "engines": { "node": ">= 0.6.0" }, "funding": { "type": "tidelift", "url": "https://tidelift.com/funding/github/npm/loglevel" } }, "node_modules/long": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz", "integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA==" }, "node_modules/lru-cache": { "version": "7.18.3", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", "engines": { "node": ">=12" } }, "node_modules/make-error": { "version": "1.3.6", "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", "dev": true }, "node_modules/make-fetch-happen": { "version": "11.0.3", "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-11.0.3.tgz", "integrity": "sha512-oPLh5m10lRNNZDjJ2kP8UpboUx2uFXVaVweVe/lWut4iHWcQEmfqSVJt2ihZsFI8HbpwyyocaXbCAWf0g1ukIA==", "dependencies": { "agentkeepalive": "^4.2.1", "cacache": "^17.0.0", "http-cache-semantics": "^4.1.1", "http-proxy-agent": "^5.0.0", "https-proxy-agent": "^5.0.0", "is-lambda": "^1.0.1", "lru-cache": "^7.7.1", "minipass": "^4.0.0", "minipass-fetch": "^3.0.0", "minipass-flush": "^1.0.5", "minipass-pipeline": "^1.2.4", "negotiator": "^0.6.3", "promise-retry": "^2.0.1", "socks-proxy-agent": "^7.0.0", "ssri": "^10.0.0" }, "engines": { "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, "node_modules/media-typer": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", "engines": { "node": ">= 0.6" } }, "node_modules/merge-descriptors": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", "integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==" }, "node_modules/methods": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", "engines": { "node": ">= 0.6" } }, "node_modules/mime": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", "bin": { "mime": "cli.js" }, "engines": { "node": ">=4" } }, "node_modules/mime-db": { "version": "1.52.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", "engines": { "node": ">= 0.6" } }, "node_modules/mime-types": { "version": "2.1.35", "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", "dependencies": { "mime-db": "1.52.0" }, "engines": { "node": ">= 0.6" } }, "node_modules/minimatch": { "version": "5.1.6", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", "dependencies": { "brace-expansion": "^2.0.1" }, "engines": { "node": ">=10" } }, "node_modules/minipass": { "version": "4.2.5", "resolved": "https://registry.npmjs.org/minipass/-/minipass-4.2.5.tgz", "integrity": "sha512-+yQl7SX3bIT83Lhb4BVorMAHVuqsskxRdlmO9kTpyukp8vsm2Sn/fUOV9xlnG8/a5JsypJzap21lz/y3FBMJ8Q==", "engines": { "node": ">=8" } }, "node_modules/minipass-collect": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/minipass-collect/-/minipass-collect-1.0.2.tgz", "integrity": "sha512-6T6lH0H8OG9kITm/Jm6tdooIbogG9e0tLgpY6mphXSm/A9u8Nq1ryBG+Qspiub9LjWlBPsPS3tWQ/Botq4FdxA==", "dependencies": { "minipass": "^3.0.0" }, "engines": { "node": ">= 8" } }, "node_modules/minipass-collect/node_modules/minipass": { "version": "3.3.6", "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", "dependencies": { "yallist": "^4.0.0" }, "engines": { "node": ">=8" } }, "node_modules/minipass-fetch": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/minipass-fetch/-/minipass-fetch-3.0.1.tgz", "integrity": "sha512-t9/wowtf7DYkwz8cfMSt0rMwiyNIBXf5CKZ3S5ZMqRqMYT0oLTp0x1WorMI9WTwvaPg21r1JbFxJMum8JrLGfw==", "dependencies": { "minipass": "^4.0.0", "minipass-sized": "^1.0.3", "minizlib": "^2.1.2" }, "engines": { "node": "^14.17.0 || ^16.13.0 || >=18.0.0" }, "optionalDependencies": { "encoding": "^0.1.13" } }, "node_modules/minipass-flush": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/minipass-flush/-/minipass-flush-1.0.5.tgz", "integrity": "sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw==", "dependencies": { "minipass": "^3.0.0" }, "engines": { "node": ">= 8" } }, "node_modules/minipass-flush/node_modules/minipass": { "version": "3.3.6", "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", "dependencies": { "yallist": "^4.0.0" }, "engines": { "node": ">=8" } }, "node_modules/minipass-pipeline": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/minipass-pipeline/-/minipass-pipeline-1.2.4.tgz", "integrity": "sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A==", "dependencies": { "minipass": "^3.0.0" }, "engines": { "node": ">=8" } }, "node_modules/minipass-pipeline/node_modules/minipass": { "version": "3.3.6", "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", "dependencies": { "yallist": "^4.0.0" }, "engines": { "node": ">=8" } }, "node_modules/minipass-sized": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/minipass-sized/-/minipass-sized-1.0.3.tgz", "integrity": "sha512-MbkQQ2CTiBMlA2Dm/5cY+9SWFEN8pzzOXi6rlM5Xxq0Yqbda5ZQy9sU75a673FE9ZK0Zsbr6Y5iP6u9nktfg2g==", "dependencies": { "minipass": "^3.0.0" }, "engines": { "node": ">=8" } }, "node_modules/minipass-sized/node_modules/minipass": { "version": "3.3.6", "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", "dependencies": { "yallist": "^4.0.0" }, "engines": { "node": ">=8" } }, "node_modules/minizlib": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", "dependencies": { "minipass": "^3.0.0", "yallist": "^4.0.0" }, "engines": { "node": ">= 8" } }, "node_modules/minizlib/node_modules/minipass": { "version": "3.3.6", "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", "dependencies": { "yallist": "^4.0.0" }, "engines": { "node": ">=8" } }, "node_modules/mkdirp": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", "bin": { "mkdirp": "bin/cmd.js" }, "engines": { "node": ">=10" } }, "node_modules/ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" }, "node_modules/negotiator": { "version": "0.6.3", "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", "engines": { "node": ">= 0.6" } }, "node_modules/node-abort-controller": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/node-abort-controller/-/node-abort-controller-3.1.1.tgz", "integrity": "sha512-AGK2yQKIjRuqnc6VkX2Xj5d+QW8xZ87pa1UK6yA6ouUyuxfHuMP6umE5QK7UmTeOAymo+Zx1Fxiuw9rVx8taHQ==" }, "node_modules/node-fetch": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.9.tgz", "integrity": "sha512-DJm/CJkZkRjKKj4Zi4BsKVZh3ValV5IR5s7LVZnW+6YMh0W1BfNA8XSs6DLMGYlId5F3KnA70uu2qepcR08Qqg==", "dependencies": { "whatwg-url": "^5.0.0" }, "engines": { "node": "4.x || >=6.0.0" }, "peerDependencies": { "encoding": "^0.1.0" }, "peerDependenciesMeta": { "encoding": { "optional": true } } }, "node_modules/nodemon": { "version": "2.0.21", "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-2.0.21.tgz", "integrity": "sha512-djN/n2549DUtY33S7o1djRCd7dEm0kBnj9c7S9XVXqRUbuggN1MZH/Nqa+5RFQr63Fbefq37nFXAE9VU86yL1A==", "dev": true, "dependencies": { "chokidar": "^3.5.2", "debug": "^3.2.7", "ignore-by-default": "^1.0.1", "minimatch": "^3.1.2", "pstree.remy": "^1.1.8", "semver": "^5.7.1", "simple-update-notifier": "^1.0.7", "supports-color": "^5.5.0", "touch": "^3.1.0", "undefsafe": "^2.0.5" }, "bin": { "nodemon": "bin/nodemon.js" }, "engines": { "node": ">=8.10.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/nodemon" } }, "node_modules/nodemon/node_modules/brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "dev": true, "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "node_modules/nodemon/node_modules/debug": { "version": "3.2.7", "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", "dev": true, "dependencies": { "ms": "^2.1.1" } }, "node_modules/nodemon/node_modules/has-flag": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", "dev": true, "engines": { "node": ">=4" } }, "node_modules/nodemon/node_modules/minimatch": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, "dependencies": { "brace-expansion": "^1.1.7" }, "engines": { "node": "*" } }, "node_modules/nodemon/node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "dev": true }, "node_modules/nodemon/node_modules/semver": { "version": "5.7.1", "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", "dev": true, "bin": { "semver": "bin/semver" } }, "node_modules/nodemon/node_modules/supports-color": { "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", "dev": true, "dependencies": { "has-flag": "^3.0.0" }, "engines": { "node": ">=4" } }, "node_modules/nopt": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/nopt/-/nopt-1.0.10.tgz", "integrity": "sha512-NWmpvLSqUrgrAC9HCuxEvb+PSloHpqVu+FqcO4eeF2h5qYRhA7ev6KvelyQAKtegUbC6RypJnlEOhd8vloNKYg==", "dev": true, "dependencies": { "abbrev": "1" }, "bin": { "nopt": "bin/nopt.js" }, "engines": { "node": "*" } }, "node_modules/normalize-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", "dev": true, "engines": { "node": ">=0.10.0" } }, "node_modules/object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", "engines": { "node": ">=0.10.0" } }, "node_modules/object-inspect": { "version": "1.12.3", "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.3.tgz", "integrity": "sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==", "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/object-is": { "version": "1.1.5", "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.5.tgz", "integrity": "sha512-3cyDsyHgtmi7I7DfSSI2LDp6SK2lwvtbg0p0R1e0RvTqF5ceGx+K2dfSjm1bKDMVCFEDAQvy+o8c6a7VujOddw==", "dependencies": { "call-bind": "^1.0.2", "define-properties": "^1.1.3" }, "engines": { "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/object-keys": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", "engines": { "node": ">= 0.4" } }, "node_modules/object.assign": { "version": "4.1.4", "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.4.tgz", "integrity": "sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ==", "dependencies": { "call-bind": "^1.0.2", "define-properties": "^1.1.4", "has-symbols": "^1.0.3", "object-keys": "^1.1.1" }, "engines": { "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/on-finished": { "version": "2.4.1", "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", "dependencies": { "ee-first": "1.1.1" }, "engines": { "node": ">= 0.8" } }, "node_modules/once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", "dependencies": { "wrappy": "1" } }, "node_modules/p-map": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==", "dependencies": { "aggregate-error": "^3.0.0" }, "engines": { "node": ">=10" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/parseurl": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", "engines": { "node": ">= 0.8" } }, "node_modules/path-to-regexp": { "version": "0.1.7", "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==" }, "node_modules/picomatch": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", "dev": true, "engines": { "node": ">=8.6" }, "funding": { "url": "https://github.com/sponsors/jonschlinkert" } }, "node_modules/pretty-format": { "version": "29.5.0", "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.5.0.tgz", "integrity": "sha512-V2mGkI31qdttvTFX7Mt4efOqHXqJWMu4/r66Xh3Z3BwZaPfPJgp6/gbwoujRpPUtfEF6AUUWx3Jim3GCw5g/Qw==", "dependencies": { "@jest/schemas": "^29.4.3", "ansi-styles": "^5.0.0", "react-is": "^18.0.0" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/pretty-format/node_modules/ansi-styles": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", "engines": { "node": ">=10" }, "funding": { "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, "node_modules/promise-inflight": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz", "integrity": "sha512-6zWPyEOFaQBJYcGMHBKTKJ3u6TBsnMFOIZSa6ce1e/ZrrsOlnHRHbabMjLiBYKp+n44X9eUI6VUPaukCXHuG4g==" }, "node_modules/promise-retry": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/promise-retry/-/promise-retry-2.0.1.tgz", "integrity": "sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g==", "dependencies": { "err-code": "^2.0.2", "retry": "^0.12.0" }, "engines": { "node": ">=10" } }, "node_modules/promise-retry/node_modules/retry": { "version": "0.12.0", "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", "integrity": "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==", "engines": { "node": ">= 4" } }, "node_modules/proxy-addr": { "version": "2.0.7", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", "dependencies": { "forwarded": "0.2.0", "ipaddr.js": "1.9.1" }, "engines": { "node": ">= 0.10" } }, "node_modules/pstree.remy": { "version": "1.1.8", "resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.8.tgz", "integrity": "sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==", "dev": true }, "node_modules/qs": { "version": "6.11.0", "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", "dependencies": { "side-channel": "^1.0.4" }, "engines": { "node": ">=0.6" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/range-parser": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", "engines": { "node": ">= 0.6" } }, "node_modules/raw-body": { "version": "2.5.2", "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz", "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==", "dependencies": { "bytes": "3.1.2", "http-errors": "2.0.0", "iconv-lite": "0.4.24", "unpipe": "1.0.0" }, "engines": { "node": ">= 0.8" } }, "node_modules/react-is": { "version": "18.2.0", "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==" }, "node_modules/readdirp": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", "dev": true, "dependencies": { "picomatch": "^2.2.1" }, "engines": { "node": ">=8.10.0" } }, "node_modules/regexp.prototype.flags": { "version": "1.4.3", "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.4.3.tgz", "integrity": "sha512-fjggEOO3slI6Wvgjwflkc4NFRCTZAu5CnNfBd5qOMYhWdn67nJBBu34/TkD++eeFmd8C9r9jfXJ27+nSiRkSUA==", "dependencies": { "call-bind": "^1.0.2", "define-properties": "^1.1.3", "functions-have-names": "^1.2.2" }, "engines": { "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/retry": { "version": "0.13.1", "resolved": "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz", "integrity": "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==", "engines": { "node": ">= 4" } }, "node_modules/safe-buffer": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", "funding": [ { "type": "github", "url": "https://github.com/sponsors/feross" }, { "type": "patreon", "url": "https://www.patreon.com/feross" }, { "type": "consulting", "url": "https://feross.org/support" } ] }, "node_modules/safer-buffer": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" }, "node_modules/semver": { "version": "7.3.8", "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", "dependencies": { "lru-cache": "^6.0.0" }, "bin": { "semver": "bin/semver.js" }, "engines": { "node": ">=10" } }, "node_modules/semver/node_modules/lru-cache": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", "dependencies": { "yallist": "^4.0.0" }, "engines": { "node": ">=10" } }, "node_modules/send": { "version": "0.18.0", "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz", "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==", "dependencies": { "debug": "2.6.9", "depd": "2.0.0", "destroy": "1.2.0", "encodeurl": "~1.0.2", "escape-html": "~1.0.3", "etag": "~1.8.1", "fresh": "0.5.2", "http-errors": "2.0.0", "mime": "1.6.0", "ms": "2.1.3", "on-finished": "2.4.1", "range-parser": "~1.2.1", "statuses": "2.0.1" }, "engines": { "node": ">= 0.8.0" } }, "node_modules/send/node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" }, "node_modules/serve-static": { "version": "1.15.0", "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz", "integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==", "dependencies": { "encodeurl": "~1.0.2", "escape-html": "~1.0.3", "parseurl": "~1.3.3", "send": "0.18.0" }, "engines": { "node": ">= 0.8.0" } }, "node_modules/setprototypeof": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" }, "node_modules/sha.js": { "version": "2.4.11", "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==", "dependencies": { "inherits": "^2.0.1", "safe-buffer": "^5.0.1" }, "bin": { "sha.js": "bin.js" } }, "node_modules/side-channel": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", "dependencies": { "call-bind": "^1.0.0", "get-intrinsic": "^1.0.2", "object-inspect": "^1.9.0" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/simple-update-notifier": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-1.1.0.tgz", "integrity": "sha512-VpsrsJSUcJEseSbMHkrsrAVSdvVS5I96Qo1QAQ4FxQ9wXFcB+pjj7FB7/us9+GcgfW4ziHtYMc1J0PLczb55mg==", "dev": true, "dependencies": { "semver": "~7.0.0" }, "engines": { "node": ">=8.10.0" } }, "node_modules/simple-update-notifier/node_modules/semver": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/semver/-/semver-7.0.0.tgz", "integrity": "sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A==", "dev": true, "bin": { "semver": "bin/semver.js" } }, "node_modules/smart-buffer": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==", "engines": { "node": ">= 6.0.0", "npm": ">= 3.0.0" } }, "node_modules/socks": { "version": "2.7.1", "resolved": "https://registry.npmjs.org/socks/-/socks-2.7.1.tgz", "integrity": "sha512-7maUZy1N7uo6+WVEX6psASxtNlKaNVMlGQKkG/63nEDdLOWNbiUMoLK7X4uYoLhQstau72mLgfEWcXcwsaHbYQ==", "dependencies": { "ip": "^2.0.0", "smart-buffer": "^4.2.0" }, "engines": { "node": ">= 10.13.0", "npm": ">= 3.0.0" } }, "node_modules/socks-proxy-agent": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-7.0.0.tgz", "integrity": "sha512-Fgl0YPZ902wEsAyiQ+idGd1A7rSFx/ayC1CQVMw5P+EQx2V0SgpGtf6OKFhVjPflPUl9YMmEOnmfjCdMUsygww==", "dependencies": { "agent-base": "^6.0.2", "debug": "^4.3.3", "socks": "^2.6.2" }, "engines": { "node": ">= 10" } }, "node_modules/socks-proxy-agent/node_modules/debug": { "version": "4.3.4", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", "dependencies": { "ms": "2.1.2" }, "engines": { "node": ">=6.0" }, "peerDependenciesMeta": { "supports-color": { "optional": true } } }, "node_modules/socks-proxy-agent/node_modules/ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, "node_modules/ssri": { "version": "10.0.1", "resolved": "https://registry.npmjs.org/ssri/-/ssri-10.0.1.tgz", "integrity": "sha512-WVy6di9DlPOeBWEjMScpNipeSX2jIZBGEn5Uuo8Q7aIuFEuDX0pw8RxcOjlD1TWP4obi24ki7m/13+nFpcbXrw==", "dependencies": { "minipass": "^4.0.0" }, "engines": { "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, "node_modules/statuses": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", "engines": { "node": ">= 0.8" } }, "node_modules/stop-iteration-iterator": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.0.0.tgz", "integrity": "sha512-iCGQj+0l0HOdZ2AEeBADlsRC+vsnDsZsbdSiH1yNSjcfKM7fdpCMfqAL/dwF5BLiw/XhRft/Wax6zQbhq2BcjQ==", "dependencies": { "internal-slot": "^1.0.4" }, "engines": { "node": ">= 0.4" } }, "node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dependencies": { "has-flag": "^4.0.0" }, "engines": { "node": ">=8" } }, "node_modules/tar": { "version": "6.1.13", "resolved": "https://registry.npmjs.org/tar/-/tar-6.1.13.tgz", "integrity": "sha512-jdIBIN6LTIe2jqzay/2vtYLlBHa3JF42ot3h1dW8Q0PaAG4v8rm0cvpVePtau5C6OKXGGcgO9q2AMNSWxiLqKw==", "dependencies": { "chownr": "^2.0.0", "fs-minipass": "^2.0.0", "minipass": "^4.0.0", "minizlib": "^2.1.1", "mkdirp": "^1.0.3", "yallist": "^4.0.0" }, "engines": { "node": ">=10" } }, "node_modules/tar/node_modules/fs-minipass": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", "dependencies": { "minipass": "^3.0.0" }, "engines": { "node": ">= 8" } }, "node_modules/tar/node_modules/fs-minipass/node_modules/minipass": { "version": "3.3.6", "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", "dependencies": { "yallist": "^4.0.0" }, "engines": { "node": ">=8" } }, "node_modules/to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", "dev": true, "dependencies": { "is-number": "^7.0.0" }, "engines": { "node": ">=8.0" } }, "node_modules/toidentifier": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", "engines": { "node": ">=0.6" } }, "node_modules/touch": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/touch/-/touch-3.1.0.tgz", "integrity": "sha512-WBx8Uy5TLtOSRtIq+M03/sKDrXCLHxwDcquSP2c43Le03/9serjQBIztjRz6FkJez9D/hleyAXTBGLwwZUw9lA==", "dev": true, "dependencies": { "nopt": "~1.0.10" }, "bin": { "nodetouch": "bin/nodetouch.js" } }, "node_modules/tr46": { "version": "0.0.3", "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" }, "node_modules/ts-graphviz": { "version": "1.5.5", "resolved": "https://registry.npmjs.org/ts-graphviz/-/ts-graphviz-1.5.5.tgz", "integrity": "sha512-abon0Tlcgvxcqr8x+p8QH1fTbR2R4cEXKGZfT4OJONZWah2YfqkmERb6hrr82omAc1IHwk5PlF8g4BS/ECYvwQ==", "engines": { "node": ">=14.16" }, "funding": { "type": "github", "url": "https://github.com/sponsors/kamiazya" } }, "node_modules/ts-node": { "version": "10.9.1", "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz", "integrity": "sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==", "dev": true, "dependencies": { "@cspotcode/source-map-support": "^0.8.0", "@tsconfig/node10": "^1.0.7", "@tsconfig/node12": "^1.0.7", "@tsconfig/node14": "^1.0.0", "@tsconfig/node16": "^1.0.2", "acorn": "^8.4.1", "acorn-walk": "^8.1.1", "arg": "^4.1.0", "create-require": "^1.1.0", "diff": "^4.0.1", "make-error": "^1.1.1", "v8-compile-cache-lib": "^3.0.1", "yn": "3.1.1" }, "bin": { "ts-node": "dist/bin.js", "ts-node-cwd": "dist/bin-cwd.js", "ts-node-esm": "dist/bin-esm.js", "ts-node-script": "dist/bin-script.js", "ts-node-transpile-only": "dist/bin-transpile.js", "ts-script": "dist/bin-script-deprecated.js" }, "peerDependencies": { "@swc/core": ">=1.2.50", "@swc/wasm": ">=1.2.50", "@types/node": "*", "typescript": ">=2.7" }, "peerDependenciesMeta": { "@swc/core": { "optional": true }, "@swc/wasm": { "optional": true } } }, "node_modules/tslib": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==" }, "node_modules/type-is": { "version": "1.6.18", "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", "dependencies": { "media-typer": "0.3.0", "mime-types": "~2.1.24" }, "engines": { "node": ">= 0.6" } }, "node_modules/typescript": { "version": "4.9.5", "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==", "dev": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" }, "engines": { "node": ">=4.2.0" } }, "node_modules/undefsafe": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz", "integrity": "sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==", "dev": true }, "node_modules/unique-filename": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-3.0.0.tgz", "integrity": "sha512-afXhuC55wkAmZ0P18QsVE6kp8JaxrEokN2HGIoIVv2ijHQd419H0+6EigAFcIzXeMIkcIkNBpB3L/DXB3cTS/g==", "dependencies": { "unique-slug": "^4.0.0" }, "engines": { "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, "node_modules/unique-slug": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-4.0.0.tgz", "integrity": "sha512-WrcA6AyEfqDX5bWige/4NQfPZMtASNVxdmWR76WESYQVAACSgWcR6e9i0mofqqBxYFtL4oAxPIptY73/0YE1DQ==", "dependencies": { "imurmurhash": "^0.1.4" }, "engines": { "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, "node_modules/unpipe": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", "engines": { "node": ">= 0.8" } }, "node_modules/utils-merge": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", "engines": { "node": ">= 0.4.0" } }, "node_modules/uuid": { "version": "9.0.0", "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.0.tgz", "integrity": "sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg==", "bin": { "uuid": "dist/bin/uuid" } }, "node_modules/v8-compile-cache-lib": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", "dev": true }, "node_modules/value-or-promise": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/value-or-promise/-/value-or-promise-1.0.11.tgz", "integrity": "sha512-41BrgH+dIbCFXClcSapVs5M6GkENd3gQOJpEfPDNa71LsUGMXDL0jMWpI/Rh7WhX+Aalfz2TTS3Zt5pUsbnhLg==", "engines": { "node": ">=12" } }, "node_modules/vary": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", "engines": { "node": ">= 0.8" } }, "node_modules/webidl-conversions": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" }, "node_modules/whatwg-mimetype": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-3.0.0.tgz", "integrity": "sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q==", "engines": { "node": ">=12" } }, "node_modules/whatwg-url": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", "dependencies": { "tr46": "~0.0.3", "webidl-conversions": "^3.0.0" } }, "node_modules/which-boxed-primitive": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", "dependencies": { "is-bigint": "^1.0.1", "is-boolean-object": "^1.1.0", "is-number-object": "^1.0.4", "is-string": "^1.0.5", "is-symbol": "^1.0.3" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/which-collection": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.1.tgz", "integrity": "sha512-W8xeTUwaln8i3K/cY1nGXzdnVZlidBcagyNFtBdD5kxnb4TvGKR7FfSIS3mYpwWS1QUCutfKz8IY8RjftB0+1A==", "dependencies": { "is-map": "^2.0.1", "is-set": "^2.0.1", "is-weakmap": "^2.0.1", "is-weakset": "^2.0.1" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/which-typed-array": { "version": "1.1.9", "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.9.tgz", "integrity": "sha512-w9c4xkx6mPidwp7180ckYWfMmvxpjlZuIudNtDf4N/tTAUB8VJbX25qZoAsrtGuYNnGw3pa0AXgbGKRB8/EceA==", "dependencies": { "available-typed-arrays": "^1.0.5", "call-bind": "^1.0.2", "for-each": "^0.3.3", "gopd": "^1.0.1", "has-tostringtag": "^1.0.0", "is-typed-array": "^1.1.10" }, "engines": { "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" }, "node_modules/xss": { "version": "1.0.14", "resolved": "https://registry.npmjs.org/xss/-/xss-1.0.14.tgz", "integrity": "sha512-og7TEJhXvn1a7kzZGQ7ETjdQVS2UfZyTlsEdDOqvQF7GoxNfY+0YLCzBy1kPdsDDx4QuNAonQPddpsn6Xl/7sw==", "dependencies": { "commander": "^2.20.3", "cssfilter": "0.0.10" }, "bin": { "xss": "bin/xss" }, "engines": { "node": ">= 0.10.0" } }, "node_modules/yallist": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" }, "node_modules/yn": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", "dev": true, "engines": { "node": ">=6" } } } } graphene-federation-3.2.0/integration_tests/gateway/package.json000066400000000000000000000006601463304117000251200ustar00rootroot00000000000000{ "name": "federation", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "start": "nodemon --ext ts --exec 'ts-node' src/index.ts" }, "author": "", "license": "ISC", "dependencies": { "@apollo/gateway": "^2.3.4", "apollo-server": "^3.12.0", "graphql": "^16.6.0" }, "devDependencies": { "nodemon": "^2.0.21", "ts-node": "^10.9.1", "typescript": "^4.9.5" } } graphene-federation-3.2.0/integration_tests/gateway/src/000077500000000000000000000000001463304117000234175ustar00rootroot00000000000000graphene-federation-3.2.0/integration_tests/gateway/src/index.ts000066400000000000000000000015221463304117000250760ustar00rootroot00000000000000import { ApolloServer } from 'apollo-server' import { ApolloGateway, IntrospectAndCompose } from '@apollo/gateway' const serviceA_url: string = 'http://service_a:5000/graphql'; const serviceB_url: string = 'http://service_b:5000/graphql'; const serviceC_url: string = 'http://service_c:5000/graphql'; const serviceD_url: string = 'http://service_d:5000/graphql'; const gateway = new ApolloGateway({ supergraphSdl: new IntrospectAndCompose({ subgraphs: [ { name: 'service_a', url: serviceA_url }, { name: 'service_b', url: serviceB_url }, { name: 'service_c', url: serviceC_url }, { name: 'service_d', url: serviceD_url }, ], }), }); const server = new ApolloServer({ gateway, }); server.listen(3000).then(({ url }) => { console.log(`🚀 Server ready at ${url}`); }); graphene-federation-3.2.0/integration_tests/service_a/000077500000000000000000000000001463304117000231275ustar00rootroot00000000000000graphene-federation-3.2.0/integration_tests/service_a/Dockerfile000066400000000000000000000002711463304117000251210ustar00rootroot00000000000000FROM python:3.10-alpine RUN apk update \ && apk add curl WORKDIR project COPY ./requirements.txt ./ RUN pip install -r requirements.txt EXPOSE 5000 CMD [ "python", "./src/app.py"] graphene-federation-3.2.0/integration_tests/service_a/requirements.txt000066400000000000000000000001141463304117000264070ustar00rootroot00000000000000graphene>=3.1 MarkupSafe==2.0.1 flask==1.1.4 graphql-server[flask]==3.0.0b5 graphene-federation-3.2.0/integration_tests/service_a/src/000077500000000000000000000000001463304117000237165ustar00rootroot00000000000000graphene-federation-3.2.0/integration_tests/service_a/src/__init__.py000066400000000000000000000000001463304117000260150ustar00rootroot00000000000000graphene-federation-3.2.0/integration_tests/service_a/src/app.py000066400000000000000000000005401463304117000250470ustar00rootroot00000000000000from flask import Flask from graphql_server.flask import GraphQLView from schema import schema app = Flask(__name__) app.debug = True app.add_url_rule( "/graphql", view_func=GraphQLView.as_view( "graphql", schema=schema.graphql_schema, graphiql=True, ), ) if __name__ == "__main__": app.run(host="0.0.0.0") graphene-federation-3.2.0/integration_tests/service_a/src/schema.py000066400000000000000000000036731463304117000255410ustar00rootroot00000000000000from graphene import Field, Int, Interface, List, NonNull, ObjectType, String from graphene_federation import FederationVersion, build_schema, extends, external, key class DecoratedText(Interface): color = Int(required=True) @key(fields="id") @extends class FileNode(ObjectType): id = external(Int(required=True)) @key(fields="id") @extends class FunnyText(ObjectType): class Meta: interfaces = (DecoratedText,) id = external(Int(required=True)) def resolve_color(self, info, **kwargs): return self.id + 2 class FunnyTextAnother(ObjectType): """ To test @extend on types with same prefix """ class Meta: interfaces = (DecoratedText,) id = Int(required=True) def resolve_color(self, info, **kwargs): return self.id + 2 @key(fields="primaryEmail") @extends class User(ObjectType): primaryEmail = external(String()) class Post(ObjectType): id = Int(required=True) title = String(required=True) text = Field(lambda: FunnyText) files = List(NonNull(FileNode)) author = Field(lambda: User) class Query(ObjectType): goodbye = String() posts = List(NonNull(Post)) def resolve_posts(root, info): return [ Post(id=1, title="title1", text=FunnyText(id=1), files=[FileNode(id=1)]), Post( id=2, title="title2", text=FunnyText(id=2), files=[FileNode(id=2), FileNode(id=3)], ), Post(id=3, title="title3", text=FunnyText(id=3)), Post( id=4, title="title4", text=FunnyText(id=4), author=User(primaryEmail="frank@frank.com"), ), ] def resolve_goodbye(root, info): return "See ya!" schema = build_schema( query=Query, types=[FunnyTextAnother], federation_version=FederationVersion.VERSION_1_0, auto_camelcase=False, ) graphene-federation-3.2.0/integration_tests/service_b/000077500000000000000000000000001463304117000231305ustar00rootroot00000000000000graphene-federation-3.2.0/integration_tests/service_b/Dockerfile000066400000000000000000000002711463304117000251220ustar00rootroot00000000000000FROM python:3.10-alpine RUN apk update \ && apk add curl WORKDIR project COPY ./requirements.txt ./ RUN pip install -r requirements.txt EXPOSE 5000 CMD [ "python", "./src/app.py"] graphene-federation-3.2.0/integration_tests/service_b/requirements.txt000066400000000000000000000001141463304117000264100ustar00rootroot00000000000000graphene>=3.1 MarkupSafe==2.0.1 flask==1.1.4 graphql-server[flask]==3.0.0b5 graphene-federation-3.2.0/integration_tests/service_b/src/000077500000000000000000000000001463304117000237175ustar00rootroot00000000000000graphene-federation-3.2.0/integration_tests/service_b/src/__init__.py000066400000000000000000000000001463304117000260160ustar00rootroot00000000000000graphene-federation-3.2.0/integration_tests/service_b/src/app.py000066400000000000000000000005401463304117000250500ustar00rootroot00000000000000from flask import Flask from graphql_server.flask import GraphQLView from schema import schema app = Flask(__name__) app.debug = True app.add_url_rule( "/graphql", view_func=GraphQLView.as_view( "graphql", schema=schema.graphql_schema, graphiql=True, ), ) if __name__ == "__main__": app.run(host="0.0.0.0") graphene-federation-3.2.0/integration_tests/service_b/src/schema.py000066400000000000000000000035131463304117000255330ustar00rootroot00000000000000from graphene import Int, Interface, Mutation, ObjectType, String from graphene_federation import FederationVersion, build_schema, key class TextInterface(Interface): id = Int(required=True) body = String(required=True) @key(fields="id") class FunnyText(ObjectType): class Meta: interfaces = (TextInterface,) def __resolve_reference(self, info, **kwargs): return FunnyText(id=self.id, body=f"funny_text_{self.id}") @key(fields="id") class FileNode(ObjectType): id = Int(required=True) name = String(required=True) def __resolve_reference(self, info, **kwargs): # todo test raise exception here return FileNode(id=self.id, name=f"file_{self.id}") @key("id") @key("primary_email") class User(ObjectType): id = Int(required=True) primary_email = String() age = Int() def resolve_age(self, info): return 17 def __resolve_reference(self, info, **kwargs): if self.id is not None: return User(id=self.id, primary_email=f"name_{self.id}@gmail.com") user_id = ( 1001 if self.primary_email == "frank@frank.com" else hash(self.primary_email) % 10000000 ) return User(id=user_id, primary_email=self.primary_email) # to test that @key applied only to FileNode, but not to FileNodeAnother class FileNodeAnother(ObjectType): id = Int(required=True) name = String(required=True) class FunnyMutation(Mutation): result = String(required=True) @classmethod def mutate(cls, root, info, **data): return FunnyMutation(result="Funny") class Mutation(ObjectType): funny_mutation = FunnyMutation.Field() types = [FileNode, FunnyText, FileNodeAnother, User] schema = build_schema( mutation=Mutation, types=types, federation_version=FederationVersion.VERSION_1_0 ) graphene-federation-3.2.0/integration_tests/service_c/000077500000000000000000000000001463304117000231315ustar00rootroot00000000000000graphene-federation-3.2.0/integration_tests/service_c/Dockerfile000066400000000000000000000002711463304117000251230ustar00rootroot00000000000000FROM python:3.10-alpine RUN apk update \ && apk add curl WORKDIR project COPY ./requirements.txt ./ RUN pip install -r requirements.txt EXPOSE 5000 CMD [ "python", "./src/app.py"] graphene-federation-3.2.0/integration_tests/service_c/requirements.txt000066400000000000000000000001141463304117000264110ustar00rootroot00000000000000graphene>=3.1 MarkupSafe==2.0.1 flask==1.1.4 graphql-server[flask]==3.0.0b5 graphene-federation-3.2.0/integration_tests/service_c/src/000077500000000000000000000000001463304117000237205ustar00rootroot00000000000000graphene-federation-3.2.0/integration_tests/service_c/src/__init__.py000066400000000000000000000000001463304117000260170ustar00rootroot00000000000000graphene-federation-3.2.0/integration_tests/service_c/src/app.py000066400000000000000000000005401463304117000250510ustar00rootroot00000000000000from flask import Flask from graphql_server.flask import GraphQLView from schema import schema app = Flask(__name__) app.debug = True app.add_url_rule( "/graphql", view_func=GraphQLView.as_view( "graphql", schema=schema.graphql_schema, graphiql=True, ), ) if __name__ == "__main__": app.run(host="0.0.0.0") graphene-federation-3.2.0/integration_tests/service_c/src/schema.py000066400000000000000000000033331463304117000255340ustar00rootroot00000000000000from graphene import Field, Int, List, NonNull, ObjectType, String from graphene_federation import ( FederationVersion, build_schema, extends, external, key, provides, requires, ) @key(fields="id") @extends class User(ObjectType): id = external(Int(required=True)) primary_email = external(String()) uppercase_email = requires(String(), fields="primaryEmail") age = external(Int()) def resolve_uppercase_email(self, info): return self.primary_email.upper() if self.primary_email else self.primary_email def resolve_age(self, info): return 18 @key(fields="id") class Article(ObjectType): id = Int(required=True) text = String(required=True) author = Field(lambda: User) def __resolve_reference(self, info, **kwargs): return Article(id=self.id, text=f"text_{self.id}") class ArticleThatProvideAuthorAge(ObjectType): """ should not contain other graphene-federation decorators to proper test test-case """ id = Int(required=True) text = String(required=True) author = provides(Field(User), fields="age") def __resolve_reference(self, info, **kwargs): return Article(id=self.id, text=f"text_{self.id}") class Query(ObjectType): articles = List(NonNull(lambda: Article)) articles_with_author_age_provide = List( NonNull(lambda: ArticleThatProvideAuthorAge) ) def resolve_articles(self, info): return [Article(id=1, text="some text", author=User(id=5))] def resolve_articles_with_author_age_provide(self, info): return [ArticleThatProvideAuthorAge(id=1, text="some text", author=User(id=5))] schema = build_schema(Query, federation_version=FederationVersion.VERSION_1_0) graphene-federation-3.2.0/integration_tests/service_d/000077500000000000000000000000001463304117000231325ustar00rootroot00000000000000graphene-federation-3.2.0/integration_tests/service_d/Dockerfile000066400000000000000000000002711463304117000251240ustar00rootroot00000000000000FROM python:3.10-alpine RUN apk update \ && apk add curl WORKDIR project COPY ./requirements.txt ./ RUN pip install -r requirements.txt EXPOSE 5000 CMD [ "python", "./src/app.py"] graphene-federation-3.2.0/integration_tests/service_d/requirements.txt000066400000000000000000000001141463304117000264120ustar00rootroot00000000000000graphene>=3.1 MarkupSafe==2.0.1 flask==1.1.4 graphql-server[flask]==3.0.0b5 graphene-federation-3.2.0/integration_tests/service_d/src/000077500000000000000000000000001463304117000237215ustar00rootroot00000000000000graphene-federation-3.2.0/integration_tests/service_d/src/__init__.py000066400000000000000000000000001463304117000260200ustar00rootroot00000000000000graphene-federation-3.2.0/integration_tests/service_d/src/app.py000066400000000000000000000005401463304117000250520ustar00rootroot00000000000000from flask import Flask from graphql_server.flask import GraphQLView from schema import schema app = Flask(__name__) app.debug = True app.add_url_rule( "/graphql", view_func=GraphQLView.as_view( "graphql", schema=schema.graphql_schema, graphiql=True, ), ) if __name__ == "__main__": app.run(host="0.0.0.0") graphene-federation-3.2.0/integration_tests/service_d/src/schema.py000066400000000000000000000011611463304117000255320ustar00rootroot00000000000000from graphene import Field, Int, ObjectType from graphene_federation import FederationVersion, build_schema, extends, external, key """ Alphabet order - matters Y should be just after X in sdl https://github.com/preply/graphene-federation/issues/26#issuecomment-572127271 """ @key(fields="id") @extends class Article(ObjectType): id = external(Int(required=True)) class X(ObjectType): x_article = Field(Article) class Y(ObjectType): id = Int(required=True) class Query(ObjectType): x = Field(X) y = Field(Y) schema = build_schema(query=Query, federation_version=FederationVersion.VERSION_1_0) graphene-federation-3.2.0/integration_tests/tests/000077500000000000000000000000001463304117000223315ustar00rootroot00000000000000graphene-federation-3.2.0/integration_tests/tests/Dockerfile000066400000000000000000000003241463304117000243220ustar00rootroot00000000000000FROM python:3.10-slim RUN apt-get update \ && apt-get install -y git gcc \ && rm -rf /var/lib/apt/lists/* WORKDIR project COPY requirements.txt ./ RUN pip install -r requirements.txt CMD [ "pytest", "-svv"] graphene-federation-3.2.0/integration_tests/tests/requirements.txt000066400000000000000000000000371463304117000256150ustar00rootroot00000000000000requests==2.28.0 pytest==7.1.2 graphene-federation-3.2.0/integration_tests/tests/tests/000077500000000000000000000000001463304117000234735ustar00rootroot00000000000000graphene-federation-3.2.0/integration_tests/tests/tests/__init__.py000066400000000000000000000000001463304117000255720ustar00rootroot00000000000000graphene-federation-3.2.0/integration_tests/tests/tests/test_main.py000066400000000000000000000124021463304117000260270ustar00rootroot00000000000000import json import requests def test_integrate_simple_schema(): query = { "query": """ query { goodbye } """, "variables": {}, } response = requests.post("http://gateway:3000/graphql/", json=query) assert response.status_code == 200 data = json.loads(response.content)["data"] assert data["goodbye"] == "See ya!" def test_external_types(): query = { "query": """ query { posts { title text { id body color } files { id name } author { id primaryEmail } } articles { id text author { id primaryEmail } } } """, "variables": {}, } response = requests.post( "http://gateway:3000/graphql/", json=query, ) assert response.status_code == 200 data = json.loads(response.content)["data"] posts = data["posts"] articles = data["articles"] assert 4 == len(posts) assert [{"id": 1, "name": "file_1"}] == posts[0]["files"] assert {"id": 1, "body": "funny_text_1", "color": 3} == posts[0]["text"] assert [{"id": 2, "name": "file_2"}, {"id": 3, "name": "file_3"}] == posts[1][ "files" ] assert {"id": 2, "body": "funny_text_2", "color": 4} == posts[1]["text"] assert posts[2]["files"] is None assert {"id": 3, "body": "funny_text_3", "color": 5} == posts[2]["text"] assert { "id": 1001, "primaryEmail": "frank@frank.com", } == posts[ 3 ]["author"] assert articles == [ { "id": 1, "text": "some text", "author": {"id": 5, "primaryEmail": "name_5@gmail.com"}, } ] def fetch_sdl(service_name="service_b"): query = { "query": """ query { _service { sdl } } """, "variables": {}, } response = requests.post(f"http://{service_name}:5000/graphql", json=query) assert response.status_code == 200 return response.json()["data"]["_service"]["sdl"] def test_key_decorator_applied_by_exact_match_only(): sdl = fetch_sdl() assert 'type FileNode @key(fields: "id")' in sdl assert 'type FileNodeAnother @key(fields: "id")' not in sdl def test_mutation_is_accessible_in_federation(): # this mutation is created in service_b mutation = """ mutation { funnyMutation { result } }""" response = requests.post("http://gateway:3000/graphql/", json={"query": mutation}) assert response.status_code == 200 assert "errors" not in response.json() assert response.json()["data"]["funnyMutation"]["result"] == "Funny" def test_multiple_key_decorators_apply_multiple_key_annotations(): sdl = fetch_sdl() assert 'type User @key(fields: "primaryEmail") @key(fields: "id")' in sdl def test_avoid_duplication_of_key_decorator(): sdl = fetch_sdl("service_a") assert 'type FileNode @key(fields: "id") @extends {' in sdl def test_requires(): query = { "query": """ query { articles { id text author { uppercaseEmail } } } """, "variables": {}, } response = requests.post( "http://gateway:3000/graphql/", json=query, ) assert response.status_code == 200 data = json.loads(response.content)["data"] articles = data["articles"] assert articles == [ {"id": 1, "text": "some text", "author": {"uppercaseEmail": "NAME_5@GMAIL.COM"}} ] def test_provides(): """ articles -> w/o provide (get age value from service b) articlesWithAuthorAgeProvide -> w/ provide (get age value from service c) :return: """ query = { "query": """ query { articles { id text author { age } } articlesWithAuthorAgeProvide { id text author { age } } } """, "variables": {}, } response = requests.post( "http://gateway:3000/graphql/", json=query, ) assert response.status_code == 200 data = json.loads(response.content)["data"] articles = data["articles"] articles_with_age_provide = data["articlesWithAuthorAgeProvide"] assert articles == [{"id": 1, "text": "some text", "author": {"age": 17}}] assert articles_with_age_provide == [ {"id": 1, "text": "some text", "author": {"age": 18}} ] graphene-federation-3.2.0/setup.cfg000066400000000000000000000001711463304117000172420ustar00rootroot00000000000000[metadata] description_file = README.md [mypy] files = **/*.py namespace_packages = True ignore_missing_imports = True graphene-federation-3.2.0/setup.py000066400000000000000000000031461463304117000171400ustar00rootroot00000000000000import os from setuptools import find_packages, setup def read(*rnames): return open(os.path.join(os.path.dirname(__file__), *rnames)).read() version = "3.2.0" tests_require = [ "pytest==7.1.2", "pytest-cov", ] dev_require = [ "black==23.12.1", "flake8==4.0.1", "mypy==0.961", ] + tests_require setup( name="graphene-federation", packages=find_packages(exclude=["tests"]), version=version, license="MIT", description="Federation implementation for graphene", long_description=(read("README.md")), long_description_content_type="text/markdown", author="Igor Kasianov", author_email="super.hang.glider@gmail.com", url="https://github.com/graphql-python/graphene-federation", download_url=f"https://github.com/graphql-python/graphene-federation/archive/{version}.tar.gz", keywords=["graphene", "graphql", "gql", "federation"], install_requires=[ "graphene>=3.1", "graphql-core>=3.1", "graphene-directives>=0.4.6", ], classifiers=[ "Development Status :: 5 - Production/Stable", "Intended Audience :: Developers", "Topic :: Software Development :: Libraries", "Programming Language :: Python :: 3.6", "Programming Language :: Python :: 3.7", "Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", ], extras_require={ "test": tests_require, "dev": dev_require, }, ) graphene-federation-3.2.0/tests/000077500000000000000000000000001463304117000165645ustar00rootroot00000000000000graphene-federation-3.2.0/tests/__init__.py000066400000000000000000000000001463304117000206630ustar00rootroot00000000000000graphene-federation-3.2.0/tests/gql/000077500000000000000000000000001463304117000173475ustar00rootroot00000000000000graphene-federation-3.2.0/tests/gql/test_annotation_corner_cases/000077500000000000000000000000001463304117000253065ustar00rootroot00000000000000test_annotate_object_with_meta_name_1.graphql000066400000000000000000000013511463304117000362660ustar00rootroot00000000000000graphene-federation-3.2.0/tests/gql/test_annotation_corner_casesextend schema @link(url: "https://specs.apollo.dev/federation/v2.7", import: ["@extends", "@external", "@key"]) type Query { a: Banana _entities(representations: [_Any!]!): [_Entity]! _service: _Service! } type Banana @extends { id: ID @external b(id: ID ): Potato } type Potato @key(fields: "id") { id: ID } union _Entity = Banana | Potato scalar _Any type _Service { sdl: String } """ A string-serialized scalar represents a set of fields that's passed to a federated directive, such as @key, @requires, or @provides """ scalar FieldSet """This string-serialized scalar represents a JWT scope""" scalar federation__Scope """This string-serialized scalar represents an authorization policy.""" scalar federation__Policytest_annotate_object_with_meta_name_2.graphql000066400000000000000000000011211463304117000362620ustar00rootroot00000000000000graphene-federation-3.2.0/tests/gql/test_annotation_corner_casesextend schema @link(url: "https://specs.apollo.dev/federation/v2.7", import: ["@extends", "@external", "@key"]) type Query { a: Banana } type Banana @extends { id: ID @external b(id: ID ): Potato } type Potato @key(fields: "id") { id: ID } """ A string-serialized scalar represents a set of fields that's passed to a federated directive, such as @key, @requires, or @provides """ scalar FieldSet """This string-serialized scalar represents a JWT scope""" scalar federation__Scope """This string-serialized scalar represents an authorization policy.""" scalar federation__Policytest_annotated_field_also_used_in_filter_1.graphql000066400000000000000000000013131463304117000372750ustar00rootroot00000000000000graphene-federation-3.2.0/tests/gql/test_annotation_corner_casesextend schema @link(url: "https://specs.apollo.dev/federation/v2.7", import: ["@extends", "@external", "@key"]) type Query { a: A _entities(representations: [_Any!]!): [_Entity]! _service: _Service! } type A @extends { id: ID @external b(id: ID ): B } type B @key(fields: "id") { id: ID } union _Entity = A | B scalar _Any type _Service { sdl: String } """ A string-serialized scalar represents a set of fields that's passed to a federated directive, such as @key, @requires, or @provides """ scalar FieldSet """This string-serialized scalar represents a JWT scope""" scalar federation__Scope """This string-serialized scalar represents an authorization policy.""" scalar federation__Policytest_annotated_field_also_used_in_filter_2.graphql000066400000000000000000000010751463304117000373030ustar00rootroot00000000000000graphene-federation-3.2.0/tests/gql/test_annotation_corner_casesextend schema @link(url: "https://specs.apollo.dev/federation/v2.7", import: ["@extends", "@external", "@key"]) type Query { a: A } type A @extends { id: ID @external b(id: ID ): B } type B @key(fields: "id") { id: ID } """ A string-serialized scalar represents a set of fields that's passed to a federated directive, such as @key, @requires, or @provides """ scalar FieldSet """This string-serialized scalar represents a JWT scope""" scalar federation__Scope """This string-serialized scalar represents an authorization policy.""" scalar federation__Policytest_camel_case_field_name_1.graphql000066400000000000000000000014501463304117000343050ustar00rootroot00000000000000graphene-federation-3.2.0/tests/gql/test_annotation_corner_casesextend schema @link(url: "https://specs.apollo.dev/federation/v2.7", import: ["@extends", "@external", "@key", "@requires"]) type Query { camel: Camel _entities(representations: [_Any!]!): [_Entity]! _service: _Service! } type Camel @key(fields: "autoCamel") @extends { autoCamel: String @external forcedCamel: String @requires(fields: "autoCamel") aSnake: String aCamel: String } union _Entity = Camel scalar _Any type _Service { sdl: String } """ A string-serialized scalar represents a set of fields that's passed to a federated directive, such as @key, @requires, or @provides """ scalar FieldSet """This string-serialized scalar represents a JWT scope""" scalar federation__Scope """This string-serialized scalar represents an authorization policy.""" scalar federation__Policytest_camel_case_field_name_2.graphql000066400000000000000000000012321463304117000343040ustar00rootroot00000000000000graphene-federation-3.2.0/tests/gql/test_annotation_corner_casesextend schema @link(url: "https://specs.apollo.dev/federation/v2.7", import: ["@extends", "@external", "@key", "@requires"]) type Query { camel: Camel } type Camel @key(fields: "autoCamel") @extends { autoCamel: String @external forcedCamel: String @requires(fields: "autoCamel") aSnake: String aCamel: String } """ A string-serialized scalar represents a set of fields that's passed to a federated directive, such as @key, @requires, or @provides """ scalar FieldSet """This string-serialized scalar represents a JWT scope""" scalar federation__Scope """This string-serialized scalar represents an authorization policy.""" scalar federation__Policytest_camel_case_field_name_without_auto_camelcase_1.graphql000066400000000000000000000014111463304117000411120ustar00rootroot00000000000000graphene-federation-3.2.0/tests/gql/test_annotation_corner_casesextend schema @link(url: "https://specs.apollo.dev/federation/v2.7", import: ["@extends", "@external", "@requires"]) type Query { camel: Camel _entities(representations: [_Any!]!): [_Entity]! _service: _Service! } type Camel @extends { auto_camel: String @external forcedCamel: String @requires(fields: "auto_camel") a_snake: String aCamel: String } union _Entity = Camel scalar _Any type _Service { sdl: String } """ A string-serialized scalar represents a set of fields that's passed to a federated directive, such as @key, @requires, or @provides """ scalar FieldSet """This string-serialized scalar represents a JWT scope""" scalar federation__Scope """This string-serialized scalar represents an authorization policy.""" scalar federation__Policytest_camel_case_field_name_without_auto_camelcase_2.graphql000066400000000000000000000011731463304117000411200ustar00rootroot00000000000000graphene-federation-3.2.0/tests/gql/test_annotation_corner_casesextend schema @link(url: "https://specs.apollo.dev/federation/v2.7", import: ["@extends", "@external", "@requires"]) type Query { camel: Camel } type Camel @extends { auto_camel: String @external forcedCamel: String @requires(fields: "auto_camel") a_snake: String aCamel: String } """ A string-serialized scalar represents a set of fields that's passed to a federated directive, such as @key, @requires, or @provides """ scalar FieldSet """This string-serialized scalar represents a JWT scope""" scalar federation__Scope """This string-serialized scalar represents an authorization policy.""" scalar federation__Policygraphene-federation-3.2.0/tests/gql/test_annotation_corner_cases/test_similar_field_name_1.graphql000066400000000000000000000015121463304117000337470ustar00rootroot00000000000000extend schema @link(url: "https://specs.apollo.dev/federation/v2.7", import: ["@extends", "@external", "@key"]) schema { query: ChatQuery } type ChatQuery { message(id: ID!): ChatMessage _entities(representations: [_Any!]!): [_Entity]! _service: _Service! } type ChatMessage { id: ID! user: ChatUser } type ChatUser @key(fields: "id") @extends { uid: ID identified: ID id: ID @external iD: ID ID: ID } union _Entity = ChatUser scalar _Any type _Service { sdl: String } """ A string-serialized scalar represents a set of fields that's passed to a federated directive, such as @key, @requires, or @provides """ scalar FieldSet """This string-serialized scalar represents a JWT scope""" scalar federation__Scope """This string-serialized scalar represents an authorization policy.""" scalar federation__Policygraphene-federation-3.2.0/tests/gql/test_annotation_corner_cases/test_similar_field_name_2.graphql000066400000000000000000000012711463304117000337520ustar00rootroot00000000000000extend schema @link(url: "https://specs.apollo.dev/federation/v2.7", import: ["@extends", "@external", "@key"]) schema { query: ChatQuery } type ChatQuery { message(id: ID!): ChatMessage } type ChatMessage { id: ID! user: ChatUser } type ChatUser @key(fields: "id") @extends { uid: ID identified: ID id: ID @external iD: ID ID: ID } """ A string-serialized scalar represents a set of fields that's passed to a federated directive, such as @key, @requires, or @provides """ scalar FieldSet """This string-serialized scalar represents a JWT scope""" scalar federation__Scope """This string-serialized scalar represents an authorization policy.""" scalar federation__Policygraphene-federation-3.2.0/tests/gql/test_annotation_corner_cases_v1/000077500000000000000000000000001463304117000257145ustar00rootroot00000000000000test_annotate_object_with_meta_name_1.graphql000066400000000000000000000007001463304117000366710ustar00rootroot00000000000000graphene-federation-3.2.0/tests/gql/test_annotation_corner_cases_v1type Query { a: Banana _entities(representations: [_Any!]!): [_Entity]! _service: _Service! } type Banana @extends { id: ID @external b(id: ID ): Potato } type Potato @key(fields: "id") { id: ID } union _Entity = Banana | Potato scalar _Any type _Service { sdl: String } """ A string-serialized scalar represents a set of fields that's passed to a federated directive, such as @key, @requires, or @provides """ scalar _FieldSettest_annotate_object_with_meta_name_2.graphql000066400000000000000000000004501463304117000366740ustar00rootroot00000000000000graphene-federation-3.2.0/tests/gql/test_annotation_corner_cases_v1type Query { a: Banana } type Banana @extends { id: ID @external b(id: ID ): Potato } type Potato @key(fields: "id") { id: ID } """ A string-serialized scalar represents a set of fields that's passed to a federated directive, such as @key, @requires, or @provides """ scalar _FieldSettest_annotated_field_also_used_in_filter_1.graphql000066400000000000000000000006421463304117000377070ustar00rootroot00000000000000graphene-federation-3.2.0/tests/gql/test_annotation_corner_cases_v1type Query { a: A _entities(representations: [_Any!]!): [_Entity]! _service: _Service! } type A @extends { id: ID @external b(id: ID ): B } type B @key(fields: "id") { id: ID } union _Entity = A | B scalar _Any type _Service { sdl: String } """ A string-serialized scalar represents a set of fields that's passed to a federated directive, such as @key, @requires, or @provides """ scalar _FieldSettest_annotated_field_also_used_in_filter_2.graphql000066400000000000000000000004241463304117000377060ustar00rootroot00000000000000graphene-federation-3.2.0/tests/gql/test_annotation_corner_cases_v1type Query { a: A } type A @extends { id: ID @external b(id: ID ): B } type B @key(fields: "id") { id: ID } """ A string-serialized scalar represents a set of fields that's passed to a federated directive, such as @key, @requires, or @provides """ scalar _FieldSettest_camel_case_field_name_1.graphql000066400000000000000000000007621463304117000347200ustar00rootroot00000000000000graphene-federation-3.2.0/tests/gql/test_annotation_corner_cases_v1type Query { camel: Camel _entities(representations: [_Any!]!): [_Entity]! _service: _Service! } type Camel @key(fields: "autoCamel") @extends { autoCamel: String @external forcedCamel: String @requires(fields: "autoCamel") aSnake: String aCamel: String } union _Entity = Camel scalar _Any type _Service { sdl: String } """ A string-serialized scalar represents a set of fields that's passed to a federated directive, such as @key, @requires, or @provides """ scalar _FieldSettest_camel_case_field_name_2.graphql000066400000000000000000000005441463304117000347170ustar00rootroot00000000000000graphene-federation-3.2.0/tests/gql/test_annotation_corner_cases_v1type Query { camel: Camel } type Camel @key(fields: "autoCamel") @extends { autoCamel: String @external forcedCamel: String @requires(fields: "autoCamel") aSnake: String aCamel: String } """ A string-serialized scalar represents a set of fields that's passed to a federated directive, such as @key, @requires, or @provides """ scalar _FieldSettest_camel_case_field_name_without_auto_camelcase_1.graphql000066400000000000000000000007331463304117000415260ustar00rootroot00000000000000graphene-federation-3.2.0/tests/gql/test_annotation_corner_cases_v1type Query { camel: Camel _entities(representations: [_Any!]!): [_Entity]! _service: _Service! } type Camel @extends { auto_camel: String @external forcedCamel: String @requires(fields: "auto_camel") a_snake: String aCamel: String } union _Entity = Camel scalar _Any type _Service { sdl: String } """ A string-serialized scalar represents a set of fields that's passed to a federated directive, such as @key, @requires, or @provides """ scalar _FieldSettest_camel_case_field_name_without_auto_camelcase_2.graphql000066400000000000000000000005151463304117000415250ustar00rootroot00000000000000graphene-federation-3.2.0/tests/gql/test_annotation_corner_cases_v1type Query { camel: Camel } type Camel @extends { auto_camel: String @external forcedCamel: String @requires(fields: "auto_camel") a_snake: String aCamel: String } """ A string-serialized scalar represents a set of fields that's passed to a federated directive, such as @key, @requires, or @provides """ scalar _FieldSettest_similar_field_name_1.graphql000066400000000000000000000010411463304117000342730ustar00rootroot00000000000000graphene-federation-3.2.0/tests/gql/test_annotation_corner_cases_v1schema { query: ChatQuery } type ChatQuery { message(id: ID!): ChatMessage _entities(representations: [_Any!]!): [_Entity]! _service: _Service! } type ChatMessage { id: ID! user: ChatUser } type ChatUser @key(fields: "id") @extends { uid: ID identified: ID id: ID @external iD: ID ID: ID } union _Entity = ChatUser scalar _Any type _Service { sdl: String } """ A string-serialized scalar represents a set of fields that's passed to a federated directive, such as @key, @requires, or @provides """ scalar _FieldSettest_similar_field_name_2.graphql000066400000000000000000000006201463304117000342760ustar00rootroot00000000000000graphene-federation-3.2.0/tests/gql/test_annotation_corner_cases_v1schema { query: ChatQuery } type ChatQuery { message(id: ID!): ChatMessage } type ChatMessage { id: ID! user: ChatUser } type ChatUser @key(fields: "id") @extends { uid: ID identified: ID id: ID @external iD: ID ID: ID } """ A string-serialized scalar represents a set of fields that's passed to a federated directive, such as @key, @requires, or @provides """ scalar _FieldSetgraphene-federation-3.2.0/tests/gql/test_custom_enum/000077500000000000000000000000001463304117000227445ustar00rootroot00000000000000graphene-federation-3.2.0/tests/gql/test_custom_enum/test_custom_enum_1.graphql000066400000000000000000000013751463304117000301470ustar00rootroot00000000000000extend schema @link(url: "https://specs.apollo.dev/federation/v2.7", import: ["@inaccessible", "@shareable"]) type TestCustomEnum @shareable { testShareableScalar: Episode @shareable testInaccessibleScalar: Episode @inaccessible } enum Episode @inaccessible { NEWHOPE @inaccessible EMPIRE JEDI } type Query { test: Episode test2: [TestCustomEnum]! _service: _Service! } type _Service { sdl: String } """ A string-serialized scalar represents a set of fields that's passed to a federated directive, such as @key, @requires, or @provides """ scalar FieldSet """This string-serialized scalar represents a JWT scope""" scalar federation__Scope """This string-serialized scalar represents an authorization policy.""" scalar federation__Policygraphene-federation-3.2.0/tests/gql/test_custom_enum/test_custom_enum_2.graphql000066400000000000000000000013061463304117000301420ustar00rootroot00000000000000extend schema @link(url: "https://specs.apollo.dev/federation/v2.7", import: ["@inaccessible", "@shareable"]) type TestCustomEnum @shareable { testShareableScalar: Episode @shareable testInaccessibleScalar: Episode @inaccessible } enum Episode @inaccessible { NEWHOPE @inaccessible EMPIRE JEDI } type Query { test: Episode test2: [TestCustomEnum]! } """ A string-serialized scalar represents a set of fields that's passed to a federated directive, such as @key, @requires, or @provides """ scalar FieldSet """This string-serialized scalar represents a JWT scope""" scalar federation__Scope """This string-serialized scalar represents an authorization policy.""" scalar federation__Policygraphene-federation-3.2.0/tests/gql/test_inaccessible/000077500000000000000000000000001463304117000230325ustar00rootroot00000000000000graphene-federation-3.2.0/tests/gql/test_inaccessible/test_inaccessible_1.graphql000066400000000000000000000011251463304117000303140ustar00rootroot00000000000000extend schema @link(url: "https://specs.apollo.dev/federation/v2.7", import: ["@inaccessible"]) type Position @inaccessible { x: Int! y: Int! @inaccessible } type Query { inStockCount: Int! _service: _Service! } type _Service { sdl: String } """ A string-serialized scalar represents a set of fields that's passed to a federated directive, such as @key, @requires, or @provides """ scalar FieldSet """This string-serialized scalar represents a JWT scope""" scalar federation__Scope """This string-serialized scalar represents an authorization policy.""" scalar federation__Policygraphene-federation-3.2.0/tests/gql/test_inaccessible/test_inaccessible_2.graphql000066400000000000000000000010361463304117000303160ustar00rootroot00000000000000extend schema @link(url: "https://specs.apollo.dev/federation/v2.7", import: ["@inaccessible"]) type Position @inaccessible { x: Int! y: Int! @inaccessible } type Query { inStockCount: Int! } """ A string-serialized scalar represents a set of fields that's passed to a federated directive, such as @key, @requires, or @provides """ scalar FieldSet """This string-serialized scalar represents a JWT scope""" scalar federation__Scope """This string-serialized scalar represents an authorization policy.""" scalar federation__Policygraphene-federation-3.2.0/tests/gql/test_inaccessible/test_inaccessible_union_1.graphql000066400000000000000000000014571463304117000315340ustar00rootroot00000000000000extend schema @link(url: "https://specs.apollo.dev/federation/v2.7", import: ["@inaccessible"]) union SearchResult @inaccessible = Human | Droid | Starship type Human @inaccessible { name: String bornIn: String } type Droid @inaccessible { name: String @inaccessible primaryFunction: String } type Starship @inaccessible { name: String length: Int @inaccessible } type Query { inStockCount: Int! _service: _Service! } type _Service { sdl: String } """ A string-serialized scalar represents a set of fields that's passed to a federated directive, such as @key, @requires, or @provides """ scalar FieldSet """This string-serialized scalar represents a JWT scope""" scalar federation__Scope """This string-serialized scalar represents an authorization policy.""" scalar federation__Policygraphene-federation-3.2.0/tests/gql/test_inaccessible/test_inaccessible_union_2.graphql000066400000000000000000000013701463304117000315270ustar00rootroot00000000000000extend schema @link(url: "https://specs.apollo.dev/federation/v2.7", import: ["@inaccessible"]) union SearchResult @inaccessible = Human | Droid | Starship type Human @inaccessible { name: String bornIn: String } type Droid @inaccessible { name: String @inaccessible primaryFunction: String } type Starship @inaccessible { name: String length: Int @inaccessible } type Query { inStockCount: Int! } """ A string-serialized scalar represents a set of fields that's passed to a federated directive, such as @key, @requires, or @provides """ scalar FieldSet """This string-serialized scalar represents a JWT scope""" scalar federation__Scope """This string-serialized scalar represents an authorization policy.""" scalar federation__Policygraphene-federation-3.2.0/tests/gql/test_key/000077500000000000000000000000001463304117000211765ustar00rootroot00000000000000graphene-federation-3.2.0/tests/gql/test_key/test_compound_primary_key_1.graphql000066400000000000000000000013621463304117000302760ustar00rootroot00000000000000extend schema @link(url: "https://specs.apollo.dev/federation/v2.7", import: ["@key"]) type Query { user: User _entities(representations: [_Any!]!): [_Entity]! _service: _Service! } type User @key(fields: "id organization { registrationNumber }") { id: ID organization: Organization } type Organization { registrationNumber: ID } union _Entity = User scalar _Any type _Service { sdl: String } """ A string-serialized scalar represents a set of fields that's passed to a federated directive, such as @key, @requires, or @provides """ scalar FieldSet """This string-serialized scalar represents a JWT scope""" scalar federation__Scope """This string-serialized scalar represents an authorization policy.""" scalar federation__Policygraphene-federation-3.2.0/tests/gql/test_key/test_compound_primary_key_2.graphql000066400000000000000000000011451463304117000302760ustar00rootroot00000000000000extend schema @link(url: "https://specs.apollo.dev/federation/v2.7", import: ["@key"]) type Query { user: User } type User @key(fields: "id organization { registrationNumber }") { id: ID organization: Organization } type Organization { registrationNumber: ID } """ A string-serialized scalar represents a set of fields that's passed to a federated directive, such as @key, @requires, or @provides """ scalar FieldSet """This string-serialized scalar represents a JWT scope""" scalar federation__Scope """This string-serialized scalar represents an authorization policy.""" scalar federation__Policygraphene-federation-3.2.0/tests/gql/test_key/test_compound_primary_key_with_depth_1.graphql000066400000000000000000000015041463304117000325130ustar00rootroot00000000000000extend schema @link(url: "https://specs.apollo.dev/federation/v2.7", import: ["@key"]) type Query { user: User _entities(representations: [_Any!]!): [_Entity]! _service: _Service! } type User @key(fields: "id organization { businessUnit { id name } }") { id: ID organization: Organization } type Organization { registrationNumber: ID businessUnit: BusinessUnit } type BusinessUnit { id: ID name: String } union _Entity = User scalar _Any type _Service { sdl: String } """ A string-serialized scalar represents a set of fields that's passed to a federated directive, such as @key, @requires, or @provides """ scalar FieldSet """This string-serialized scalar represents a JWT scope""" scalar federation__Scope """This string-serialized scalar represents an authorization policy.""" scalar federation__Policygraphene-federation-3.2.0/tests/gql/test_key/test_compound_primary_key_with_depth_2.graphql000066400000000000000000000012671463304117000325220ustar00rootroot00000000000000extend schema @link(url: "https://specs.apollo.dev/federation/v2.7", import: ["@key"]) type Query { user: User } type User @key(fields: "id organization { businessUnit { id name } }") { id: ID organization: Organization } type Organization { registrationNumber: ID businessUnit: BusinessUnit } type BusinessUnit { id: ID name: String } """ A string-serialized scalar represents a set of fields that's passed to a federated directive, such as @key, @requires, or @provides """ scalar FieldSet """This string-serialized scalar represents a JWT scope""" scalar federation__Scope """This string-serialized scalar represents an authorization policy.""" scalar federation__Policygraphene-federation-3.2.0/tests/gql/test_key/test_multiple_keys_1.graphql000066400000000000000000000012671463304117000267310ustar00rootroot00000000000000extend schema @link(url: "https://specs.apollo.dev/federation/v2.7", import: ["@key"]) type Query { user: User _entities(representations: [_Any!]!): [_Entity]! _service: _Service! } type User @key(fields: "email") @key(fields: "identifier") { identifier: ID email: String } union _Entity = User scalar _Any type _Service { sdl: String } """ A string-serialized scalar represents a set of fields that's passed to a federated directive, such as @key, @requires, or @provides """ scalar FieldSet """This string-serialized scalar represents a JWT scope""" scalar federation__Scope """This string-serialized scalar represents an authorization policy.""" scalar federation__Policygraphene-federation-3.2.0/tests/gql/test_key/test_multiple_keys_2.graphql000066400000000000000000000010521463304117000267220ustar00rootroot00000000000000extend schema @link(url: "https://specs.apollo.dev/federation/v2.7", import: ["@key"]) type Query { user: User } type User @key(fields: "email") @key(fields: "identifier") { identifier: ID email: String } """ A string-serialized scalar represents a set of fields that's passed to a federated directive, such as @key, @requires, or @provides """ scalar FieldSet """This string-serialized scalar represents a JWT scope""" scalar federation__Scope """This string-serialized scalar represents an authorization policy.""" scalar federation__Policygraphene-federation-3.2.0/tests/gql/test_key_v1/000077500000000000000000000000001463304117000216045ustar00rootroot00000000000000graphene-federation-3.2.0/tests/gql/test_key_v1/test_multiple_keys_1.graphql000066400000000000000000000006471463304117000273400ustar00rootroot00000000000000type Query { user: User _entities(representations: [_Any!]!): [_Entity]! _service: _Service! } type User @key(fields: "email") @key(fields: "identifier") { identifier: ID email: String } union _Entity = User scalar _Any type _Service { sdl: String } """ A string-serialized scalar represents a set of fields that's passed to a federated directive, such as @key, @requires, or @provides """ scalar _FieldSetgraphene-federation-3.2.0/tests/gql/test_key_v1/test_multiple_keys_2.graphql000066400000000000000000000004321463304117000273310ustar00rootroot00000000000000type Query { user: User } type User @key(fields: "email") @key(fields: "identifier") { identifier: ID email: String } """ A string-serialized scalar represents a set of fields that's passed to a federated directive, such as @key, @requires, or @provides """ scalar _FieldSetgraphene-federation-3.2.0/tests/gql/test_override/000077500000000000000000000000001463304117000222255ustar00rootroot00000000000000graphene-federation-3.2.0/tests/gql/test_override/test_override_1.graphql000066400000000000000000000012611463304117000267030ustar00rootroot00000000000000extend schema @link(url: "https://specs.apollo.dev/federation/v2.7", import: ["@override"]) type Query { product: Product _service: _Service! } type Product { sku: ID @override(from: "subgraph-1") size: Int @override(from: "subgraph-2") weight: Int @override(from: "subgraph-3", label: "Test label") } type _Service { sdl: String } """ A string-serialized scalar represents a set of fields that's passed to a federated directive, such as @key, @requires, or @provides """ scalar FieldSet """This string-serialized scalar represents a JWT scope""" scalar federation__Scope """This string-serialized scalar represents an authorization policy.""" scalar federation__Policygraphene-federation-3.2.0/tests/gql/test_override/test_override_2.graphql000066400000000000000000000011721463304117000267050ustar00rootroot00000000000000extend schema @link(url: "https://specs.apollo.dev/federation/v2.7", import: ["@override"]) type Query { product: Product } type Product { sku: ID @override(from: "subgraph-1") size: Int @override(from: "subgraph-2") weight: Int @override(from: "subgraph-3", label: "Test label") } """ A string-serialized scalar represents a set of fields that's passed to a federated directive, such as @key, @requires, or @provides """ scalar FieldSet """This string-serialized scalar represents a JWT scope""" scalar federation__Scope """This string-serialized scalar represents an authorization policy.""" scalar federation__Policygraphene-federation-3.2.0/tests/gql/test_provides/000077500000000000000000000000001463304117000222415ustar00rootroot00000000000000graphene-federation-3.2.0/tests/gql/test_provides/test_provides_1.graphql000066400000000000000000000015131463304117000267330ustar00rootroot00000000000000extend schema @link(url: "https://specs.apollo.dev/federation/v2.7", import: ["@external", "@key", "@provides"]) type Query { inStockCount: InStockCount _entities(representations: [_Any!]!): [_Entity]! _service: _Service! } type InStockCount { product: Product! @provides(fields: "name") quantity: Int! } type Product @key(fields: "sku") { sku: String! @external name: String @external weight: Int @external } union _Entity = Product scalar _Any type _Service { sdl: String } """ A string-serialized scalar represents a set of fields that's passed to a federated directive, such as @key, @requires, or @provides """ scalar FieldSet """This string-serialized scalar represents a JWT scope""" scalar federation__Scope """This string-serialized scalar represents an authorization policy.""" scalar federation__Policygraphene-federation-3.2.0/tests/gql/test_provides/test_provides_2.graphql000066400000000000000000000012731463304117000267370ustar00rootroot00000000000000extend schema @link(url: "https://specs.apollo.dev/federation/v2.7", import: ["@external", "@key", "@provides"]) type Query { inStockCount: InStockCount } type InStockCount { product: Product! @provides(fields: "name") quantity: Int! } type Product @key(fields: "sku") { sku: String! @external name: String @external weight: Int @external } """ A string-serialized scalar represents a set of fields that's passed to a federated directive, such as @key, @requires, or @provides """ scalar FieldSet """This string-serialized scalar represents a JWT scope""" scalar federation__Scope """This string-serialized scalar represents an authorization policy.""" scalar federation__Policygraphene-federation-3.2.0/tests/gql/test_provides/test_provides_multiple_fields_1.graphql000066400000000000000000000015221463304117000321740ustar00rootroot00000000000000extend schema @link(url: "https://specs.apollo.dev/federation/v2.7", import: ["@external", "@key", "@provides"]) type Query { inStockCount: InStockCount _entities(representations: [_Any!]!): [_Entity]! _service: _Service! } type InStockCount { product: Product! @provides(fields: "name weight") quantity: Int! } type Product @key(fields: "sku") { sku: String! @external name: String @external weight: Int @external } union _Entity = Product scalar _Any type _Service { sdl: String } """ A string-serialized scalar represents a set of fields that's passed to a federated directive, such as @key, @requires, or @provides """ scalar FieldSet """This string-serialized scalar represents a JWT scope""" scalar federation__Scope """This string-serialized scalar represents an authorization policy.""" scalar federation__Policygraphene-federation-3.2.0/tests/gql/test_provides/test_provides_multiple_fields_2.graphql000066400000000000000000000013021463304117000321710ustar00rootroot00000000000000extend schema @link(url: "https://specs.apollo.dev/federation/v2.7", import: ["@external", "@key", "@provides"]) type Query { inStockCount: InStockCount } type InStockCount { product: Product! @provides(fields: "name weight") quantity: Int! } type Product @key(fields: "sku") { sku: String! @external name: String @external weight: Int @external } """ A string-serialized scalar represents a set of fields that's passed to a federated directive, such as @key, @requires, or @provides """ scalar FieldSet """This string-serialized scalar represents a JWT scope""" scalar federation__Scope """This string-serialized scalar represents an authorization policy.""" scalar federation__Policygraphene-federation-3.2.0/tests/gql/test_provides/test_provides_multiple_fields_as_list_1.graphql000066400000000000000000000015471463304117000337210ustar00rootroot00000000000000extend schema @link(url: "https://specs.apollo.dev/federation/v2.7", import: ["@extends", "@external", "@key", "@provides"]) type Query { inStockCount: InStockCount _entities(representations: [_Any!]!): [_Entity]! _service: _Service! } type InStockCount { product: Product! @provides(fields: "name weight") quantity: Int! } type Product @key(fields: "sku") @extends { sku: String! @external name: String @external weight: Int @external } union _Entity = Product scalar _Any type _Service { sdl: String } """ A string-serialized scalar represents a set of fields that's passed to a federated directive, such as @key, @requires, or @provides """ scalar FieldSet """This string-serialized scalar represents a JWT scope""" scalar federation__Scope """This string-serialized scalar represents an authorization policy.""" scalar federation__Policygraphene-federation-3.2.0/tests/gql/test_provides/test_provides_multiple_fields_as_list_2.graphql000066400000000000000000000013271463304117000337160ustar00rootroot00000000000000extend schema @link(url: "https://specs.apollo.dev/federation/v2.7", import: ["@extends", "@external", "@key", "@provides"]) type Query { inStockCount: InStockCount } type InStockCount { product: Product! @provides(fields: "name weight") quantity: Int! } type Product @key(fields: "sku") @extends { sku: String! @external name: String @external weight: Int @external } """ A string-serialized scalar represents a set of fields that's passed to a federated directive, such as @key, @requires, or @provides """ scalar FieldSet """This string-serialized scalar represents a JWT scope""" scalar federation__Scope """This string-serialized scalar represents an authorization policy.""" scalar federation__Policygraphene-federation-3.2.0/tests/gql/test_provides_v1/000077500000000000000000000000001463304117000226475ustar00rootroot00000000000000graphene-federation-3.2.0/tests/gql/test_provides_v1/test_provides_1.graphql000066400000000000000000000010411463304117000273350ustar00rootroot00000000000000type Query { inStockCount: InStockCount _entities(representations: [_Any!]!): [_Entity]! _service: _Service! } type InStockCount { product: Product! @provides(fields: "name") quantity: Int! } type Product @key(fields: "sku") { sku: String! @external name: String @external weight: Int @external } union _Entity = Product scalar _Any type _Service { sdl: String } """ A string-serialized scalar represents a set of fields that's passed to a federated directive, such as @key, @requires, or @provides """ scalar _FieldSetgraphene-federation-3.2.0/tests/gql/test_provides_v1/test_provides_2.graphql000066400000000000000000000006211463304117000273410ustar00rootroot00000000000000type Query { inStockCount: InStockCount } type InStockCount { product: Product! @provides(fields: "name") quantity: Int! } type Product @key(fields: "sku") { sku: String! @external name: String @external weight: Int @external } """ A string-serialized scalar represents a set of fields that's passed to a federated directive, such as @key, @requires, or @provides """ scalar _FieldSetgraphene-federation-3.2.0/tests/gql/test_provides_v1/test_provides_multiple_fields_1.graphql000066400000000000000000000010501463304117000325760ustar00rootroot00000000000000type Query { inStockCount: InStockCount _entities(representations: [_Any!]!): [_Entity]! _service: _Service! } type InStockCount { product: Product! @provides(fields: "name weight") quantity: Int! } type Product @key(fields: "sku") { sku: String! @external name: String @external weight: Int @external } union _Entity = Product scalar _Any type _Service { sdl: String } """ A string-serialized scalar represents a set of fields that's passed to a federated directive, such as @key, @requires, or @provides """ scalar _FieldSetgraphene-federation-3.2.0/tests/gql/test_provides_v1/test_provides_multiple_fields_2.graphql000066400000000000000000000006301463304117000326020ustar00rootroot00000000000000type Query { inStockCount: InStockCount } type InStockCount { product: Product! @provides(fields: "name weight") quantity: Int! } type Product @key(fields: "sku") { sku: String! @external name: String @external weight: Int @external } """ A string-serialized scalar represents a set of fields that's passed to a federated directive, such as @key, @requires, or @provides """ scalar _FieldSetgraphene-federation-3.2.0/tests/gql/test_provides_v1/test_provides_multiple_fields_as_list_1.graphql000066400000000000000000000010611463304117000343160ustar00rootroot00000000000000type Query { inStockCount: InStockCount _entities(representations: [_Any!]!): [_Entity]! _service: _Service! } type InStockCount { product: Product! @provides(fields: "name weight") quantity: Int! } type Product @key(fields: "sku") @extends { sku: String! @external name: String @external weight: Int @external } union _Entity = Product scalar _Any type _Service { sdl: String } """ A string-serialized scalar represents a set of fields that's passed to a federated directive, such as @key, @requires, or @provides """ scalar _FieldSetgraphene-federation-3.2.0/tests/gql/test_provides_v1/test_provides_multiple_fields_as_list_2.graphql000066400000000000000000000006411463304117000343220ustar00rootroot00000000000000type Query { inStockCount: InStockCount } type InStockCount { product: Product! @provides(fields: "name weight") quantity: Int! } type Product @key(fields: "sku") @extends { sku: String! @external name: String @external weight: Int @external } """ A string-serialized scalar represents a set of fields that's passed to a federated directive, such as @key, @requires, or @provides """ scalar _FieldSetgraphene-federation-3.2.0/tests/gql/test_requires/000077500000000000000000000000001463304117000222455ustar00rootroot00000000000000graphene-federation-3.2.0/tests/gql/test_requires/test_requires_multiple_fields_1.graphql000066400000000000000000000014631463304117000322100ustar00rootroot00000000000000extend schema @link(url: "https://specs.apollo.dev/federation/v2.7", import: ["@extends", "@external", "@key", "@requires"]) type Query { product: Product _entities(representations: [_Any!]!): [_Entity]! _service: _Service! } type Product @key(fields: "sku") @extends { sku: ID @external size: Int @external weight: Int @external shippingEstimate: String @requires(fields: "size weight") } union _Entity = Product scalar _Any type _Service { sdl: String } """ A string-serialized scalar represents a set of fields that's passed to a federated directive, such as @key, @requires, or @provides """ scalar FieldSet """This string-serialized scalar represents a JWT scope""" scalar federation__Scope """This string-serialized scalar represents an authorization policy.""" scalar federation__Policygraphene-federation-3.2.0/tests/gql/test_requires/test_requires_multiple_fields_2.graphql000066400000000000000000000012431463304117000322050ustar00rootroot00000000000000extend schema @link(url: "https://specs.apollo.dev/federation/v2.7", import: ["@extends", "@external", "@key", "@requires"]) type Query { product: Product } type Product @key(fields: "sku") @extends { sku: ID @external size: Int @external weight: Int @external shippingEstimate: String @requires(fields: "size weight") } """ A string-serialized scalar represents a set of fields that's passed to a federated directive, such as @key, @requires, or @provides """ scalar FieldSet """This string-serialized scalar represents a JWT scope""" scalar federation__Scope """This string-serialized scalar represents an authorization policy.""" scalar federation__Policygraphene-federation-3.2.0/tests/gql/test_requires/test_requires_multiple_fields_as_list_1.graphql000066400000000000000000000014631463304117000337260ustar00rootroot00000000000000extend schema @link(url: "https://specs.apollo.dev/federation/v2.7", import: ["@extends", "@external", "@key", "@requires"]) type Query { product: Product _entities(representations: [_Any!]!): [_Entity]! _service: _Service! } type Product @key(fields: "sku") @extends { sku: ID @external size: Int @external weight: Int @external shippingEstimate: String @requires(fields: "size weight") } union _Entity = Product scalar _Any type _Service { sdl: String } """ A string-serialized scalar represents a set of fields that's passed to a federated directive, such as @key, @requires, or @provides """ scalar FieldSet """This string-serialized scalar represents a JWT scope""" scalar federation__Scope """This string-serialized scalar represents an authorization policy.""" scalar federation__Policygraphene-federation-3.2.0/tests/gql/test_requires/test_requires_multiple_fields_as_list_2.graphql000066400000000000000000000012431463304117000337230ustar00rootroot00000000000000extend schema @link(url: "https://specs.apollo.dev/federation/v2.7", import: ["@extends", "@external", "@key", "@requires"]) type Query { product: Product } type Product @key(fields: "sku") @extends { sku: ID @external size: Int @external weight: Int @external shippingEstimate: String @requires(fields: "size weight") } """ A string-serialized scalar represents a set of fields that's passed to a federated directive, such as @key, @requires, or @provides """ scalar FieldSet """This string-serialized scalar represents a JWT scope""" scalar federation__Scope """This string-serialized scalar represents an authorization policy.""" scalar federation__Policygraphene-federation-3.2.0/tests/gql/test_requires/test_requires_with_input_1.graphql000066400000000000000000000014141463304117000312150ustar00rootroot00000000000000extend schema @link(url: "https://specs.apollo.dev/federation/v2.7", import: ["@extends", "@external", "@key", "@requires"]) type Query { acme: Acme _entities(representations: [_Any!]!): [_Entity]! _service: _Service! } type Acme @key(fields: "id") @extends { id: ID! @external age: Int @external foo(someInput: String ): String @requires(fields: "age") } union _Entity = Acme scalar _Any type _Service { sdl: String } """ A string-serialized scalar represents a set of fields that's passed to a federated directive, such as @key, @requires, or @provides """ scalar FieldSet """This string-serialized scalar represents a JWT scope""" scalar federation__Scope """This string-serialized scalar represents an authorization policy.""" scalar federation__Policygraphene-federation-3.2.0/tests/gql/test_requires/test_requires_with_input_2.graphql000066400000000000000000000011771463304117000312240ustar00rootroot00000000000000extend schema @link(url: "https://specs.apollo.dev/federation/v2.7", import: ["@extends", "@external", "@key", "@requires"]) type Query { acme: Acme } type Acme @key(fields: "id") @extends { id: ID! @external age: Int @external foo(someInput: String ): String @requires(fields: "age") } """ A string-serialized scalar represents a set of fields that's passed to a federated directive, such as @key, @requires, or @provides """ scalar FieldSet """This string-serialized scalar represents a JWT scope""" scalar federation__Scope """This string-serialized scalar represents an authorization policy.""" scalar federation__Policygraphene-federation-3.2.0/tests/gql/test_requires_v1/000077500000000000000000000000001463304117000226535ustar00rootroot00000000000000graphene-federation-3.2.0/tests/gql/test_requires_v1/test_requires_multiple_fields_1.graphql000066400000000000000000000007751463304117000326230ustar00rootroot00000000000000type Query { product: Product _entities(representations: [_Any!]!): [_Entity]! _service: _Service! } type Product @key(fields: "sku") @extends { sku: ID @external size: Int @external weight: Int @external shippingEstimate: String @requires(fields: "size weight") } union _Entity = Product scalar _Any type _Service { sdl: String } """ A string-serialized scalar represents a set of fields that's passed to a federated directive, such as @key, @requires, or @provides """ scalar _FieldSetgraphene-federation-3.2.0/tests/gql/test_requires_v1/test_requires_multiple_fields_2.graphql000066400000000000000000000005551463304117000326200ustar00rootroot00000000000000type Query { product: Product } type Product @key(fields: "sku") @extends { sku: ID @external size: Int @external weight: Int @external shippingEstimate: String @requires(fields: "size weight") } """ A string-serialized scalar represents a set of fields that's passed to a federated directive, such as @key, @requires, or @provides """ scalar _FieldSetgraphene-federation-3.2.0/tests/gql/test_requires_v1/test_requires_multiple_fields_as_list_1.graphql000066400000000000000000000007751463304117000343410ustar00rootroot00000000000000type Query { product: Product _entities(representations: [_Any!]!): [_Entity]! _service: _Service! } type Product @key(fields: "sku") @extends { sku: ID @external size: Int @external weight: Int @external shippingEstimate: String @requires(fields: "size weight") } union _Entity = Product scalar _Any type _Service { sdl: String } """ A string-serialized scalar represents a set of fields that's passed to a federated directive, such as @key, @requires, or @provides """ scalar _FieldSetgraphene-federation-3.2.0/tests/gql/test_requires_v1/test_requires_multiple_fields_as_list_2.graphql000066400000000000000000000005551463304117000343360ustar00rootroot00000000000000type Query { product: Product } type Product @key(fields: "sku") @extends { sku: ID @external size: Int @external weight: Int @external shippingEstimate: String @requires(fields: "size weight") } """ A string-serialized scalar represents a set of fields that's passed to a federated directive, such as @key, @requires, or @provides """ scalar _FieldSetgraphene-federation-3.2.0/tests/gql/test_requires_v1/test_requires_with_input_1.graphql000066400000000000000000000007261463304117000316300ustar00rootroot00000000000000type Query { acme: Acme _entities(representations: [_Any!]!): [_Entity]! _service: _Service! } type Acme @key(fields: "id") @extends { id: ID! @external age: Int @external foo(someInput: String ): String @requires(fields: "age") } union _Entity = Acme scalar _Any type _Service { sdl: String } """ A string-serialized scalar represents a set of fields that's passed to a federated directive, such as @key, @requires, or @provides """ scalar _FieldSetgraphene-federation-3.2.0/tests/gql/test_requires_v1/test_requires_with_input_2.graphql000066400000000000000000000005111463304117000316210ustar00rootroot00000000000000type Query { acme: Acme } type Acme @key(fields: "id") @extends { id: ID! @external age: Int @external foo(someInput: String ): String @requires(fields: "age") } """ A string-serialized scalar represents a set of fields that's passed to a federated directive, such as @key, @requires, or @provides """ scalar _FieldSetgraphene-federation-3.2.0/tests/gql/test_scalar/000077500000000000000000000000001463304117000216535ustar00rootroot00000000000000graphene-federation-3.2.0/tests/gql/test_scalar/test_custom_scalar_1.graphql000066400000000000000000000013701463304117000273520ustar00rootroot00000000000000extend schema @link(url: "https://specs.apollo.dev/federation/v2.7", import: ["@inaccessible", "@shareable"]) type TestScalar @shareable { testShareableScalar(x: AddressScalar): String @shareable testInaccessibleScalar(x: AddressScalar): String @inaccessible } scalar AddressScalar type Query { test(x: AddressScalar): String test2: [AddressScalar]! _service: _Service! } type _Service { sdl: String } """ A string-serialized scalar represents a set of fields that's passed to a federated directive, such as @key, @requires, or @provides """ scalar FieldSet """This string-serialized scalar represents a JWT scope""" scalar federation__Scope """This string-serialized scalar represents an authorization policy.""" scalar federation__Policygraphene-federation-3.2.0/tests/gql/test_scalar/test_custom_scalar_2.graphql000066400000000000000000000013011463304117000273450ustar00rootroot00000000000000extend schema @link(url: "https://specs.apollo.dev/federation/v2.7", import: ["@inaccessible", "@shareable"]) type TestScalar @shareable { testShareableScalar(x: AddressScalar): String @shareable testInaccessibleScalar(x: AddressScalar): String @inaccessible } scalar AddressScalar type Query { test(x: AddressScalar): String test2: [AddressScalar]! } """ A string-serialized scalar represents a set of fields that's passed to a federated directive, such as @key, @requires, or @provides """ scalar FieldSet """This string-serialized scalar represents a JWT scope""" scalar federation__Scope """This string-serialized scalar represents an authorization policy.""" scalar federation__Policygraphene-federation-3.2.0/tests/gql/test_schema_annotation/000077500000000000000000000000001463304117000241005ustar00rootroot00000000000000graphene-federation-3.2.0/tests/gql/test_schema_annotation/test_chat_schema_1.graphql000066400000000000000000000015031463304117000311750ustar00rootroot00000000000000extend schema @link(url: "https://specs.apollo.dev/federation/v2.7", import: ["@extends", "@external", "@key"]) schema { query: ChatQuery } type ChatQuery { message(id: ID!): ChatMessage _entities(representations: [_Any!]!): [_Entity]! _service: _Service! } type ChatMessage { id: ID! text: String userId: ID user: ChatUser! } type ChatUser @key(fields: "userId") @extends { userId: ID! @external } union _Entity = ChatUser scalar _Any type _Service { sdl: String } """ A string-serialized scalar represents a set of fields that's passed to a federated directive, such as @key, @requires, or @provides """ scalar FieldSet """This string-serialized scalar represents a JWT scope""" scalar federation__Scope """This string-serialized scalar represents an authorization policy.""" scalar federation__Policygraphene-federation-3.2.0/tests/gql/test_schema_annotation/test_chat_schema_2.graphql000066400000000000000000000012621463304117000312000ustar00rootroot00000000000000extend schema @link(url: "https://specs.apollo.dev/federation/v2.7", import: ["@extends", "@external", "@key"]) schema { query: ChatQuery } type ChatQuery { message(id: ID!): ChatMessage } type ChatMessage { id: ID! text: String userId: ID user: ChatUser! } type ChatUser @key(fields: "userId") @extends { userId: ID! @external } """ A string-serialized scalar represents a set of fields that's passed to a federated directive, such as @key, @requires, or @provides """ scalar FieldSet """This string-serialized scalar represents a JWT scope""" scalar federation__Scope """This string-serialized scalar represents an authorization policy.""" scalar federation__Policygraphene-federation-3.2.0/tests/gql/test_schema_annotation/test_user_schema_1.graphql000066400000000000000000000013601463304117000312350ustar00rootroot00000000000000extend schema @link(url: "https://specs.apollo.dev/federation/v2.7", import: ["@key"]) schema { query: UserQuery } type UserQuery { user(userId: ID!): User _entities(representations: [_Any!]!): [_Entity]! _service: _Service! } type User @key(fields: "email") @key(fields: "userId") { userId: ID! email: String! name: String } union _Entity = User scalar _Any type _Service { sdl: String } """ A string-serialized scalar represents a set of fields that's passed to a federated directive, such as @key, @requires, or @provides """ scalar FieldSet """This string-serialized scalar represents a JWT scope""" scalar federation__Scope """This string-serialized scalar represents an authorization policy.""" scalar federation__Policygraphene-federation-3.2.0/tests/gql/test_schema_annotation/test_user_schema_2.graphql000066400000000000000000000011431463304117000312350ustar00rootroot00000000000000extend schema @link(url: "https://specs.apollo.dev/federation/v2.7", import: ["@key"]) schema { query: UserQuery } type UserQuery { user(userId: ID!): User } type User @key(fields: "email") @key(fields: "userId") { userId: ID! email: String! name: String } """ A string-serialized scalar represents a set of fields that's passed to a federated directive, such as @key, @requires, or @provides """ scalar FieldSet """This string-serialized scalar represents a JWT scope""" scalar federation__Scope """This string-serialized scalar represents an authorization policy.""" scalar federation__Policygraphene-federation-3.2.0/tests/gql/test_schema_annotation_v1/000077500000000000000000000000001463304117000245065ustar00rootroot00000000000000graphene-federation-3.2.0/tests/gql/test_schema_annotation_v1/test_chat_schema_1.graphql000066400000000000000000000010321463304117000316000ustar00rootroot00000000000000schema { query: ChatQuery } type ChatQuery { message(id: ID!): ChatMessage _entities(representations: [_Any!]!): [_Entity]! _service: _Service! } type ChatMessage { id: ID! text: String userId: ID user: ChatUser! } type ChatUser @key(fields: "userId") @extends { userId: ID! @external } union _Entity = ChatUser scalar _Any type _Service { sdl: String } """ A string-serialized scalar represents a set of fields that's passed to a federated directive, such as @key, @requires, or @provides """ scalar _FieldSetgraphene-federation-3.2.0/tests/gql/test_schema_annotation_v1/test_chat_schema_2.graphql000066400000000000000000000006111463304117000316030ustar00rootroot00000000000000schema { query: ChatQuery } type ChatQuery { message(id: ID!): ChatMessage } type ChatMessage { id: ID! text: String userId: ID user: ChatUser! } type ChatUser @key(fields: "userId") @extends { userId: ID! @external } """ A string-serialized scalar represents a set of fields that's passed to a federated directive, such as @key, @requires, or @provides """ scalar _FieldSetgraphene-federation-3.2.0/tests/gql/test_schema_annotation_v1/test_user_schema_1.graphql000066400000000000000000000007401463304117000316440ustar00rootroot00000000000000schema { query: UserQuery } type UserQuery { user(userId: ID!): User _entities(representations: [_Any!]!): [_Entity]! _service: _Service! } type User @key(fields: "email") @key(fields: "userId") { userId: ID! email: String! name: String } union _Entity = User scalar _Any type _Service { sdl: String } """ A string-serialized scalar represents a set of fields that's passed to a federated directive, such as @key, @requires, or @provides """ scalar _FieldSetgraphene-federation-3.2.0/tests/gql/test_schema_annotation_v1/test_user_schema_2.graphql000066400000000000000000000005231463304117000316440ustar00rootroot00000000000000schema { query: UserQuery } type UserQuery { user(userId: ID!): User } type User @key(fields: "email") @key(fields: "userId") { userId: ID! email: String! name: String } """ A string-serialized scalar represents a set of fields that's passed to a federated directive, such as @key, @requires, or @provides """ scalar _FieldSetgraphene-federation-3.2.0/tests/gql/test_shareable/000077500000000000000000000000001463304117000223345ustar00rootroot00000000000000graphene-federation-3.2.0/tests/gql/test_shareable/test_shareable_1.graphql000066400000000000000000000011141463304117000271160ustar00rootroot00000000000000extend schema @link(url: "https://specs.apollo.dev/federation/v2.7", import: ["@shareable"]) type Position @shareable { x: Int! y: Int! @shareable } type Query { inStockCount: Int! _service: _Service! } type _Service { sdl: String } """ A string-serialized scalar represents a set of fields that's passed to a federated directive, such as @key, @requires, or @provides """ scalar FieldSet """This string-serialized scalar represents a JWT scope""" scalar federation__Scope """This string-serialized scalar represents an authorization policy.""" scalar federation__Policygraphene-federation-3.2.0/tests/gql/test_shareable/test_shareable_2.graphql000066400000000000000000000010251463304117000271200ustar00rootroot00000000000000extend schema @link(url: "https://specs.apollo.dev/federation/v2.7", import: ["@shareable"]) type Position @shareable { x: Int! y: Int! @shareable } type Query { inStockCount: Int! } """ A string-serialized scalar represents a set of fields that's passed to a federated directive, such as @key, @requires, or @provides """ scalar FieldSet """This string-serialized scalar represents a JWT scope""" scalar federation__Scope """This string-serialized scalar represents an authorization policy.""" scalar federation__Policygraphene-federation-3.2.0/tests/test_annotation_corner_cases.py000066400000000000000000000070751463304117000251060ustar00rootroot00000000000000from pathlib import Path from graphene import Field, ID, ObjectType, String from graphene_federation import ( LATEST_VERSION, build_schema, extends, external, key, requires, ) from tests.util import file_handlers, sdl_query save_file, open_file = file_handlers(Path(__file__)) def test_similar_field_name(): """ Test annotation with fields that have similar names. """ @extends @key("id") class ChatUser(ObjectType): uid = ID() identified = ID() id = external(ID()) i_d = ID() ID = ID() class ChatMessage(ObjectType): id = ID(required=True) user = Field(ChatUser) class ChatQuery(ObjectType): message = Field(ChatMessage, id=ID(required=True)) schema = build_schema(query=ChatQuery, federation_version=LATEST_VERSION) # save_file(str(schema), "1") # save_file(sdl_query(schema), "2") assert open_file("1") == str(schema) assert open_file("2") == sdl_query(schema) def test_camel_case_field_name(): """ Test annotation with fields that have camel cases or snake case. """ @key("auto_camel") @extends class Camel(ObjectType): auto_camel = external(String()) forcedCamel = requires(String(), fields="auto_camel") a_snake = String() aCamel = String() class Query(ObjectType): camel = Field(Camel) schema = build_schema(query=Query, federation_version=LATEST_VERSION) # save_file(str(schema), "1") # save_file(sdl_query(schema), "2") assert open_file("1") == str(schema) assert open_file("2") == sdl_query(schema) def test_camel_case_field_name_without_auto_camelcase(): """ Test annotation with fields that have camel cases or snake case but with the auto_camelcase disabled. """ @extends class Camel(ObjectType): auto_camel = external(String()) forcedCamel = requires(String(), fields="auto_camel") a_snake = String() aCamel = String() class Query(ObjectType): camel = Field(Camel) schema = build_schema( query=Query, auto_camelcase=False, federation_version=LATEST_VERSION ) # save_file(str(schema), "1") # save_file(sdl_query(schema), "2") assert open_file("1") == str(schema) assert open_file("2") == sdl_query(schema) def test_annotated_field_also_used_in_filter(): """ Test that when a field also used in filter needs to get annotated, it really annotates only the field. See issue https://github.com/preply/graphene-federation/issues/50 """ @key("id") class B(ObjectType): id = ID() @extends class A(ObjectType): id = external(ID()) b = Field(B, id=ID()) class Query(ObjectType): a = Field(A) schema = build_schema(query=Query, federation_version=LATEST_VERSION) # save_file(str(schema), "1") # save_file(sdl_query(schema), "2") assert open_file("1") == str(schema) assert open_file("2") == sdl_query(schema) def test_annotate_object_with_meta_name(): @key("id") class B(ObjectType): class Meta: name = "Potato" id = ID() @extends class A(ObjectType): class Meta: name = "Banana" id = external(ID()) b = Field(B, id=ID()) class Query(ObjectType): a = Field(A) schema = build_schema(query=Query, federation_version=LATEST_VERSION) # save_file(str(schema), "1") # save_file(sdl_query(schema), "2") assert open_file("1") == str(schema) assert open_file("2") == sdl_query(schema) graphene-federation-3.2.0/tests/test_annotation_corner_cases_v1.py000066400000000000000000000065761463304117000255210ustar00rootroot00000000000000from pathlib import Path from graphene import Field, ID, ObjectType, String from graphene_federation import ( FederationVersion, build_schema, extends, external, key, requires, ) from tests.util import file_handlers, sdl_query save_file, open_file = file_handlers(Path(__file__)) def test_similar_field_name(): """ Test annotation with fields that have similar names. """ @extends @key("id") class ChatUser(ObjectType): uid = ID() identified = ID() id = external(ID()) i_d = ID() ID = ID() class ChatMessage(ObjectType): id = ID(required=True) user = Field(ChatUser) class ChatQuery(ObjectType): message = Field(ChatMessage, id=ID(required=True)) schema = build_schema( query=ChatQuery, federation_version=FederationVersion.VERSION_1_0 ) # save_file(str(schema), "1") # save_file(sdl_query(schema), "2") assert open_file("1") == str(schema) assert open_file("2") == sdl_query(schema) def test_camel_case_field_name(): """ Test annotation with fields that have camel cases or snake case. """ @key("auto_camel") @extends class Camel(ObjectType): auto_camel = external(String()) forcedCamel = requires(String(), fields="auto_camel") a_snake = String() aCamel = String() class Query(ObjectType): camel = Field(Camel) schema = build_schema(query=Query, federation_version=FederationVersion.VERSION_1_0) assert open_file("1") == str(schema) assert open_file("2") == sdl_query(schema) def test_camel_case_field_name_without_auto_camelcase(): """ Test annotation with fields that have camel cases or snake case but with the auto_camelcase disabled. """ @extends class Camel(ObjectType): auto_camel = external(String()) forcedCamel = requires(String(), fields="auto_camel") a_snake = String() aCamel = String() class Query(ObjectType): camel = Field(Camel) schema = build_schema( query=Query, auto_camelcase=False, federation_version=FederationVersion.VERSION_1_0, ) assert open_file("1") == str(schema) assert open_file("2") == sdl_query(schema) def test_annotated_field_also_used_in_filter(): """ Test that when a field also used in filter needs to get annotated, it really annotates only the field. See issue https://github.com/preply/graphene-federation/issues/50 """ @key("id") class B(ObjectType): id = ID() @extends class A(ObjectType): id = external(ID()) b = Field(B, id=ID()) class Query(ObjectType): a = Field(A) schema = build_schema(query=Query, federation_version=FederationVersion.VERSION_1_0) assert open_file("1") == str(schema) assert open_file("2") == sdl_query(schema) def test_annotate_object_with_meta_name(): @key("id") class B(ObjectType): class Meta: name = "Potato" id = ID() @extends class A(ObjectType): class Meta: name = "Banana" id = external(ID()) b = Field(B, id=ID()) class Query(ObjectType): a = Field(A) schema = build_schema(query=Query, federation_version=FederationVersion.VERSION_1_0) assert open_file("1") == str(schema) assert open_file("2") == sdl_query(schema) graphene-federation-3.2.0/tests/test_custom_enum.py000066400000000000000000000020101463304117000225240ustar00rootroot00000000000000from pathlib import Path import graphene from graphene import ObjectType from graphene_federation import LATEST_VERSION, build_schema from graphene_federation import inaccessible, shareable from tests.util import file_handlers, sdl_query save_file, open_file = file_handlers(Path(__file__)) def test_custom_enum(): @inaccessible class Episode(graphene.Enum): NEWHOPE = 4 EMPIRE = 5 JEDI = 6 inaccessible(Episode.NEWHOPE) @shareable class TestCustomEnum(graphene.ObjectType): test_shareable_scalar = shareable(Episode()) test_inaccessible_scalar = inaccessible(Episode()) class Query(ObjectType): test = Episode() test2 = graphene.List(TestCustomEnum, required=True) schema = build_schema( query=Query, federation_version=LATEST_VERSION, types=(TestCustomEnum,) ) # save_file(str(schema), "1") # save_file(sdl_query(schema), "2") assert open_file("1") == str(schema) assert open_file("2") == sdl_query(schema) graphene-federation-3.2.0/tests/test_custom_field_names.py000066400000000000000000000120461463304117000240400ustar00rootroot00000000000000from pathlib import Path import pytest from graphene import Field, ID, ObjectType, String from graphene_federation import build_schema, key, provides, requires from tests.util import file_handlers save_file, open_file = file_handlers(Path(__file__)) def test_key_auto_camelcase_false(): try: @key("identifier") @key("valid_email") class User(ObjectType): identifier = ID() email = String(name="valid_email") class Query(ObjectType): user = Field(User) _schema = build_schema(query=Query, auto_camelcase=False) except Exception as exc: pytest.fail(f"Unexpected Error {exc}") def test_key_auto_camelcase_true(): with pytest.raises(ValueError) as err: @key("identifier") @key("valid_email") class User(ObjectType): identifier = ID() email = String(name="valid_email") class Query(ObjectType): user = Field(User) _schema = build_schema(query=Query, auto_camelcase=True) assert str(err.value) == '@key, field "validEmail" does not exist on type "User"' def test_key_auto_camelcase_with_auto_case_false(): try: @key("identifier") @key("valid_email", auto_case=False) class User(ObjectType): identifier = ID() email = String(name="valid_email") class Query(ObjectType): user = Field(User) _schema = build_schema(query=Query, auto_camelcase=True) except Exception as exc: pytest.fail(f"Unexpected Error {exc}") def test_requires_auto_camelcase_false(): try: class Employee(ObjectType): identifier = ID() email = String(name="corp_email") class User(ObjectType): identifier = ID() employee = Field(Employee) email = requires(String(), fields="employee { corp_email }") class Query(ObjectType): user = Field(User) _schema = build_schema(query=Query, auto_camelcase=False) except Exception as exc: pytest.fail(f"Unexpected Error {exc}") def test_requires_auto_camelcase_true(): with pytest.raises(ValueError) as err: class Employee(ObjectType): identifier = ID() email = String(name="corp_email") class User(ObjectType): identifier = ID() employee = Field(Employee) email = requires(String(), fields="employee { corp_email }") class Query(ObjectType): user = Field(User) _schema = build_schema(query=Query, auto_camelcase=True) assert ( str(err.value) == '@requires, field "corpEmail" does not exist on type "Employee"' ) def test_requires_auto_camelcase_with_auto_case_false(): try: class Employee(ObjectType): identifier = ID() email = String(name="corp_email") class User(ObjectType): identifier = ID() employee = Field(Employee) email = requires( String(), fields="employee { corp_email }", auto_case=False ) class Query(ObjectType): user = Field(User) _schema = build_schema(query=Query, auto_camelcase=True) except Exception as exc: pytest.fail(f"Unexpected Error {exc}") def test_provides_auto_camelcase_false(): try: class Employee(ObjectType): identifier = ID() email = String(name="corp_email") class User(ObjectType): identifier = ID() employee = Field(Employee) email = String() class Query(ObjectType): user = provides(Field(User), fields="employee { corp_email }") _schema = build_schema(query=Query, auto_camelcase=False) except Exception as exc: pytest.fail(f"Unexpected Error {exc}") def test_provides_auto_camelcase_true(): with pytest.raises(ValueError) as err: class Employee(ObjectType): identifier = ID() email = String(name="corp_email") class User(ObjectType): identifier = ID() employee = Field(Employee) email = String() class Query(ObjectType): user = provides(Field(User), fields="employee { corp_email }") _schema = build_schema(query=Query, auto_camelcase=True) assert ( str(err.value) == '@provides, field "corpEmail" does not exist on type "Employee"' ) def test_provides_auto_camelcase_with_auto_case_false(): try: class Employee(ObjectType): identifier = ID() email = String(name="corp_email") class User(ObjectType): identifier = ID() employee = Field(Employee) email = String() class Query(ObjectType): user = provides( Field(User), fields="employee { corp_email }", auto_case=False ) _schema = build_schema(query=Query, auto_camelcase=True) except Exception as exc: pytest.fail(f"Unexpected Error {exc}") graphene-federation-3.2.0/tests/test_extends.py000066400000000000000000000016571463304117000216600ustar00rootroot00000000000000import pytest from graphene import ID, ObjectType, String from graphene_directives import DirectiveValidationError from graphene_federation import build_schema, extends, key def test_extend_non_existing_field_failure(): """ Test that using the key decorator and providing a field that does not exist fails. """ with pytest.raises(ValueError) as err: @key("potato") @extends class A(ObjectType): id = ID() build_schema(types=(A,)) assert str(err.value) == '@key, field "potato" does not exist on type "A"' def test_multiple_extend_failure(): """ Test that the extend decorator can't be used more than once on a type. """ with pytest.raises(DirectiveValidationError) as err: @extends @extends class A(ObjectType): id = ID() potato = String() assert str(err.value) == "@extends is not repeatable, at: A" graphene-federation-3.2.0/tests/test_inaccessible.py000066400000000000000000000043671463304117000226330ustar00rootroot00000000000000from pathlib import Path import graphene from graphene import ObjectType from graphene_federation import LATEST_VERSION, build_schema, inaccessible from tests.util import file_handlers, sdl_query save_file, open_file = file_handlers(Path(__file__)) def test_inaccessible_interface(): @inaccessible class ReviewInterface(graphene.Interface): interfaced_body = graphene.String(required=True) @inaccessible class Review(graphene.ObjectType): class Meta: interfaces = (ReviewInterface,) id = inaccessible(graphene.Int(required=True)) body = graphene.String(required=True) class Query(ObjectType): in_stock_count = graphene.Int(required=True) build_schema( query=Query, federation_version=LATEST_VERSION, types=(ReviewInterface, Review) ) def test_inaccessible(): @inaccessible class Position(graphene.ObjectType): x = graphene.Int(required=True) y = inaccessible(graphene.Int(required=True)) class Query(ObjectType): in_stock_count = graphene.Int(required=True) schema = build_schema( query=Query, federation_version=LATEST_VERSION, types=(Position,) ) # save_file(str(schema), "1") # save_file(sdl_query(schema), "2") assert open_file("1") == str(schema) assert open_file("2") == sdl_query(schema) def test_inaccessible_union(): @inaccessible class Human(graphene.ObjectType): name = graphene.String() born_in = graphene.String() @inaccessible class Droid(graphene.ObjectType): name = inaccessible(graphene.String()) primary_function = graphene.String() @inaccessible class Starship(graphene.ObjectType): name = graphene.String() length = inaccessible(graphene.Int()) @inaccessible class SearchResult(graphene.Union): class Meta: types = (Human, Droid, Starship) class Query(ObjectType): in_stock_count = graphene.Int(required=True) schema = build_schema( query=Query, federation_version=LATEST_VERSION, types=(SearchResult,) ) # save_file(str(schema), "1") # save_file(sdl_query(schema), "2") assert open_file("1") == str(schema) assert open_file("2") == sdl_query(schema) graphene-federation-3.2.0/tests/test_key.py000066400000000000000000000104731463304117000207720ustar00rootroot00000000000000from pathlib import Path import pytest from graphene import Field, ID, ObjectType, String from graphene_federation import LATEST_VERSION, build_schema, key from tests.util import file_handlers, sdl_query save_file, open_file = file_handlers(Path(__file__)) def test_multiple_keys(): @key("identifier") @key("email") class User(ObjectType): identifier = ID() email = String() class Query(ObjectType): user = Field(User) schema = build_schema(query=Query, federation_version=LATEST_VERSION) # save_file(str(schema), "1") # save_file(sdl_query(schema), "2") assert open_file("1") == str(schema) assert open_file("2") == sdl_query(schema) def test_key_non_existing_field_failure(): """ Test that using the key decorator and providing a field that does not exist fails. """ with pytest.raises(ValueError) as err: @key("potato") class A(ObjectType): id = ID() build_schema(types=(A,), federation_version=LATEST_VERSION) assert '@key, field "potato" does not exist on type "A"' == str(err.value) def test_compound_primary_key(): class Organization(ObjectType): registration_number = ID() @key("id organization { registration_number }") class User(ObjectType): id = ID() organization = Field(Organization) class Query(ObjectType): user = Field(User) schema = build_schema(query=Query, federation_version=LATEST_VERSION) # save_file(str(schema), "1") # save_file(sdl_query(schema), "2") assert open_file("1") == str(schema) assert open_file("2") == sdl_query(schema) def test_compound_primary_key_with_depth(): class BusinessUnit(ObjectType): id = ID() name = String() class Organization(ObjectType): registration_number = ID() business_unit = Field(BusinessUnit) @key("id organization { business_unit {id name}}") class User(ObjectType): id = ID() organization = Field(Organization) class Query(ObjectType): user = Field(User) schema = build_schema(query=Query, federation_version=LATEST_VERSION) # save_file(str(schema), "1") # save_file(sdl_query(schema), "2") assert open_file("1") == str(schema) assert open_file("2") == sdl_query(schema) def test_invalid_compound_primary_key_failures(): class BusinessUnit(ObjectType): id = ID() name = String() class Organization(ObjectType): registration_number = ID() bu = Field(BusinessUnit) @key("id name organization { registration_number }") class User(ObjectType): id = ID() organization = Field(Organization) class Query(ObjectType): user = Field(User) with pytest.raises(ValueError) as err: # Field name absent on User ObjectType build_schema(query=Query, federation_version=LATEST_VERSION) assert '@key, field "name" does not exist on type "User"' == str(err.value) @key("id organization { name }") class User(ObjectType): id = ID() organization = Field(Organization) class Query(ObjectType): user = Field(User) with pytest.raises(ValueError) as err: # Presence of invalid field in organization field key build_schema(query=Query, federation_version=LATEST_VERSION) assert '@key, field "name" does not exist on type "Organization"' == str(err.value) @key("id organization { bu }") class User(ObjectType): id = ID() organization = Field(Organization) class Query(ObjectType): user = Field(User) with pytest.raises(ValueError) as err: # Presence of BusinessUnit in the key without subselection build_schema(query=Query, federation_version=LATEST_VERSION) assert '@key, type Organization, field "bu" needs sub selections.' == str(err.value) @key("id organization { bu {name { field }} }") class User(ObjectType): id = ID() organization = Field(Organization) class Query(ObjectType): user = Field(User) with pytest.raises(ValueError) as err: # Presence of subselection for the scalar 'name' field build_schema(query=Query, federation_version=LATEST_VERSION) assert '@key, type BusinessUnit, field "name" cannot have sub selections.' == str( err.value ) graphene-federation-3.2.0/tests/test_key_v1.py000066400000000000000000000022041463304117000213710ustar00rootroot00000000000000from pathlib import Path import pytest from graphene import Field, ID, ObjectType, String from graphene_federation import FederationVersion, build_schema, key from tests.util import file_handlers, sdl_query save_file, open_file = file_handlers(Path(__file__)) def test_multiple_keys(): @key("identifier") @key("email") class User(ObjectType): identifier = ID() email = String() class Query(ObjectType): user = Field(User) schema = build_schema(query=Query, federation_version=FederationVersion.VERSION_1_0) # save_file(str(schema), "1") # save_file(sdl_query(schema), "2") assert open_file("1") == str(schema) assert open_file("2") == sdl_query(schema) def test_key_non_existing_field_failure(): """ Test that using the key decorator and providing a field that does not exist fails. """ with pytest.raises(ValueError) as err: @key("potato") class A(ObjectType): id = ID() _ = build_schema(types=(A,), federation_version=FederationVersion.VERSION_1_0) assert '@key, field "potato" does not exist on type "A"' == str(err.value) graphene-federation-3.2.0/tests/test_override.py000066400000000000000000000025401463304117000220150ustar00rootroot00000000000000from pathlib import Path import pytest from graphene import Field, ID, ObjectType, String from graphene import Int from graphene_directives import DirectiveValidationError from graphene_federation import LATEST_VERSION, build_schema from graphene_federation import override from tests.util import file_handlers, sdl_query save_file, open_file = file_handlers(Path(__file__)) def test_chain_requires_failure(): """ Check that we can't nest call the override method on a field. """ with pytest.raises(DirectiveValidationError) as err: class A(ObjectType): something = override( override(String(), from_="subgraph-1"), from_="subgraph-2" ) assert "@override is not repeatable" in str(err.value) def test_override(): """ Check that requires can take more than one field as input. """ class Product(ObjectType): sku = override(ID(), from_="subgraph-1") size = override(Int(), from_="subgraph-2") weight = override(Int(), from_="subgraph-3", label="Test label") class Query(ObjectType): product = Field(Product) schema = build_schema(query=Query, federation_version=LATEST_VERSION) # save_file(str(schema), "1") # save_file(sdl_query(schema), "2") assert open_file("1") == str(schema) assert open_file("2") == sdl_query(schema) graphene-federation-3.2.0/tests/test_provides.py000066400000000000000000000052031463304117000220300ustar00rootroot00000000000000from pathlib import Path from graphene import Field, ObjectType, String from graphene import Int from graphene_federation import LATEST_VERSION, build_schema, extends, key from graphene_federation import external, provides from tests.util import file_handlers, sdl_query save_file, open_file = file_handlers(Path(__file__)) def test_provides(): """ https://www.apollographql.com/docs/federation/entities/#resolving-another-services-field-advanced """ @key("sku") class Product(ObjectType): sku = external(String(required=True)) name = external(String()) weight = external(Int()) class InStockCount(ObjectType): product = provides(Field(Product, required=True), fields="name") quantity = Int(required=True) class Query(ObjectType): in_stock_count = Field(InStockCount) schema = build_schema(query=Query, federation_version=LATEST_VERSION) # save_file(str(schema), "1") # save_file(sdl_query(schema), "2") assert open_file("1") == str(schema) assert open_file("2") == sdl_query(schema) def test_provides_multiple_fields(): """ https://www.apollographql.com/docs/federation/entities/#resolving-another-services-field-advanced """ @key("sku") class Product(ObjectType): sku = external(String(required=True)) name = external(String()) weight = external(Int()) class InStockCount(ObjectType): product = provides(Field(Product, required=True), fields="name weight") quantity = Int(required=True) class Query(ObjectType): in_stock_count = Field(InStockCount) schema = build_schema(query=Query, federation_version=LATEST_VERSION) # save_file(str(schema), "1") # save_file(sdl_query(schema), "2") assert open_file("1") == str(schema) assert open_file("2") == sdl_query(schema) def test_provides_multiple_fields_as_list(): """ https://www.apollographql.com/docs/federation/entities/#resolving-another-services-field-advanced """ @key("sku") @extends class Product(ObjectType): sku = external(String(required=True)) name = external(String()) weight = external(Int()) class InStockCount(ObjectType): product = provides(Field(Product, required=True), fields=["name", "weight"]) quantity = Int(required=True) class Query(ObjectType): in_stock_count = Field(InStockCount) schema = build_schema(query=Query, federation_version=LATEST_VERSION) # save_file(str(schema), "1") # save_file(sdl_query(schema), "2") assert open_file("1") == str(schema) assert open_file("2") == sdl_query(schema) graphene-federation-3.2.0/tests/test_provides_v1.py000066400000000000000000000052631463304117000224440ustar00rootroot00000000000000from pathlib import Path from graphene import Field, ObjectType, String from graphene import Int from graphene_federation import FederationVersion, build_schema, extends, key from graphene_federation import external, provides from tests.util import file_handlers, sdl_query save_file, open_file = file_handlers(Path(__file__)) def test_provides(): """ https://www.apollographql.com/docs/federation/entities/#resolving-another-services-field-advanced """ @key("sku") class Product(ObjectType): sku = external(String(required=True)) name = external(String()) weight = external(Int()) class InStockCount(ObjectType): product = provides(Field(Product, required=True), fields="name") quantity = Int(required=True) class Query(ObjectType): in_stock_count = Field(InStockCount) schema = build_schema(query=Query, federation_version=FederationVersion.VERSION_1_0) # save_file(str(schema), "1") # save_file(sdl_query(schema), "2") assert open_file("1") == str(schema) assert open_file("2") == sdl_query(schema) def test_provides_multiple_fields(): """ https://www.apollographql.com/docs/federation/entities/#resolving-another-services-field-advanced """ @key("sku") class Product(ObjectType): sku = external(String(required=True)) name = external(String()) weight = external(Int()) class InStockCount(ObjectType): product = provides(Field(Product, required=True), fields="name weight") quantity = Int(required=True) class Query(ObjectType): in_stock_count = Field(InStockCount) schema = build_schema(query=Query, federation_version=FederationVersion.VERSION_1_0) # save_file(str(schema), "1") # save_file(sdl_query(schema), "2") assert open_file("1") == str(schema) assert open_file("2") == sdl_query(schema) def test_provides_multiple_fields_as_list(): """ https://www.apollographql.com/docs/federation/entities/#resolving-another-services-field-advanced """ @key("sku") @extends class Product(ObjectType): sku = external(String(required=True)) name = external(String()) weight = external(Int()) class InStockCount(ObjectType): product = provides(Field(Product, required=True), fields=["name", "weight"]) quantity = Int(required=True) class Query(ObjectType): in_stock_count = Field(InStockCount) schema = build_schema(query=Query, federation_version=FederationVersion.VERSION_1_0) # save_file(str(schema), "1") # save_file(sdl_query(schema), "2") assert open_file("1") == str(schema) assert open_file("2") == sdl_query(schema) graphene-federation-3.2.0/tests/test_requires.py000066400000000000000000000052451463304117000220420ustar00rootroot00000000000000from pathlib import Path import pytest from graphene import Field, ID, ObjectType, String from graphene import Int from graphene_directives import DirectiveValidationError from graphene_federation import LATEST_VERSION, build_schema, key from graphene_federation import extends, external, requires from tests.util import file_handlers, sdl_query save_file, open_file = file_handlers(Path(__file__)) def test_chain_requires_failure(): """ Check that we can't nest call the requires method on a field. """ with pytest.raises(DirectiveValidationError) as err: class A(ObjectType): id = external(ID()) something = requires(requires(String(), fields="id"), fields="id3") assert "@requires is not repeatable" in str(err.value) def test_requires_multiple_fields(): """ Check that requires can take more than one field as input. """ @key("sku") @extends class Product(ObjectType): sku = external(ID()) size = external(Int()) weight = external(Int()) shipping_estimate = requires(String(), fields="size weight") class Query(ObjectType): product = Field(Product) schema = build_schema(query=Query, federation_version=LATEST_VERSION) # save_file(str(schema), "1") # save_file(sdl_query(schema), "2") assert open_file("1") == str(schema) assert open_file("2") == sdl_query(schema) def test_requires_multiple_fields_as_list(): """ Check that requires can take more than one field as input. """ @key("sku") @extends class Product(ObjectType): sku = external(ID()) size = external(Int()) weight = external(Int()) shipping_estimate = requires(String(), fields=["size", "weight"]) class Query(ObjectType): product = Field(Product) schema = build_schema(query=Query, federation_version=LATEST_VERSION) # save_file(str(schema), "1") # save_file(sdl_query(schema), "2") assert open_file("1") == str(schema) assert open_file("2") == sdl_query(schema) def test_requires_with_input(): """ Test checking that the issue https://github.com/preply/graphene-federation/pull/47 is resolved. """ @key("id") @extends class Acme(ObjectType): id = external(ID(required=True)) age = external(Int()) foo = requires(Field(String, someInput=String()), fields="age") class Query(ObjectType): acme = Field(Acme) schema = build_schema(query=Query, federation_version=LATEST_VERSION) # save_file(str(schema), "1") # save_file(sdl_query(schema), "2") assert open_file("1") == str(schema) assert open_file("2") == sdl_query(schema) graphene-federation-3.2.0/tests/test_requires_v1.py000066400000000000000000000053251463304117000224470ustar00rootroot00000000000000from pathlib import Path import pytest from graphene import Field, ID, ObjectType, String from graphene import Int from graphene_directives import DirectiveValidationError from graphene_federation import FederationVersion, build_schema, key from graphene_federation import extends, external, requires from tests.util import file_handlers, sdl_query save_file, open_file = file_handlers(Path(__file__)) def test_chain_requires_failure(): """ Check that we can't nest call the requires method on a field. """ with pytest.raises(DirectiveValidationError) as err: class A(ObjectType): id = external(ID()) something = requires(requires(String(), fields="id"), fields="id3") assert "@requires is not repeatable" in str(err.value) def test_requires_multiple_fields(): """ Check that requires can take more than one field as input. """ @key("sku") @extends class Product(ObjectType): sku = external(ID()) size = external(Int()) weight = external(Int()) shipping_estimate = requires(String(), fields="size weight") class Query(ObjectType): product = Field(Product) schema = build_schema(query=Query, federation_version=FederationVersion.VERSION_1_0) # save_file(str(schema), "1") # save_file(sdl_query(schema), "2") assert open_file("1") == str(schema) assert open_file("2") == sdl_query(schema) def test_requires_multiple_fields_as_list(): """ Check that requires can take more than one field as input. """ @key("sku") @extends class Product(ObjectType): sku = external(ID()) size = external(Int()) weight = external(Int()) shipping_estimate = requires(String(), fields=["size", "weight"]) class Query(ObjectType): product = Field(Product) schema = build_schema(query=Query, federation_version=FederationVersion.VERSION_1_0) # save_file(str(schema), "1") # save_file(sdl_query(schema), "2") assert open_file("1") == str(schema) assert open_file("2") == sdl_query(schema) def test_requires_with_input(): """ Test checking that the issue https://github.com/preply/graphene-federation/pull/47 is resolved. """ @key("id") @extends class Acme(ObjectType): id = external(ID(required=True)) age = external(Int()) foo = requires(Field(String, someInput=String()), fields="age") class Query(ObjectType): acme = Field(Acme) schema = build_schema(query=Query, federation_version=FederationVersion.VERSION_1_0) # save_file(str(schema), "1") # save_file(sdl_query(schema), "2") assert open_file("1") == str(schema) assert open_file("2") == sdl_query(schema) graphene-federation-3.2.0/tests/test_scalar.py000066400000000000000000000023631463304117000214460ustar00rootroot00000000000000from pathlib import Path from typing import Any import graphene from graphene import ObjectType, String from graphene import Scalar from graphene_federation import LATEST_VERSION, build_schema from graphene_federation import inaccessible, shareable from tests.util import file_handlers, sdl_query save_file, open_file = file_handlers(Path(__file__)) def test_custom_scalar(): class AddressScalar(Scalar): base = String @staticmethod def coerce_address(value: Any): ... serialize = coerce_address parse_value = coerce_address @staticmethod def parse_literal(ast): ... @shareable class TestScalar(graphene.ObjectType): test_shareable_scalar = shareable(String(x=AddressScalar())) test_inaccessible_scalar = inaccessible(String(x=AddressScalar())) class Query(ObjectType): test = String(x=AddressScalar()) test2 = graphene.List(AddressScalar, required=True) schema = build_schema( query=Query, federation_version=LATEST_VERSION, types=(TestScalar,) ) # save_file(str(schema), "1") # save_file(sdl_query(schema), "2") assert open_file("1") == str(schema) assert open_file("2") == sdl_query(schema) graphene-federation-3.2.0/tests/test_schema_annotation.py000066400000000000000000000067631463304117000237030ustar00rootroot00000000000000from pathlib import Path from graphene import Field, ID, ObjectType, String from graphene import NonNull from graphql import graphql_sync from graphene_federation import LATEST_VERSION, build_schema, key from graphene_federation import extends, external from tests.util import file_handlers, sdl_query save_file, open_file = file_handlers(Path(__file__)) # ------------------------ # User service # ------------------------ users = [ {"user_id": "1", "name": "Jane", "email": "jane@mail.com"}, {"user_id": "2", "name": "Jack", "email": "jack@mail.com"}, {"user_id": "3", "name": "Mary", "email": "mary@mail.com"}, ] @key("user_id") @key("email") class User(ObjectType): user_id = ID(required=True) email = String(required=True) name = String() def __resolve_reference(self, info, *args, **kwargs): if self.id: user = next(filter(lambda x: x["id"] == self.id, users)) elif self.email: user = next(filter(lambda x: x["email"] == self.email, users)) return User(**user) class UserQuery(ObjectType): user = Field(User, user_id=ID(required=True)) def resolve_user(self, info, user_id, *args, **kwargs): return User(**next(filter(lambda x: x["user_id"] == user_id, users))) user_schema = build_schema(query=UserQuery, federation_version=LATEST_VERSION) # ------------------------ # Chat service # ------------------------ chat_messages = [ {"id": "1", "user_id": "1", "text": "Hi"}, {"id": "2", "user_id": "1", "text": "How is the weather?"}, {"id": "3", "user_id": "2", "text": "Who are you"}, {"id": "4", "user_id": "3", "text": "Don't be rude Jack"}, {"id": "5", "user_id": "3", "text": "Hi Jane"}, {"id": "6", "user_id": "2", "text": "Sorry but weather sucks so I am upset"}, ] @extends @key("user_id") class ChatUser(ObjectType): user_id = external(ID(required=True)) class ChatMessage(ObjectType): id = ID(required=True) text = String() user_id = ID() user = NonNull(ChatUser) def resolve_user(self, info, *args, **kwargs): return ChatUser(user_id=self.user_id) class ChatQuery(ObjectType): message = Field(ChatMessage, id=ID(required=True)) def resolve_message(self, info, id, *args, **kwargs): return ChatMessage(**next(filter(lambda x: x["id"] == id, chat_messages))) chat_schema = build_schema(query=ChatQuery, federation_version=LATEST_VERSION) # ------------------------ # Tests # ------------------------ def test_user_schema(): """ Check that the user schema has been annotated correctly and that a request to retrieve a user works. """ assert open_file("1") == str(user_schema) assert open_file("2") == sdl_query(user_schema) query = """ query { user(userId: "2") { name } } """ result = graphql_sync(user_schema.graphql_schema, query) assert not result.errors assert result.data == {"user": {"name": "Jack"}} def test_chat_schema(): """ Check that the chat schema has been annotated correctly and that a request to retrieve a chat message works. """ assert open_file("1") == str(chat_schema) assert open_file("2") == sdl_query(chat_schema) # Query the message field query = """ query { message(id: "4") { text userId } } """ result = graphql_sync(chat_schema.graphql_schema, query) assert not result.errors assert result.data == {"message": {"text": "Don't be rude Jack", "userId": "3"}} graphene-federation-3.2.0/tests/test_schema_annotation_v1.py000066400000000000000000000070361463304117000243030ustar00rootroot00000000000000from pathlib import Path from graphene import Field, ID, ObjectType, String from graphene import NonNull from graphql import graphql_sync from graphene_federation import FederationVersion, build_schema, key from graphene_federation import extends, external from tests.util import file_handlers, sdl_query save_file, open_file = file_handlers(Path(__file__)) # ------------------------ # User service # ------------------------ users = [ {"user_id": "1", "name": "Jane", "email": "jane@mail.com"}, {"user_id": "2", "name": "Jack", "email": "jack@mail.com"}, {"user_id": "3", "name": "Mary", "email": "mary@mail.com"}, ] @key("user_id") @key("email") class User(ObjectType): user_id = ID(required=True) email = String(required=True) name = String() def __resolve_reference(self, info, *args, **kwargs): if self.id: user = next(filter(lambda x: x["id"] == self.id, users)) elif self.email: user = next(filter(lambda x: x["email"] == self.email, users)) return User(**user) class UserQuery(ObjectType): user = Field(User, user_id=ID(required=True)) def resolve_user(self, info, user_id, *args, **kwargs): return User(**next(filter(lambda x: x["user_id"] == user_id, users))) user_schema = build_schema( query=UserQuery, federation_version=FederationVersion.VERSION_1_0 ) # ------------------------ # Chat service # ------------------------ chat_messages = [ {"id": "1", "user_id": "1", "text": "Hi"}, {"id": "2", "user_id": "1", "text": "How is the weather?"}, {"id": "3", "user_id": "2", "text": "Who are you"}, {"id": "4", "user_id": "3", "text": "Don't be rude Jack"}, {"id": "5", "user_id": "3", "text": "Hi Jane"}, {"id": "6", "user_id": "2", "text": "Sorry but weather sucks so I am upset"}, ] @key("user_id") @extends class ChatUser(ObjectType): user_id = external(ID(required=True)) class ChatMessage(ObjectType): id = ID(required=True) text = String() user_id = ID() user = NonNull(ChatUser) def resolve_user(self, info, *args, **kwargs): return ChatUser(user_id=self.user_id) class ChatQuery(ObjectType): message = Field(ChatMessage, id=ID(required=True)) def resolve_message(self, info, id, *args, **kwargs): return ChatMessage(**next(filter(lambda x: x["id"] == id, chat_messages))) chat_schema = build_schema( query=ChatQuery, federation_version=FederationVersion.VERSION_1_0 ) # ------------------------ # Tests # ------------------------ def test_user_schema(): """ Check that the user schema has been annotated correctly and that a request to retrieve a user works. """ assert open_file("1") == str(user_schema) assert open_file("2") == sdl_query(user_schema) query = """ query { user(userId: "2") { name } } """ result = graphql_sync(user_schema.graphql_schema, query) assert not result.errors assert result.data == {"user": {"name": "Jack"}} def test_chat_schema(): """ Check that the chat schema has been annotated correctly and that a request to retrieve a chat message works. """ assert open_file("1") == str(chat_schema) assert open_file("2") == sdl_query(chat_schema) # Query the message field query = """ query { message(id: "4") { text userId } } """ result = graphql_sync(chat_schema.graphql_schema, query) assert not result.errors assert result.data == {"message": {"text": "Don't be rude Jack", "userId": "3"}} graphene-federation-3.2.0/tests/test_shareable.py000066400000000000000000000051021463304117000221210ustar00rootroot00000000000000from pathlib import Path import graphene import pytest from graphene import ObjectType from graphene_directives import DirectiveValidationError from graphene_federation import LATEST_VERSION, build_schema from graphene_federation import shareable from tests.util import file_handlers, sdl_query save_file, open_file = file_handlers(Path(__file__)) def test_shareable_interface_failures(): with pytest.raises(DirectiveValidationError) as err: @shareable class ReviewInterface(graphene.Interface): interfaced_body = graphene.String(required=True) @shareable class Review(graphene.ObjectType): class Meta: interfaces = (ReviewInterface,) id = shareable(graphene.Int(required=True)) body = graphene.String(required=True) class Query(ObjectType): in_stock_count = graphene.Int(required=True) build_schema( query=Query, federation_version=LATEST_VERSION, types=(ReviewInterface, Review), ) assert "@shareable cannot be used for ReviewInterface" in str(err.value) def test_shareable(): @shareable class Position(graphene.ObjectType): x = graphene.Int(required=True) y = shareable(graphene.Int(required=True)) class Query(ObjectType): in_stock_count = graphene.Int(required=True) schema = build_schema( query=Query, federation_version=LATEST_VERSION, types=(Position,) ) # save_file(str(schema), "1") # save_file(sdl_query(schema), "2") assert open_file("1") == str(schema) assert open_file("2") == sdl_query(schema) def test_shareable_union(): with pytest.raises(DirectiveValidationError) as err: @shareable class Human(graphene.ObjectType): name = graphene.String() born_in = graphene.String() @shareable class Droid(graphene.ObjectType): name = shareable(graphene.String()) primary_function = graphene.String() @shareable class Starship(graphene.ObjectType): name = graphene.String() length = shareable(graphene.Int()) @shareable class SearchResult(graphene.Union): class Meta: types = (Human, Droid, Starship) class Query(ObjectType): in_stock_count = graphene.Int(required=True) _ = build_schema( query=Query, federation_version=LATEST_VERSION, types=(SearchResult,) ) assert "@shareable cannot be used for SearchResult" in str(err.value) graphene-federation-3.2.0/tests/util.py000066400000000000000000000021621463304117000201140ustar00rootroot00000000000000import inspect import os from collections.abc import Callable from pathlib import Path from typing import Any from graphql import graphql_sync def file_handlers( path: Path, ) -> tuple[Callable[[Any, str], None], Callable[[str], str]]: curr_dir = path.parent file_name = path.name.replace(".py", "") try: os.mkdir(f"{curr_dir}/gql/{file_name}") except FileExistsError: pass def save_file(data, extra_path: str = ""): function_name = inspect.stack()[1].function with open( f"{curr_dir}/gql/{file_name}/{function_name}_{extra_path}.graphql", "w" ) as f: f.write(str(data)) def open_file(extra_path: str = ""): function_name = inspect.stack()[1].function with open( f"{curr_dir}/gql/{file_name}/{function_name}_{extra_path}.graphql", "r" ) as f: return f.read() return save_file, open_file def sdl_query(schema) -> str: query = "query { _service { sdl } }" result = graphql_sync(schema.graphql_schema, query) assert not result.errors return result.data["_service"]["sdl"]