Repository: apollographql/apollo-server
Pull request #7430 (2023-03-08 18:36:10)

NEBULA-2161: send package version to embeds
This pr should be rebased off of https://github.com/apollographql/apollo-server/pull/7432

## Context

[JIRA](https://apollographql.atlassian.net/browse/NEBULA-2161?atlOrigin=eyJpIjoiNjRiNTc1ZWIyNTE0NDU3ZWE4ZDZlN2FiOWNlMzI1OTUiLCJwIjoiamlyYS1zbGFjay1pbnQifQ)
[Slack](https://apollograph.slack.com/archives/C025ENJA19Q/p1678288648578999)

## What Changed?

This PR does 2 things
1. passes the packageVersion as query params in the <script tag that requests the embedded Explorer / Sandbox CDN bundle.
2. sends the packageVersion to the embedded Explorer & Sandbox, which are [set up to send these as query params to Studio](https://github.com/apollographql/embeddable-explorer/pull/221). This is redundant data capture just in case.

## How to test

erm, run the landing pages locally, check the console & see that the version shows up correctly in the cdn script url

there are already tests for the html functions

netlify[bot]: ### <span aria-hidden="true">👷</span> Deploy Preview for *apollo-server-docs* processing.


|  Name | Link |
|:-:|------------------------|
|<span aria-hidden="true">🔨</span> Latest commit | 0343a505cac2632692a066c349c462350d3d0204 |
|<span aria-hidden="true">🔍</span> Latest deploy log | https://app.netlify.com/sites/apollo-server-docs/deploys/6408d5a57cf6580008b3b3ff |
codesandbox-ci[bot]: This pull request is automatically built and testable in [CodeSandbox](https://codesandbox.io).

  To see build info of the built libraries, click [here](https://ci.codesandbox.io/status/apollographql/apollo-server/pr/7430/builds/354644) or the icon next to each commit SHA.

Latest deployment of this branch, based on commit 0343a505cac2632692a066c349c462350d3d0204:

|Sandbox| Source |
|--|--|
|[Apollo Server Typescript](https://codesandbox.io/s/apollo-server-typescript-2mulpn)| Configuration |
|[Apollo Server](https://codesandbox.io/s/apollo-server-omsu72)| Configuration |

mayakoneval: @trevor-scheer I don't think this is changeset worthy, its not a user facing change at all / no behavior change at all?

Patch:
diff --git a/.changeset/polite-poets-teach.md b/.changeset/polite-poets-teach.md
new file mode 100644
index 000000000..1f66f76b7
--- /dev/null
+++ b/.changeset/polite-poets-teach.md
@@ -0,0 +1,5 @@
+---
+'@apollo/server': minor
+---
+
+We now send your @apollo/server version to the embedded Explorer & Sandbox used in the landing pages for analytics.
diff --git a/packages/server/src/plugin/landingPage/default/getEmbeddedHTML.ts b/packages/server/src/plugin/landingPage/default/getEmbeddedHTML.ts
index f13015d3b..85f9e79ae 100644
--- a/packages/server/src/plugin/landingPage/default/getEmbeddedHTML.ts
+++ b/packages/server/src/plugin/landingPage/default/getEmbeddedHTML.ts
@@ -21,8 +21,9 @@ function getConfigStringForHtml(config: LandingPageConfig) {
 }
 
 export const getEmbeddedExplorerHTML = (
-  version: string,
+  explorerCdnVersion: string,
   config: ApolloServerPluginEmbeddedLandingPageProductionDefaultOptions,
+  apolloServerVersion: string,
 ) => {
   interface EmbeddableExplorerOptions {
     graphRef: string;
@@ -49,22 +50,25 @@ export const getEmbeddedExplorerHTML = (
     persistExplorerState: false,
     ...(typeof config.embed === 'boolean' ? {} : config.embed),
   };
-  const embeddedExplorerParams: Omit<EmbeddableExplorerOptions, 'endpointUrl'> =
-    {
-      graphRef: config.graphRef,
-      target: '#embeddableExplorer',
-      initialState: {
-        document: config.document,
-        headers: config.headers,
-        variables: config.variables,
-        displayOptions: {
-          ...productionLandingPageConfigOrDefault.displayOptions,
-        },
+  const embeddedExplorerParams: Omit<
+    EmbeddableExplorerOptions,
+    'endpointUrl'
+  > & { runtime: string } = {
+    graphRef: config.graphRef,
+    target: '#embeddableExplorer',
+    initialState: {
+      document: config.document,
+      headers: config.headers,
+      variables: config.variables,
+      displayOptions: {
+        ...productionLandingPageConfigOrDefault.displayOptions,
       },
-      persistExplorerState:
-        productionLandingPageConfigOrDefault.persistExplorerState,
-      includeCookies: config.includeCookies,
-    };
+    },
+    persistExplorerState:
+      productionLandingPageConfigOrDefault.persistExplorerState,
+    includeCookies: config.includeCookies,
+    runtime: apolloServerVersion,
+  };
 
   return `
 <div class="fallback">
@@ -80,7 +84,11 @@ export const getEmbeddedExplorerHTML = (
 style="width: 100vw; height: 100vh; position: absolute; top: 0;"
 id="embeddableExplorer"
 ></div>
-<script src="https://embeddable-explorer.cdn.apollographql.com/${version}/embeddable-explorer.umd.production.min.js"></script>
+<script src="https://embeddable-explorer.cdn.apollographql.com/${encodeURIComponent(
+    explorerCdnVersion,
+  )}/embeddable-explorer.umd.production.min.js?runtime=${encodeURIComponent(
+    apolloServerVersion,
+  )}"></script>
 <script>
   var endpointUrl = window.location.href;
   var embeddedExplorerConfig = ${getConfigStringForHtml(
@@ -95,8 +103,9 @@ id="embeddableExplorer"
 };
 
 export const getEmbeddedSandboxHTML = (
-  version: string,
+  sandboxCdnVersion: string,
   config: LandingPageConfig,
+  apolloServerVersion: string,
 ) => {
   return `
 <div class="fallback">
@@ -112,7 +121,11 @@ export const getEmbeddedSandboxHTML = (
 style="width: 100vw; height: 100vh; position: absolute; top: 0;"
 id="embeddableSandbox"
 ></div>
-<script src="https://embeddable-sandbox.cdn.apollographql.com/${version}/embeddable-sandbox.umd.production.min.js"></script>
+<script src="https://embeddable-sandbox.cdn.apollographql.com/${encodeURIComponent(
+    sandboxCdnVersion,
+  )}/embeddable-sandbox.umd.production.min.js?runtime=${encodeURIComponent(
+    apolloServerVersion,
+  )}"></script>
 <script>
   var initialEndpoint = window.location.href;
   new window.EmbeddedSandbox({
@@ -125,6 +138,7 @@ id="embeddableSandbox"
       includeCookies: config.includeCookies,
     })},
     hideCookieToggle: false,
+    runtime: '${apolloServerVersion}'
   });
 </script>
 `;
diff --git a/packages/server/src/plugin/landingPage/default/index.ts b/packages/server/src/plugin/landingPage/default/index.ts
index 4371f91f9..3b5af1e10 100644
--- a/packages/server/src/plugin/landingPage/default/index.ts
+++ b/packages/server/src/plugin/landingPage/default/index.ts
@@ -12,6 +12,7 @@ import {
   getEmbeddedExplorerHTML,
   getEmbeddedSandboxHTML,
 } from './getEmbeddedHTML.js';
+import { packageVersion } from '../../../generated/packageVersion.js';
 
 export type {
   ApolloServerPluginLandingPageLocalDefaultOptions,
@@ -57,8 +58,9 @@ function encodeConfig(config: LandingPageConfig): string {
 }
 
 const getNonEmbeddedLandingPageHTML = (
-  version: string,
+  cdnVersion: string,
   config: LandingPageConfig,
+  apolloServerVersion: string,
 ) => {
   const encodedConfig = encodeConfig(config);
 
@@ -68,7 +70,7 @@ const getNonEmbeddedLandingPageHTML = (
   <p>The full landing page cannot be loaded; it appears that you might be offline.</p>
 </div>
 <script>window.landingPage = ${encodedConfig};</script>
-<script src="https://apollo-server-landing-page.cdn.apollographql.com/${version}/static/js/main.js"></script>`;
+<script src="https://apollo-server-landing-page.cdn.apollographql.com/${cdnVersion}/static/js/main.js?runtime=${apolloServerVersion}"></script>`;
 };
 
 // Helper for the two actual plugin functions.
@@ -80,6 +82,7 @@ function ApolloServerPluginLandingPageDefault<TContext extends BaseContext>(
   },
 ): ImplicitlyInstallablePlugin<TContext> {
   const version = maybeVersion ?? '_latest';
+  const apolloServerVersion = `@apollo/server@${packageVersion}`;
 
   return {
     __internal_installed_implicitly__: false,
@@ -132,9 +135,9 @@ function ApolloServerPluginLandingPageDefault<TContext extends BaseContext>(
     ${
       config.embed
         ? 'graphRef' in config && config.graphRef
-          ? getEmbeddedExplorerHTML(version, config)
-          : getEmbeddedSandboxHTML(version, config)
-        : getNonEmbeddedLandingPageHTML(version, config)
+          ? getEmbeddedExplorerHTML(version, config, apolloServerVersion)
+          : getEmbeddedSandboxHTML(version, config, apolloServerVersion)
+        : getNonEmbeddedLandingPageHTML(version, config, apolloServerVersion)
     }
     </div>
   </body>


Test patch:
diff --git a/packages/server/src/__tests__/plugin/landingPage/getEmbeddedExplorerHTML.test.ts b/packages/server/src/__tests__/plugin/landingPage/getEmbeddedExplorerHTML.test.ts
index 0e2ecba05..0682cd068 100644
--- a/packages/server/src/__tests__/plugin/landingPage/getEmbeddedExplorerHTML.test.ts
+++ b/packages/server/src/__tests__/plugin/landingPage/getEmbeddedExplorerHTML.test.ts
@@ -2,8 +2,9 @@ import { getEmbeddedExplorerHTML } from '../../../plugin/landingPage/default/get
 import type { ApolloServerPluginEmbeddedLandingPageProductionDefaultOptions } from '../../../plugin/landingPage/default/types';
 import { describe, it, expect } from '@jest/globals';
 
-const version = '_latest';
+const cdnVersion = '_latest';
 expect.addSnapshotSerializer(require('jest-serializer-html'));
+const apolloServerVersion = '@apollo/server@4.0.0';
 
 describe('Embedded Explorer Landing Page Config HTML', () => {
   it('with document, variables, headers and displayOptions provided', () => {
@@ -29,7 +30,8 @@ describe('Embedded Explorer Landing Page Config HTML', () => {
         },
         graphRef: 'graph@current',
       };
-    expect(getEmbeddedExplorerHTML(version, config)).toMatchInlineSnapshot(`
+    expect(getEmbeddedExplorerHTML(cdnVersion, config, apolloServerVersion))
+      .toMatchInlineSnapshot(`
       <div class="fallback">
         <h1>
           Welcome to Apollo Server
@@ -47,11 +49,11 @@ describe('Embedded Explorer Landing Page Config HTML', () => {
            id="embeddableExplorer"
       >
       </div>
-      <script src="https://embeddable-explorer.cdn.apollographql.com/_latest/embeddable-explorer.umd.production.min.js">
+      <script src="https://embeddable-explorer.cdn.apollographql.com/_latest/embeddable-explorer.umd.production.min.js?runtime=%40apollo%2Fserver%404.0.0">
       </script>
       <script>
         var endpointUrl = window.location.href;
-        var embeddedExplorerConfig = {"graphRef":"graph@current","target":"#embeddableExplorer","initialState":{"document":"query Test { id }","headers":{"authorization":"true"},"variables":{"option":{"a":"val","b":1,"c":true}},"displayOptions":{"showHeadersAndEnvVars":true,"docsPanelState":"open","theme":"light"}},"persistExplorerState":true,"includeCookies":true};
+        var embeddedExplorerConfig = {"graphRef":"graph@current","target":"#embeddableExplorer","initialState":{"document":"query Test { id }","headers":{"authorization":"true"},"variables":{"option":{"a":"val","b":1,"c":true}},"displayOptions":{"showHeadersAndEnvVars":true,"docsPanelState":"open","theme":"light"}},"persistExplorerState":true,"includeCookies":true,"runtime":"@apollo/server@4.0.0"};
         new window.EmbeddedExplorer({
           ...embeddedExplorerConfig,
           endpointUrl,
@@ -67,7 +69,8 @@ describe('Embedded Explorer Landing Page Config HTML', () => {
         embed: true as true,
         graphRef: 'graph@current',
       };
-    expect(getEmbeddedExplorerHTML(version, config)).toMatchInlineSnapshot(`
+    expect(getEmbeddedExplorerHTML(cdnVersion, config, apolloServerVersion))
+      .toMatchInlineSnapshot(`
       <div class="fallback">
         <h1>
           Welcome to Apollo Server
@@ -85,11 +88,11 @@ describe('Embedded Explorer Landing Page Config HTML', () => {
            id="embeddableExplorer"
       >
       </div>
-      <script src="https://embeddable-explorer.cdn.apollographql.com/_latest/embeddable-explorer.umd.production.min.js">
+      <script src="https://embeddable-explorer.cdn.apollographql.com/_latest/embeddable-explorer.umd.production.min.js?runtime=%40apollo%2Fserver%404.0.0">
       </script>
       <script>
         var endpointUrl = window.location.href;
-        var embeddedExplorerConfig = {"graphRef":"graph@current","target":"#embeddableExplorer","initialState":{"displayOptions":{}},"persistExplorerState":false,"includeCookies":false};
+        var embeddedExplorerConfig = {"graphRef":"graph@current","target":"#embeddableExplorer","initialState":{"displayOptions":{}},"persistExplorerState":false,"includeCookies":false,"runtime":"@apollo/server@4.0.0"};
         new window.EmbeddedExplorer({
           ...embeddedExplorerConfig,
           endpointUrl,
diff --git a/packages/server/src/__tests__/plugin/landingPage/getEmbeddedSandboxHTML.test.ts b/packages/server/src/__tests__/plugin/landingPage/getEmbeddedSandboxHTML.test.ts
index 17854abc9..8ad9f1021 100644
--- a/packages/server/src/__tests__/plugin/landingPage/getEmbeddedSandboxHTML.test.ts
+++ b/packages/server/src/__tests__/plugin/landingPage/getEmbeddedSandboxHTML.test.ts
@@ -2,8 +2,9 @@ import { getEmbeddedSandboxHTML } from '../../../plugin/landingPage/default/getE
 import type { LandingPageConfig } from '../../../plugin/landingPage/default/types';
 import { describe, it, expect } from '@jest/globals';
 
-const version = '_latest';
+const cdnVersion = '_latest';
 expect.addSnapshotSerializer(require('jest-serializer-html'));
+const apolloServerVersion = '@apollo/server@4.0.0';
 
 describe('Landing Page Config HTML', () => {
   it('for embedded sandbox with document, variables and headers provided', () => {
@@ -20,7 +21,8 @@ describe('Landing Page Config HTML', () => {
       headers: { authorization: 'true' },
       embed: true,
     };
-    expect(getEmbeddedSandboxHTML(version, config)).toMatchInlineSnapshot(`
+    expect(getEmbeddedSandboxHTML(cdnVersion, config, apolloServerVersion))
+      .toMatchInlineSnapshot(`
       <div class="fallback">
         <h1>
           Welcome to Apollo Server
@@ -38,7 +40,7 @@ describe('Landing Page Config HTML', () => {
            id="embeddableSandbox"
       >
       </div>
-      <script src="https://embeddable-sandbox.cdn.apollographql.com/_latest/embeddable-sandbox.umd.production.min.js">
+      <script src="https://embeddable-sandbox.cdn.apollographql.com/_latest/embeddable-sandbox.umd.production.min.js?runtime=%40apollo%2Fserver%404.0.0">
       </script>
       <script>
         var initialEndpoint = window.location.href;
@@ -47,6 +49,7 @@ describe('Landing Page Config HTML', () => {
           initialEndpoint,
           initialState: {"document":"query Test { id }","variables":{"option":{"a":"val","b":1,"c":true}},"headers":{"authorization":"true"},"includeCookies":true},
           hideCookieToggle: false,
+          runtime: '@apollo/server@4.0.0'
         });
       </script>
     `);
@@ -57,7 +60,8 @@ describe('Landing Page Config HTML', () => {
       includeCookies: false,
       embed: true,
     };
-    expect(getEmbeddedSandboxHTML(version, config)).toMatchInlineSnapshot(`
+    expect(getEmbeddedSandboxHTML(cdnVersion, config, apolloServerVersion))
+      .toMatchInlineSnapshot(`
       <div class="fallback">
         <h1>
           Welcome to Apollo Server
@@ -75,7 +79,7 @@ describe('Landing Page Config HTML', () => {
            id="embeddableSandbox"
       >
       </div>
-      <script src="https://embeddable-sandbox.cdn.apollographql.com/_latest/embeddable-sandbox.umd.production.min.js">
+      <script src="https://embeddable-sandbox.cdn.apollographql.com/_latest/embeddable-sandbox.umd.production.min.js?runtime=%40apollo%2Fserver%404.0.0">
       </script>
       <script>
         var initialEndpoint = window.location.href;
@@ -84,6 +88,7 @@ describe('Landing Page Config HTML', () => {
           initialEndpoint,
           initialState: {"includeCookies":false},
           hideCookieToggle: false,
+          runtime: '@apollo/server@4.0.0'
         });
       </script>
     `);