diff --git a/.changesets/feat_glasser_persisted_queries_ga.md b/.changesets/feat_glasser_persisted_queries_ga.md new file mode 100644 index 0000000000..fff9f3bd05 --- /dev/null +++ b/.changesets/feat_glasser_persisted_queries_ga.md @@ -0,0 +1,15 @@ +### Move Persisted Queries to General Availability ([PR #3914](https://github.com/apollographql/router/pull/3914)) + +[Persisted Queries](https://www.apollographql.com/docs/graphos/operations/persisted-queries/) (a GraphOS Enterprise feature) is now moving to General Availability, from Preview where it has been since Apollo Router 1.25. + +For more information about launch stages, please see the documentation here: https://www.apollographql.com/docs/resources/product-launch-stages/ + +The feature is now configured with a `persisted_queries` top-level key in the YAML configuration instead of with `preview_persisted_queries`. Existing configuration files will keep working as before, only with a warning. To fix that warning, rename the configuration section like so: + +```diff +-preview_persisted_queries: ++persisted_queries: + enabled: true +``` + +By [@glasser](https://github.com/glasser) in https://github.com/apollographql/router/pull/3914 \ No newline at end of file diff --git a/apollo-router/src/configuration/metrics.rs b/apollo-router/src/configuration/metrics.rs index 9506b1e1f2..a681b7dda3 100644 --- a/apollo-router/src/configuration/metrics.rs +++ b/apollo-router/src/configuration/metrics.rs @@ -206,7 +206,7 @@ impl Metrics { ); log_usage_metrics!( value.apollo.router.config.persisted_queries, - "$.preview_persisted_queries[?(@.enabled == true)]", + "$.persisted_queries[?(@.enabled == true)]", opt.log_unknown, "$[?(@.log_unknown == true)]", opt.safelist.require_id, diff --git a/apollo-router/src/configuration/migrations/0012-persisted-queries-ga.yaml b/apollo-router/src/configuration/migrations/0012-persisted-queries-ga.yaml new file mode 100644 index 0000000000..1d9ba4c685 --- /dev/null +++ b/apollo-router/src/configuration/migrations/0012-persisted-queries-ga.yaml @@ -0,0 +1,5 @@ +description: Persisted queries are no longer preview, `preview_persisted_queries` is renamed `persisted_queries` +actions: + - type: move + from: preview_persisted_queries + to: persisted_queries diff --git a/apollo-router/src/configuration/migrations/README.md b/apollo-router/src/configuration/migrations/README.md index 1d494cee80..3913561c21 100644 --- a/apollo-router/src/configuration/migrations/README.md +++ b/apollo-router/src/configuration/migrations/README.md @@ -5,8 +5,8 @@ It uses [proteus](https://github.com/rust-playground/proteus) under the hood, wh A migration has the following format: -The filename should begin with a 5 digit numerical prefix. This allows us to apply migrations in a deterministic order. -`Filename: 00001-name.yaml` +The filename should begin with a 4 digit numerical prefix. This allows us to apply migrations in a deterministic order. +`Filename: 0001-name.yaml` The yaml consists of a description and a number of actions: ```yaml diff --git a/apollo-router/src/configuration/mod.rs b/apollo-router/src/configuration/mod.rs index 02c1694051..2a3801ec8f 100644 --- a/apollo-router/src/configuration/mod.rs +++ b/apollo-router/src/configuration/mod.rs @@ -151,11 +151,9 @@ pub struct Configuration { #[serde(default)] pub(crate) apq: Apq, - // NOTE: when renaming this to move out of preview, also update paths - // in `uplink/license.rs`. /// Configures managed persisted queries #[serde(default)] - pub preview_persisted_queries: PersistedQueries, + pub persisted_queries: PersistedQueries, /// Configuration for operation limits, parser limits, HTTP limits, etc. #[serde(default)] @@ -228,7 +226,7 @@ impl<'de> serde::Deserialize<'de> for Configuration { apollo_plugins: ApolloPlugins, tls: Tls, apq: Apq, - preview_persisted_queries: PersistedQueries, + persisted_queries: PersistedQueries, #[serde(skip)] uplink: UplinkConfig, limits: Limits, @@ -247,7 +245,7 @@ impl<'de> serde::Deserialize<'de> for Configuration { .apollo_plugins(ad_hoc.apollo_plugins.plugins) .tls(ad_hoc.tls) .apq(ad_hoc.apq) - .persisted_query(ad_hoc.preview_persisted_queries) + .persisted_query(ad_hoc.persisted_queries) .operation_limits(ad_hoc.limits) .chaos(ad_hoc.experimental_chaos) .uplink(ad_hoc.uplink) @@ -310,7 +308,7 @@ impl Configuration { homepage: homepage.unwrap_or_default(), cors: cors.unwrap_or_default(), apq: apq.unwrap_or_default(), - preview_persisted_queries: persisted_query.unwrap_or_default(), + persisted_queries: persisted_query.unwrap_or_default(), limits: operation_limits.unwrap_or_default(), experimental_chaos: chaos.unwrap_or_default(), experimental_graphql_validation_mode: graphql_validation_mode.unwrap_or_default(), @@ -380,7 +378,7 @@ impl Configuration { tls: tls.unwrap_or_default(), notify: notify.unwrap_or_default(), apq: apq.unwrap_or_default(), - preview_persisted_queries: persisted_query.unwrap_or_default(), + persisted_queries: persisted_query.unwrap_or_default(), uplink, }; @@ -439,31 +437,31 @@ impl Configuration { } // PQs. - if self.preview_persisted_queries.enabled { - if self.preview_persisted_queries.safelist.enabled && self.apq.enabled { + if self.persisted_queries.enabled { + if self.persisted_queries.safelist.enabled && self.apq.enabled { return Err(ConfigurationError::InvalidConfiguration { message: "apqs must be disabled to enable safelisting", - error: "either set preview_persisted_queries.safelist.enabled: false or apq.enabled: false in your router yaml configuration".into() + error: "either set persisted_queries.safelist.enabled: false or apq.enabled: false in your router yaml configuration".into() }); - } else if !self.preview_persisted_queries.safelist.enabled - && self.preview_persisted_queries.safelist.require_id + } else if !self.persisted_queries.safelist.enabled + && self.persisted_queries.safelist.require_id { return Err(ConfigurationError::InvalidConfiguration { message: "safelist must be enabled to require IDs", - error: "either set preview_persisted_queries.safelist.enabled: true or preview_persisted_queries.safelist.require_id: false in your router yaml configuration".into() + error: "either set persisted_queries.safelist.enabled: true or persisted_queries.safelist.require_id: false in your router yaml configuration".into() }); } } else { // If the feature isn't enabled, sub-features shouldn't be. - if self.preview_persisted_queries.safelist.enabled { + if self.persisted_queries.safelist.enabled { return Err(ConfigurationError::InvalidConfiguration { message: "persisted queries must be enabled to enable safelisting", - error: "either set preview_persisted_queries.safelist.enabled: false or preview_persisted_queries.enabled: true in your router yaml configuration".into() + error: "either set persisted_queries.safelist.enabled: false or persisted_queries.enabled: true in your router yaml configuration".into() }); - } else if self.preview_persisted_queries.log_unknown { + } else if self.persisted_queries.log_unknown { return Err(ConfigurationError::InvalidConfiguration { message: "persisted queries must be enabled to enable logging unknown operations", - error: "either set preview_persisted_queries.log_unknown: false or preview_persisted_queries.enabled: true in your router yaml configuration".into() + error: "either set persisted_queries.log_unknown: false or persisted_queries.enabled: true in your router yaml configuration".into() }); } } diff --git a/apollo-router/src/configuration/snapshots/apollo_router__configuration__tests__schema_generation.snap b/apollo-router/src/configuration/snapshots/apollo_router__configuration__tests__schema_generation.snap index 20fba9012f..90130d9670 100644 --- a/apollo-router/src/configuration/snapshots/apollo_router__configuration__tests__schema_generation.snap +++ b/apollo-router/src/configuration/snapshots/apollo_router__configuration__tests__schema_generation.snap @@ -1603,6 +1603,52 @@ expression: "&schema" } ] }, + "persisted_queries": { + "description": "Configures managed persisted queries", + "default": { + "enabled": false, + "log_unknown": false, + "safelist": { + "enabled": false, + "require_id": false + } + }, + "type": "object", + "properties": { + "enabled": { + "description": "Activates Persisted Queries (disabled by default)", + "default": false, + "type": "boolean" + }, + "log_unknown": { + "description": "Enabling this field configures the router to log any freeform GraphQL request that is not in the persisted query list", + "default": false, + "type": "boolean" + }, + "safelist": { + "description": "Restricts execution of operations that are not found in the Persisted Query List", + "default": { + "enabled": false, + "require_id": false + }, + "type": "object", + "properties": { + "enabled": { + "description": "Enables using the persisted query list as a safelist (disabled by default)", + "default": false, + "type": "boolean" + }, + "require_id": { + "description": "Enabling this field configures the router to reject any request that does not include the persisted query ID", + "default": false, + "type": "boolean" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, "plugins": { "description": "Plugin configuration", "default": null, @@ -1666,52 +1712,6 @@ expression: "&schema" }, "additionalProperties": false }, - "preview_persisted_queries": { - "description": "Configures managed persisted queries", - "default": { - "enabled": false, - "log_unknown": false, - "safelist": { - "enabled": false, - "require_id": false - } - }, - "type": "object", - "properties": { - "enabled": { - "description": "Activates Persisted Queries (disabled by default)", - "default": false, - "type": "boolean" - }, - "log_unknown": { - "description": "Enabling this field configures the router to log any freeform GraphQL request that is not in the persisted query list", - "default": false, - "type": "boolean" - }, - "safelist": { - "description": "Restricts execution of operations that are not found in the Persisted Query List", - "default": { - "enabled": false, - "require_id": false - }, - "type": "object", - "properties": { - "enabled": { - "description": "Enables using the persisted query list as a safelist (disabled by default)", - "default": false, - "type": "boolean" - }, - "require_id": { - "description": "Enabling this field configures the router to reject any request that does not include the persisted query ID", - "default": false, - "type": "boolean" - } - }, - "additionalProperties": false - } - }, - "additionalProperties": false - }, "rhai": { "description": "Configuration for the Rhai Plugin", "type": "object", diff --git a/apollo-router/src/configuration/snapshots/apollo_router__configuration__tests__upgrade_old_configuration@persisted-queries-preview.yaml.snap b/apollo-router/src/configuration/snapshots/apollo_router__configuration__tests__upgrade_old_configuration@persisted-queries-preview.yaml.snap new file mode 100644 index 0000000000..68444d096e --- /dev/null +++ b/apollo-router/src/configuration/snapshots/apollo_router__configuration__tests__upgrade_old_configuration@persisted-queries-preview.yaml.snap @@ -0,0 +1,8 @@ +--- +source: apollo-router/src/configuration/tests.rs +expression: new_config +--- +--- +persisted_queries: + enabled: true + diff --git a/apollo-router/src/configuration/testdata/metrics/persisted_queries.router.yaml b/apollo-router/src/configuration/testdata/metrics/persisted_queries.router.yaml index be7aa92231..ae47ed37fe 100644 --- a/apollo-router/src/configuration/testdata/metrics/persisted_queries.router.yaml +++ b/apollo-router/src/configuration/testdata/metrics/persisted_queries.router.yaml @@ -1,6 +1,6 @@ apq: enabled: false -preview_persisted_queries: +persisted_queries: enabled: true log_unknown: true safelist: diff --git a/apollo-router/src/configuration/testdata/migrations/persisted-queries-preview.yaml b/apollo-router/src/configuration/testdata/migrations/persisted-queries-preview.yaml new file mode 100644 index 0000000000..0c9648d083 --- /dev/null +++ b/apollo-router/src/configuration/testdata/migrations/persisted-queries-preview.yaml @@ -0,0 +1,3 @@ +preview_persisted_queries: + enabled: true + diff --git a/apollo-router/src/services/layers/persisted_queries/manifest_poller.rs b/apollo-router/src/services/layers/persisted_queries/manifest_poller.rs index 308cb5a440..409277ea63 100644 --- a/apollo-router/src/services/layers/persisted_queries/manifest_poller.rs +++ b/apollo-router/src/services/layers/persisted_queries/manifest_poller.rs @@ -398,19 +398,18 @@ async fn poll_uplink( while let Some(event) = uplink_executor.next().await { match event { ManifestPollEvent::NewManifest(new_manifest) => { - let freeform_graphql_behavior = if config.preview_persisted_queries.safelist.enabled - { - if config.preview_persisted_queries.safelist.require_id { + let freeform_graphql_behavior = if config.persisted_queries.safelist.enabled { + if config.persisted_queries.safelist.require_id { FreeformGraphQLBehavior::DenyAll { - log_unknown: config.preview_persisted_queries.log_unknown, + log_unknown: config.persisted_queries.log_unknown, } } else { FreeformGraphQLBehavior::AllowIfInSafelist { safelist: FreeformGraphQLSafelist::new(&new_manifest), - log_unknown: config.preview_persisted_queries.log_unknown, + log_unknown: config.persisted_queries.log_unknown, } } - } else if config.preview_persisted_queries.log_unknown { + } else if config.persisted_queries.log_unknown { FreeformGraphQLBehavior::LogUnlessInSafelist { safelist: FreeformGraphQLSafelist::new(&new_manifest), apq_enabled: config.apq.enabled, diff --git a/apollo-router/src/services/layers/persisted_queries/mod.rs b/apollo-router/src/services/layers/persisted_queries/mod.rs index 025fc34613..27c92ba13f 100644 --- a/apollo-router/src/services/layers/persisted_queries/mod.rs +++ b/apollo-router/src/services/layers/persisted_queries/mod.rs @@ -37,7 +37,7 @@ impl PersistedQueryLayer { /// Create a new [`PersistedQueryLayer`] from CLI options, YAML configuration, /// and optionally, an existing persisted query manifest poller. pub(crate) async fn new(configuration: &Configuration) -> Result { - if configuration.preview_persisted_queries.enabled { + if configuration.persisted_queries.enabled { Ok(Self { manifest_poller: Some( PersistedQueryManifestPoller::new(configuration.clone()).await?, diff --git a/apollo-router/src/uplink/license_enforcement.rs b/apollo-router/src/uplink/license_enforcement.rs index ab2ef2093f..6c8c3cfe5a 100644 --- a/apollo-router/src/uplink/license_enforcement.rs +++ b/apollo-router/src/uplink/license_enforcement.rs @@ -178,7 +178,7 @@ impl LicenseEnforcementReport { .name("Operation aliases limiting") .build(), ConfigurationRestriction::builder() - .path("$.preview_persisted_queries") + .path("$.persisted_queries") .name("Persisted queries") .build(), ] diff --git a/apollo-router/tests/integration_tests.rs b/apollo-router/tests/integration_tests.rs index 4ba47753af..68fc4123f3 100644 --- a/apollo-router/tests/integration_tests.rs +++ b/apollo-router/tests/integration_tests.rs @@ -533,7 +533,7 @@ async fn persisted_queries() { .await; let config = serde_json::json!({ - "preview_persisted_queries": { + "persisted_queries": { "enabled": true }, "apq": { @@ -1482,7 +1482,7 @@ async fn all_stock_router_example_yamls_are_valid() { let mut configuration: Configuration = serde_yaml::from_str(&raw_yaml) .unwrap_or_else(|e| panic!("unable to parse YAML {display_path}: {e}")); let (_mock_guard, configuration) = - if configuration.preview_persisted_queries.enabled { + if configuration.persisted_queries.enabled { let (_mock_guard, uplink_config) = mock_empty_pq_uplink().await; configuration.uplink = Some(uplink_config); (Some(_mock_guard), configuration) diff --git a/docs/source/config.json b/docs/source/config.json index 73d6f03a0c..192cca44eb 100644 --- a/docs/source/config.json +++ b/docs/source/config.json @@ -59,8 +59,7 @@ "Safelisting with persisted queries": [ "/configuration/persisted-queries", [ - "enterprise", - "preview" + "enterprise" ] ], "Privacy and data collection": "/privacy" diff --git a/docs/source/configuration/persisted-queries.mdx b/docs/source/configuration/persisted-queries.mdx index 306b970b2d..a5e4e4954d 100644 --- a/docs/source/configuration/persisted-queries.mdx +++ b/docs/source/configuration/persisted-queries.mdx @@ -6,8 +6,6 @@ minVersion: 1.25.0 - - ## Differences from automatic persisted queries @@ -40,21 +38,21 @@ For more information on other configuration aspects, see the [GraphOS persisted The router provides four configuration options that you can combine to create the recommended [security levels](#router-security-levels). This section details each configuration option. Refer to the [security levels](#router-security-levels) section for recommended combinations. -#### `preview_persisted_queries` +#### `persisted_queries` This base configuration enables the feature. All other configuration options build off this one. ```yaml title="router.yaml" -preview_persisted_queries: +persisted_queries: enabled: true ``` #### `log_unknown` -Adding `log_unknown: true` to `preview_persisted_queries` configures the router to log any incoming operations not preregistered to the PQL. +Adding `log_unknown: true` to `persisted_queries` configures the router to log any incoming operations not preregistered to the PQL. ```yaml title="router.yaml" -preview_persisted_queries: +persisted_queries: enabled: true log_unknown: true ``` @@ -63,10 +61,10 @@ If used with the [`safelist`](#safelist) option, the router logs unregistered an #### `safelist` -Adding `safelist: true` to `preview_persisted_queries` causes the router to reject any operations that haven't been preregistered to your PQL. +Adding `safelist: true` to `persisted_queries` causes the router to reject any operations that haven't been preregistered to your PQL. ```yaml title="router.yaml" -preview_persisted_queries: +persisted_queries: enabled: true safelist: enabled: true @@ -85,7 +83,7 @@ Adding `require_id: true` to the `safelist` option causes the router to reject a - use a full operation string rather than the operation ID ```yaml title="router.yaml" -preview_persisted_queries: +persisted_queries: enabled: true safelist: enabled: true diff --git a/examples/persisted-queries/additive_pq.yaml b/examples/persisted-queries/additive_pq.yaml index 6c76844297..732b79f677 100644 --- a/examples/persisted-queries/additive_pq.yaml +++ b/examples/persisted-queries/additive_pq.yaml @@ -7,5 +7,5 @@ # 2) make requests against the router: # curl --get http://localhost:4000 --header 'content-type: application/json' --data-urlencode 'extensions={"persistedQuery":{"sha256Hash":"hash-of-operation", "version": 1}}' -preview_persisted_queries: +persisted_queries: enabled: true diff --git a/examples/persisted-queries/pq_log_unknown.yaml b/examples/persisted-queries/pq_log_unknown.yaml index 9513cb278e..a0f53331ae 100644 --- a/examples/persisted-queries/pq_log_unknown.yaml +++ b/examples/persisted-queries/pq_log_unknown.yaml @@ -7,7 +7,7 @@ # 2) make requests against the router: # curl --get http://localhost:4000 --header 'content-type: application/json' --data-urlencode 'extensions={"persistedQuery":{"sha256Hash":"hash-of-operation", "version": 1}}' -preview_persisted_queries: +persisted_queries: enabled: true log_unknown: true diff --git a/examples/persisted-queries/safelist_pq.yaml b/examples/persisted-queries/safelist_pq.yaml index 3a900a06f7..9e4f6eda49 100644 --- a/examples/persisted-queries/safelist_pq.yaml +++ b/examples/persisted-queries/safelist_pq.yaml @@ -8,7 +8,7 @@ # 2) make requests against the router: # curl --get http://localhost:4000 --header 'content-type: application/json' --data-urlencode 'extensions={"persistedQuery":{"sha256Hash":"hash-of-operation", "version": 1}}' -preview_persisted_queries: +persisted_queries: enabled: true safelist: enabled: true diff --git a/examples/persisted-queries/safelist_pq_require_id.yaml b/examples/persisted-queries/safelist_pq_require_id.yaml index 8cd6855c8a..7994f8a9b8 100644 --- a/examples/persisted-queries/safelist_pq_require_id.yaml +++ b/examples/persisted-queries/safelist_pq_require_id.yaml @@ -8,7 +8,7 @@ # 2) make requests against the router: # curl --get http://localhost:4000 --header 'content-type: application/json' --data-urlencode 'extensions={"persistedQuery":{"sha256Hash":"hash-of-operation", "version": 1}}' -preview_persisted_queries: +persisted_queries: enabled: true safelist: enabled: true