diff --git a/src/language-server/engine/index.ts b/src/language-server/engine/index.ts index 25f538f5..b4ffb197 100644 --- a/src/language-server/engine/index.ts +++ b/src/language-server/engine/index.ts @@ -1,7 +1,11 @@ import { GraphQLDataSource } from "./GraphQLDataSource"; import { DefaultEngineConfig } from "../config"; import { SCHEMA_TAGS_AND_FIELD_STATS } from "./operations/schemaTagsAndFieldStats"; -import { SchemaTagsAndFieldStatsQuery } from "../graphqlTypes"; +import { + FrontendUrlRootQuery, + SchemaTagsAndFieldStatsQuery, +} from "../graphqlTypes"; +import { FRONTEND_URL_ROOT } from "./operations/frontendUrlRoot"; export interface ClientIdentity { name?: string; @@ -93,4 +97,12 @@ export class ApolloEngineClient extends GraphQLDataSource { return { schemaTags, fieldLatencies }; } + + async loadFrontendUrlRoot() { + const { data } = await this.execute({ + query: FRONTEND_URL_ROOT, + }); + + return data?.frontendUrlRoot; + } } diff --git a/src/language-server/engine/operations/frontendUrlRoot.ts b/src/language-server/engine/operations/frontendUrlRoot.ts new file mode 100644 index 00000000..fc6835ab --- /dev/null +++ b/src/language-server/engine/operations/frontendUrlRoot.ts @@ -0,0 +1,7 @@ +import gql from "graphql-tag"; + +export const FRONTEND_URL_ROOT = gql` + query FrontendUrlRoot { + frontendUrlRoot + } +`; diff --git a/src/language-server/graphqlTypes.ts b/src/language-server/graphqlTypes.ts index 359503b4..75f7afa9 100644 --- a/src/language-server/graphqlTypes.ts +++ b/src/language-server/graphqlTypes.ts @@ -14,6 +14,8 @@ export type Scalars = { /** Long type */ Long: any; GraphQLDocument: any; + /** A lowercase hexadecimal SHA-256 */ + SHA256: any; /** Arbitrary JSON object */ Object: any; /** A blob (base64'ed in JSON & GraphQL) */ @@ -21,8 +23,6 @@ export type Scalars = { StringOrInt: any; /** Always null */ Void: any; - /** A lowercase hexadecimal SHA-256 */ - SHA256: any; }; export type Query = { @@ -349,6 +349,8 @@ export type Service = Identity & { deletedAt: Maybe; description: Maybe; devGraphOwner: Maybe; + /** Get a graphql by hash */ + document: Maybe; firstReportedAt: Maybe; /** * When this is true, this graph will be hidden from non-admin members of the org who haven't been explicitly assigned a @@ -458,6 +460,11 @@ export type ServiceCompositionResultByIdArgs = { }; +export type ServiceDocumentArgs = { + hash: Maybe; +}; + + export type ServiceImplementingServicesArgs = { graphVariant: Scalars['String']; includeDeleted: Maybe; @@ -715,6 +722,8 @@ export type GraphVariant = { __typename?: 'GraphVariant'; /** As new schema tags keep getting published, activeSchemaPublish refers to the latest. */ activeSchemaPublish: Maybe; + /** Filter configuration used to create the contract schema */ + contractFilterConfig: Maybe; /** Explorer setting for default headers for a graph */ defaultHeaders: Maybe; derivedVariantCount: Scalars['Int']; @@ -1472,6 +1481,12 @@ export type TypeFilterConfig = { includeIntrospectionTypes: Maybe; }; +export type FilterConfig = { + __typename?: 'FilterConfig'; + exclude: Array; + include: Array; +}; + export type Launch = { __typename?: 'Launch'; approvedAt: Maybe; @@ -1523,12 +1538,6 @@ export type FilterBuildInput = { schemaHash: Scalars['String']; }; -export type FilterConfig = { - __typename?: 'FilterConfig'; - exclude: Array; - include: Array; -}; - export type BuildResult = BuildFailure | BuildSuccess; export type BuildFailure = { @@ -7494,6 +7503,11 @@ export type Uri = { gcs: Scalars['String']; }; +export type FrontendUrlRootQueryVariables = Exact<{ [key: string]: never; }>; + + +export type FrontendUrlRootQuery = { __typename?: 'Query', frontendUrlRoot: string }; + export type SchemaTagsAndFieldStatsQueryVariables = Exact<{ id: Scalars['ID']; }>; diff --git a/src/language-server/project/client.ts b/src/language-server/project/client.ts index 1aa663c3..a18551c3 100644 --- a/src/language-server/project/client.ts +++ b/src/language-server/project/client.ts @@ -54,6 +54,7 @@ import { } from "../diagnostics"; import URI from "vscode-uri"; import type { EngineDecoration } from "src/messages"; +import { join } from "path"; type Maybe = null | undefined | T; @@ -98,6 +99,7 @@ export class GraphQLClientProject extends GraphQLProject { private _onSchemaTags?: NotificationHandler<[ServiceID, SchemaTag[]]>; private fieldLatencies?: FieldLatencies; + private frontendUrlRoot?: string; private _validationRules?: ValidationRule[]; @@ -332,16 +334,19 @@ export class GraphQLClientProject extends GraphQLProject { if (!engineClient) return; const serviceID = this.serviceID; - if (!serviceID) return; await this.loadingHandler.handle( `Loading Apollo data for ${this.displayName}`, (async () => { try { - const { schemaTags, fieldLatencies } = - await engineClient.loadSchemaTagsAndFieldLatencies(serviceID); - this._onSchemaTags && this._onSchemaTags([serviceID, schemaTags]); - this.fieldLatencies = fieldLatencies; + if (serviceID) { + const { schemaTags, fieldLatencies } = + await engineClient.loadSchemaTagsAndFieldLatencies(serviceID); + this._onSchemaTags && this._onSchemaTags([serviceID, schemaTags]); + this.fieldLatencies = fieldLatencies; + } + const frontendUrlRoot = await engineClient.loadFrontendUrlRoot(); + this.frontendUrlRoot = frontendUrlRoot; this.lastLoadDate = +new Date(); this.generateDecorations(); @@ -396,18 +401,22 @@ export class GraphQLClientProject extends GraphQLProject { ); const frontendUrlRoot = - "https://studio-staging.apollographql.com"; // TODO + this.frontendUrlRoot ?? "https://studio.apollographql.com"; const endpoint = this.config.service?.endpoint; const variant = this.config.variant; const graphId = this.config.graph; this.config.client.service; - const runInExplorerLink = graphId - ? `${frontendUrlRoot}/${graphId}/engine/explorer?variant=${variant}&explorerURLState=${explorerURLState}` - : `${frontendUrlRoot}/sandbox/explorer?explorerURLState=${explorerURLState}${ + const runInExplorerPath = graphId + ? `${graphId}/engine/explorer?variant=${variant}&explorerURLState=${explorerURLState}` + : `/sandbox/explorer?explorerURLState=${explorerURLState}${ endpoint ? `&endpoint=${endpoint}` : "" }`; + const runInExplorerLink = join( + frontendUrlRoot, + runInExplorerPath + ); decorations.push({ document: uri,