apollo-language-server 0.7.0

A GraphQL language server with first-class support for Apollo Federation
Documentation
use apollo_compiler::ast::ScalarTypeDefinition;
use once_cell::sync::Lazy;
use std::collections::HashMap;

use super::{Spec, SpecDirective, SpecStatus, SpecType, SpecsByVersion};

pub const FEDERATION_SPEC_NAME: &str = "federation";
pub const LINK_DIRECTIVE: &str = "link";
pub const KEY_DIRECTIVE: &str = "key";
pub const KEY_DESCRIPTION: &str = "The [`@key`](https://www.apollographql.com/docs/federation/federated-schemas/federated-directives/#key) directive designates an object or interface type as an entity and specifies its key fields. Key fields are a set of fields that a 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 if your subgraph library supports repeatable directives.";

pub const EXTENDS_DIRECTIVE: &str = "extends";
pub const EXTENDS_DESCRIPTION: &str = "The [`@extends`](https://www.apollographql.com/docs/federation/federated-schemas/federated-directives/#extends) directive is used to indicate that a type is an extension of another type. Its purpose was to enable type extensions in libraries that lacked support for the `extend` keyword. Type extension is no longer needed in Federation 2.0 and above.";

pub const EXTERNAL_DIRECTIVE: &str = "external";
pub const EXTERNAL_DESCRIPTION_V1: &str = "The [`@external`](https://www.apollographql.com/docs/federation/federated-schemas/federated-directives/#external) directive indicates that this subgraph 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`.";

pub const EXTERNAL_DESCRIPTION_V2: &str = "The [`@external`](https://www.apollographql.com/docs/federation/federated-schemas/federated-directives/#external) directive indicates that this subgraph can't resolve a particular object field (or all of its fields), 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`.";

pub const PROVIDES_DIRECTIVE: &str = "provides";
pub const PROVIDES_DESCRIPTION: &str = "The [`@provides`](https://www.apollographql.com/docs/federation/federated-schemas/federated-directives/#provides) directive 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).\n\nIf a subgraph can always resolve a particular entity field, do not apply this directive.\n\nUsing this directive is always an optional optimization. It can reduce the total number of subgraphs that your router needs to communicate with to resolve certain operations, which can improve performance.";

pub const REQUIRES_DIRECTIVE: &str = "requires";
pub const REQUIRES_DESCRIPTION: &str = "A [`@requires`](https://www.apollographql.com/docs/federation/federated-schemas/federated-directives/#requires) directive 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.";

pub const TAG_DIRECTIVE: &str = "tag";
pub const TAG_DESCRIPTION: &str = "The [`@tag`](https://www.apollographql.com/docs/federation/federated-schemas/federated-directives/#tag) directive 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. The GraphOS Enterprise contracts feature uses `@tag` with its inclusion and exclusion filters.";

pub const INACCESSIBLE_DIRECTIVE: &str = "inaccessible";
pub const INACCESSIBLE_DESCRIPTION: &str = "The [`@inaccessible`](https://www.apollographql.com/docs/federation/federated-schemas/federated-directives/#inaccessible) directive 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.";

pub const OVERRIDE_DIRECTIVE: &str = "override";
pub const OVERRIDE_DESCRIPTION: &str = "The [`@override`](https://www.apollographql.com/docs/federation/federated-schemas/federated-directives/#override) directive 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.\n\nYou can apply `@override` to entity fields and fields of the root operation types (such as `Query` and `Mutation`).";

pub const SHAREABLE_DIRECTIVE: &str = "shareable";
pub const SHAREABLE_DESCRIPTION: &str = "The [`@shareable`](https://www.apollographql.com/docs/federation/federated-schemas/federated-directives/#shareable) directive 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).";

pub const COMPOSE_DIRECTIVE: &str = "composeDirective";
pub const COMPOSE_DIRECTIVE_DESCRIPTION: &str = "The [`@composeDirective`](https://www.apollographql.com/docs/federation/federated-schemas/federated-directives/#composedirective) directive 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).";

pub const INTERFACE_OBJECT_DIRECTIVE: &str = "interfaceObject";
pub const INTERFACE_OBJECT_DESCRIPTION: &str = "The [`@interfaceObject`](https://www.apollographql.com/docs/federation/federated-schemas/federated-directives/#interfaceobject) directive 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.\n\nDuring composition, the fields of every @interfaceObject are added both to their corresponding interface definition and to all entity types that implement that interface.";

