apollo_federation/link/
spec_registry.rs1use std::collections::BTreeSet;
2use std::collections::HashMap;
3use std::str;
4use std::sync::LazyLock;
5
6use apollo_compiler::Name;
7use apollo_compiler::name;
8
9use crate::connectors::spec::CONNECT_VERSIONS;
10use crate::link::Link;
11use crate::link::authenticated_spec_definition::AUTHENTICATED_VERSIONS;
12use crate::link::cache_tag_spec_definition::CACHE_TAG_VERSIONS;
13use crate::link::context_spec_definition::CONTEXT_VERSIONS;
14use crate::link::cost_spec_definition::COST_VERSIONS;
15use crate::link::federation_spec_definition::FEDERATION_VERSIONS;
16use crate::link::inaccessible_spec_definition::INACCESSIBLE_VERSIONS;
17use crate::link::join_spec_definition::JOIN_VERSIONS;
18use crate::link::policy_spec_definition::POLICY_VERSIONS;
19use crate::link::requires_scopes_spec_definition::REQUIRES_SCOPES_VERSIONS;
20use crate::link::spec::Identity;
21use crate::link::spec::Url;
22use crate::link::spec::Version;
23use crate::link::spec_definition::SpecDefinition;
24use crate::link::spec_definition::SpecDefinitions;
25use crate::link::tag_spec_definition::TAG_VERSIONS;
26use crate::schema::type_and_directive_specification::DirectiveCompositionSpecification;
27use crate::schema::type_and_directive_specification::DirectiveSpecification;
28
29pub(crate) const APOLLO_SPEC_DOMAIN: &str = "https://specs.apollo.dev";
30
31impl Identity {
32 pub const AUTHENTICATED_NAME: Name = name!("authenticated");
33 pub fn authenticated_identity() -> Identity {
34 Identity {
35 domain: APOLLO_SPEC_DOMAIN.to_string(),
36 name: Self::AUTHENTICATED_NAME.into(),
37 }
38 }
39
40 pub const CACHE_TAG_NAME: Name = name!("cacheTag");
41 pub fn cache_tag_identity() -> Identity {
42 Identity {
43 domain: APOLLO_SPEC_DOMAIN.to_string(),
44 name: Self::CACHE_TAG_NAME.into(),
45 }
46 }
47
48 pub const CONNECT_NAME: Name = name!("connect");
49 pub fn connect_identity() -> Identity {
50 Identity {
51 domain: APOLLO_SPEC_DOMAIN.to_string(),
52 name: Self::CONNECT_NAME.into(),
53 }
54 }
55
56 pub const CONTEXT_NAME: Name = name!("context");
57 pub fn context_identity() -> Identity {
58 Identity {
59 domain: APOLLO_SPEC_DOMAIN.to_string(),
60 name: Self::CONTEXT_NAME.into(),
61 }
62 }
63
64 pub const CORE_NAME: Name = name!("core");
65 pub fn core_identity() -> Identity {
66 Identity {
67 domain: APOLLO_SPEC_DOMAIN.to_string(),
68 name: Self::CORE_NAME.into(),
69 }
70 }
71
72 pub const COST_NAME: Name = name!("cost");
73 pub fn cost_identity() -> Identity {
74 Identity {
75 domain: APOLLO_SPEC_DOMAIN.to_string(),
76 name: Self::COST_NAME.into(),
77 }
78 }
79
80 pub const FEDERATION_NAME: Name = name!("federation");
81 pub fn federation_identity() -> Identity {
82 Identity {
83 domain: APOLLO_SPEC_DOMAIN.to_string(),
84 name: Self::FEDERATION_NAME.into(),
85 }
86 }
87
88 pub const INACCESSIBLE_NAME: Name = name!("inaccessible");
89 pub fn inaccessible_identity() -> Identity {
90 Identity {
91 domain: APOLLO_SPEC_DOMAIN.to_string(),
92 name: Self::INACCESSIBLE_NAME.into(),
93 }
94 }
95
96 pub const JOIN_NAME: Name = name!("join");
97 pub fn join_identity() -> Identity {
98 Identity {
99 domain: APOLLO_SPEC_DOMAIN.to_string(),
100 name: Self::JOIN_NAME.into(),
101 }
102 }
103
104 pub const LINK_NAME: Name = name!("link");
105 pub fn link_identity() -> Identity {
106 Identity {
107 domain: APOLLO_SPEC_DOMAIN.to_string(),
108 name: Self::LINK_NAME.into(),
109 }
110 }
111
112 pub const POLICY_NAME: Name = name!("policy");
113 pub fn policy_identity() -> Identity {
114 Identity {
115 domain: APOLLO_SPEC_DOMAIN.to_string(),
116 name: Self::POLICY_NAME.into(),
117 }
118 }
119
120 pub const REQUIRES_SCOPES_NAME: Name = name!("requiresScopes");
121 pub fn requires_scopes_identity() -> Identity {
122 Identity {
123 domain: APOLLO_SPEC_DOMAIN.to_string(),
124 name: Self::REQUIRES_SCOPES_NAME.into(),
125 }
126 }
127
128 pub const SOURCE_NAME: Name = name!("source");
129 pub fn source_identity() -> Identity {
130 Identity {
131 domain: APOLLO_SPEC_DOMAIN.to_string(),
132 name: Self::SOURCE_NAME.into(),
133 }
134 }
135
136 pub const TAG_NAME: Name = name!("tag");
137 pub fn tag_identity() -> Identity {
138 Identity {
139 domain: APOLLO_SPEC_DOMAIN.to_string(),
140 name: Self::TAG_NAME.into(),
141 }
142 }
143}
144
145pub(crate) static SPEC_REGISTRY: LazyLock<SpecRegistry> = LazyLock::new(|| {
146 let mut registry = SpecRegistry::new();
147 registry.extend(&AUTHENTICATED_VERSIONS);
148 registry.extend(&CACHE_TAG_VERSIONS);
149 registry.extend(&CONNECT_VERSIONS);
150 registry.extend(&CONTEXT_VERSIONS);
151 registry.extend(&COST_VERSIONS);
152 registry.extend(&FEDERATION_VERSIONS);
153 registry.extend(&INACCESSIBLE_VERSIONS);
154 registry.extend(&POLICY_VERSIONS);
155 registry.extend(&REQUIRES_SCOPES_VERSIONS);
156 registry.extend(&TAG_VERSIONS);
157 registry.extend(&JOIN_VERSIONS);
158 registry
159});
160
161pub(crate) struct SpecRegistry {
162 definitions_by_url: HashMap<Url, &'static (dyn SpecDefinition + Sync)>,
163 available_versions_by_identity: HashMap<Identity, BTreeSet<Version>>,
164}
165
166impl SpecRegistry {
167 fn new() -> Self {
168 Self {
169 definitions_by_url: HashMap::new(),
170 available_versions_by_identity: HashMap::new(),
171 }
172 }
173
174 fn extend<T: SpecDefinition + Sync>(&mut self, definitions: &'static SpecDefinitions<T>) {
175 for (v, spec) in definitions.iter() {
176 self.definitions_by_url.insert(spec.url().clone(), spec);
177 self.available_versions_by_identity
178 .entry(spec.url().identity.clone())
179 .or_default()
180 .insert(v.clone());
181 }
182 }
183
184 pub(crate) fn get_definition(&self, url: &Url) -> Option<&&(dyn SpecDefinition + Sync)> {
185 self.definitions_by_url.get(url)
186 }
187
188 pub(crate) fn get_versions(&self, identity: &Identity) -> Option<&BTreeSet<Version>> {
189 self.available_versions_by_identity.get(identity)
190 }
191
192 pub(crate) fn get_composition_spec(
198 &self,
199 source: &Link,
200 directive_name_in_spec: &Name,
201 ) -> Option<DirectiveCompositionSpecification> {
202 let specs = self.get_definition(&source.url)?.directive_specs();
203 let spec = specs.iter().find(|s| s.name() == directive_name_in_spec)?;
204 let directive_spec: DirectiveSpecification = spec.as_any().downcast_ref().cloned()?;
205 directive_spec.composition
206 }
207}