pub const AUTHENTICATED_DIRECTIVE: &str = "authenticated";
pub const AUTHENTICATED_DESCRIPTION: &str = "The [`@authenticated`](https://www.apollographql.com/docs/federation/federated-schemas/federated-directives/#authenticated) directive indicates to composition that the target element is accessible only to the authenticated supergraph users. For more granular access control, see the `@requiresScopes` directive.";

pub const REQUIRES_SCOPES_DIRECTIVE: &str = "requiresScopes";
pub const REQUIRES_SCOPES_DESCRIPTION: &str = "The [`@requiresScopes`](https://www.apollographql.com/docs/federation/federated-schemas/federated-directives/#requiresscopes) directive indicates to composition that the target element is accessible only to the authenticated supergraph users with the appropriate JWT scopes.";

pub const POLICY_DIRECTIVE: &str = "policy";
pub const POLICY_DESCRIPTION: &str = "The [`@policy`](https://www.apollographql.com/docs/federation/federated-schemas/federated-directives/#policy) directive indicates to composition that the target element is restricted based on authorization policies that are evaluated in a Rhai script or coprocessor.";

pub const SCOPE_SCALAR: &str = "Scope";
pub const POLICY_SCALAR: &str = "Policy";

pub const PROGRESSIVE_OVERRIDE_DESCRIPTION: &str = "The [`@override`](https://www.apollographql.com/docs/federation/federated-schemas/federated-directives/#override) directive 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.\n\nYou can apply `@override` to entity fields and fields of the root operation types (such as `Query` and `Mutation`). A second `label` argument can be used to progressively override a field. See [the docs](https://www.apollographql.com/docs/federation/entities/migrate-fields/#incremental-migration-with-progressive-override) for more information.";

pub const CONTEXT_DIRECTIVE: &str = "context";
pub const CONTEXT_DESCRIPTION: &str = "The [`@context`](https://www.apollographql.com/docs/federation/federated-schemas/federated-directives/#context) directive defines a named context from which a field of the annotated type can be passed to a receiver of the context. The receiver must be a field annotated with the `@fromContext` directive.";

pub const FROM_CONTEXT_DIRECTIVE: &str = "fromContext";
pub const FROM_CONTEXT_DESCRIPTION: &str = "The [`@fromContext`](https://www.apollographql.com/docs/federation/federated-schemas/federated-directives/#fromcontext) directive sets the context from which to receive the value of the annotated field. The context must have been defined with the `@context` directive.";

pub const CONTEXT_FIELD_VALUE_SCALAR: &str = "ContextFieldValue";
pub const FIELD_SET_SCALAR_V1: &str = "_FieldSet";
pub const FIELD_SET_SCALAR_V2: &str = "FieldSet";

pub const CACHE_TAG_DIRECTIVE: &str = "cacheTag";
pub const CACHE_TAG_DESCRIPTION: &str = "The [`@cacheTag`](https://www.apollographql.com/docs/graphos/routing/performance/caching/response-caching/invalidation#invalidation-methods) directive defines a format to define a cache tag that can be used to invalidate cached data.";

pub static FEDERATION_SPECS_BY_VERSION: Lazy<SpecsByVersion> = {
    Lazy::new(|| {
        // v1 Directives
        let extends = SpecDirective::new(
            "directive @extends on OBJECT | INTERFACE",
            EXTENDS_DESCRIPTION,
        );

        let external_v1 = SpecDirective::new(
            "directive @external on FIELD_DEFINITION",
            EXTERNAL_DESCRIPTION_V1,
        );

        let field_set_v1 = SpecType::<ScalarTypeDefinition>::new(FIELD_SET_SCALAR_V1);
        let key_v1 = SpecDirective::new(
            "directive @key(fields: _FieldSet!) repeatable on OBJECT | INTERFACE",
            KEY_DESCRIPTION,
        );

        let provides_v1 = SpecDirective::new(
            "directive @provides(fields: _FieldSet!) on FIELD_DEFINITION",
            PROVIDES_DESCRIPTION,
        );

        let requires_v1 = SpecDirective::new(
            "directive @requires(fields: _FieldSet!) on FIELD_DEFINITION",
            REQUIRES_DESCRIPTION,
        );

        let tag_directive_v01 = SpecDirective::new("directive @tag(name: String!) repeatable on FIELD_DEFINITION | INTERFACE | OBJECT | UNION", TAG_DESCRIPTION);

        let tag_directive_v02 = SpecDirective::new(
            "directive @tag(name: String!) repeatable on FIELD_DEFINITION | INTERFACE | OBJECT | UNION | ARGUMENT_DEFINITION | SCALAR | ENUM | ENUM_VALUE | INPUT_OBJECT | INPUT_FIELD_DEFINITION", TAG_DESCRIPTION
        );

        let tag_directive_v03 = SpecDirective::new(
"directive @tag(name: String!) repeatable on FIELD_DEFINITION | INTERFACE | OBJECT | UNION | ARGUMENT_DEFINITION | SCALAR | ENUM | ENUM_VALUE | INPUT_OBJECT | INPUT_FIELD_DEFINITION | SCHEMA",
TAG_DESCRIPTION);

        // v2.0 Directives
        let inaccessible= SpecDirective::new(
            "directive @inaccessible on FIELD_DEFINITION | INTERFACE | OBJECT | UNION | ARGUMENT_DEFINITION | SCALAR | ENUM | ENUM_VALUE | INPUT_OBJECT | INPUT_FIELD_DEFINITION", INACCESSIBLE_DESCRIPTION
        );

        let field_set_v2 = SpecType::<ScalarTypeDefinition>::new(FIELD_SET_SCALAR_V2);
        let key_v2= SpecDirective::new(
            "directive @key(fields: FieldSet!, resolvable: Boolean = true) repeatable on OBJECT | INTERFACE", KEY_DESCRIPTION
        );

        let provides_v2 = SpecDirective::new(
            "directive @provides(fields: FieldSet!) on FIELD_DEFINITION",
            PROVIDES_DESCRIPTION,
        );

        let requires_v2 = SpecDirective::new(
            "directive @requires(fields: FieldSet!) on FIELD_DEFINITION",
            REQUIRES_DESCRIPTION,
        );

        let external_v2 = SpecDirective::new(
            "directive @external on FIELD_DEFINITION | OBJECT",
            EXTERNAL_DESCRIPTION_V2,
        );

        let r#override = SpecDirective::new(
            "directive @override(from: String!) on FIELD_DEFINITION",
            OVERRIDE_DESCRIPTION,
        );

        let shareable = SpecDirective::new(
            "directive @shareable on FIELD_DEFINITION | OBJECT",
            SHAREABLE_DESCRIPTION,
        );

        // v2.1 Directives
        let compose_directive = SpecDirective::new(
            "directive @composeDirective(name: String!) repeatable on SCHEMA",
            COMPOSE_DIRECTIVE_DESCRIPTION,
        );

        // v2.3 Directives
        let interface_object = SpecDirective::new(
            "directive @interfaceObject on OBJECT",
            INTERFACE_OBJECT_DESCRIPTION,
        );

        // v2.5 Directives
        let authenticated = SpecDirective::new(
            "directive @authenticated on FIELD_DEFINITION | OBJECT | INTERFACE | SCALAR | ENUM",
            AUTHENTICATED_DESCRIPTION,
        );

        let scope = SpecType::<ScalarTypeDefinition>::new(SCOPE_SCALAR);
        let requires_scopes= SpecDirective::new(
            "directive @requiresScopes(scopes: [[Scope!]!]!) on FIELD_DEFINITION | OBJECT | INTERFACE | SCALAR | ENUM", REQUIRES_SCOPES_DESCRIPTION
        );

        // v2.6 Directives
        let policy_scalar = SpecType::<ScalarTypeDefinition>::new(POLICY_SCALAR);
        let policy= SpecDirective::new(
            "directive @policy(policies: [[Policy!]!]!) on FIELD_DEFINITION | OBJECT | INTERFACE | SCALAR | ENUM", POLICY_DESCRIPTION
        );

        // v2.7 Directives
        let progressive_override = SpecDirective::new(
            "directive @override(from: String!, label: String) on FIELD_DEFINITION",
            PROGRESSIVE_OVERRIDE_DESCRIPTION,
        );

        // v2.8 Directives
        let context = SpecDirective::new(
            "directive @context(name: String!) on OBJECT | INTERFACE | UNION",
            CONTEXT_DESCRIPTION,
        );

        let context_field_value_scalar =
            SpecType::<ScalarTypeDefinition>::new(CONTEXT_FIELD_VALUE_SCALAR);

        let from_context = SpecDirective::new(
            "directive @fromContext(field: String!) on ARGUMENT_DEFINITION",
            FROM_CONTEXT_DESCRIPTION,
        );

        // v2.12 Directives
        let cache_tag = SpecDirective::new(
            "directive @cacheTag(format: String!) on OBJECT | FIELD_DEFINITION",
            CACHE_TAG_DESCRIPTION,
        );

        let fed1_0 = Spec {
            status: SpecStatus::Deprecated,
            scalars: HashMap::from([(FIELD_SET_SCALAR_V1.to_string(), field_set_v1)]),
            input_types: HashMap::new(),
            directives: HashMap::from([
                (EXTERNAL_DIRECTIVE.to_string(), external_v1),
                (EXTENDS_DIRECTIVE.to_string(), extends.clone()),
                (KEY_DIRECTIVE.to_string(), key_v1),
                (PROVIDES_DIRECTIVE.to_string(), provides_v1),
                (REQUIRES_DIRECTIVE.to_string(), requires_v1),
                (TAG_DIRECTIVE.to_string(), tag_directive_v01), // Introduced in fed 1.1
            ]),
            enums: HashMap::new(),
        };

        let fed2_0 = Spec {
            status: SpecStatus::Supported,
            scalars: HashMap::from([(FIELD_SET_SCALAR_V2.to_string(), field_set_v2)]),
            input_types: HashMap::new(),
            directives: HashMap::from([
                (EXTERNAL_DIRECTIVE.to_string(), external_v2),
                (EXTENDS_DIRECTIVE.to_string(), extends),
                (KEY_DIRECTIVE.to_string(), key_v2),
                (PROVIDES_DIRECTIVE.to_string(), provides_v2),
                (REQUIRES_DIRECTIVE.to_string(), requires_v2),
                (INACCESSIBLE_DIRECTIVE.to_string(), inaccessible),
                (OVERRIDE_DIRECTIVE.to_string(), r#override),
                (SHAREABLE_DIRECTIVE.to_string(), shareable),
                (TAG_DIRECTIVE.to_string(), tag_directive_v02),
            ]),
            enums: HashMap::new(),
        };

        let mut fed2_1 = fed2_0.clone();
        fed2_1
            .directives
            .insert(COMPOSE_DIRECTIVE.to_string(), compose_directive);

        let fed2_2 = fed2_1.clone();

        let mut fed2_3 = fed2_2.clone();
        fed2_3.directives.extend([
            (INTERFACE_OBJECT_DIRECTIVE.to_string(), interface_object),
            (TAG_DIRECTIVE.to_string(), tag_directive_v03),
        ]);

        let fed2_4 = fed2_3.clone();

        let mut fed2_5 = fed2_4.clone();
        fed2_5.scalars.extend([(SCOPE_SCALAR.to_string(), scope)]);
        fed2_5.directives.extend([
            (AUTHENTICATED_DIRECTIVE.to_string(), authenticated),
            (REQUIRES_SCOPES_DIRECTIVE.to_string(), requires_scopes),
        ]);

        let mut fed2_6 = fed2_5.clone();
        fed2_6
            .scalars
            .insert(POLICY_SCALAR.to_string(), policy_scalar);
        fed2_6
            .directives
            .insert(POLICY_DIRECTIVE.to_string(), policy);

        let mut fed2_7 = fed2_6.clone();
        fed2_7
            .directives
            .insert(OVERRIDE_DIRECTIVE.to_string(), progressive_override);

        let mut fed2_8 = fed2_7.clone();
        fed2_8.scalars.insert(
            CONTEXT_FIELD_VALUE_SCALAR.to_string(),
            context_field_value_scalar,
        );
        fed2_8.directives.extend([
            (CONTEXT_DIRECTIVE.to_string(), context),
            (FROM_CONTEXT_DIRECTIVE.to_string(), from_context),
        ]);

        let fed2_9 = fed2_8.clone();
        let fed2_10 = fed2_9.clone();
        let fed2_11 = fed2_10.clone();
        let mut fed2_12 = fed2_11.clone();
        fed2_12
            .directives
            .extend([(CACHE_TAG_DIRECTIVE.to_string(), cache_tag)]);
        fed2_12.status = SpecStatus::Latest;

        HashMap::from([
            ("1.0".to_string(), fed1_0),
            ("2.0".to_string(), fed2_0),
            ("2.1".to_string(), fed2_1),
            ("2.2".to_string(), fed2_2),
            ("2.3".to_string(), fed2_3),
            ("2.4".to_string(), fed2_4),
            ("2.5".to_string(), fed2_5),
            ("2.6".to_string(), fed2_6),
            ("2.7".to_string(), fed2_7),
            ("2.8".to_string(), fed2_8),
            ("2.9".to_string(), fed2_9),
            ("2.10".to_string(), fed2_10),
            ("2.11".to_string(), fed2_11),
            ("2.12".to_string(), fed2_12),
        ])
    })
};