Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add metadata to hive ui with linked directive support #6517

Open
wants to merge 13 commits into
base: main
Choose a base branch
from
1 change: 1 addition & 0 deletions integration-tests/tests/api/schema/check.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2185,6 +2185,7 @@ test.concurrent(
tags: null,
targetId: target.id,
url: null,
schemaMetadata: null,
});
await storage.destroy();

Expand Down
1 change: 1 addition & 0 deletions integration-tests/tests/api/schema/publish.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4082,6 +4082,7 @@ test.concurrent(
tags: null,
targetId: target.id,
url: null,
schemaMetadata: null,
});
await storage.destroy();

Expand Down
221 changes: 221 additions & 0 deletions integration-tests/tests/schema/metadata.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,221 @@
import { getServiceHost } from 'testkit/utils';
import type { SchemaBuilderApi } from '@hive/schema';
import { createTRPCProxyClient, httpLink } from '@trpc/client';

const host =
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: should this go somewhere into the test kit? 😄

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

possibly... I dont see anywhere in the codebase where we use SCHEMA_SERVICE_HOST_OVERRIDE... If anything I assume RUN_AGAINST_LOCAL_SERVICES is more practical for us to use for overriding the hosts.
I'd be in favor of removing the env var condition in this file and in the contracts.spec.ts before moving this into the test kit.

process.env['SCHEMA_SERVICE_HOST_OVERRIDE'] ||
(await getServiceHost('schema', 3002).then(r => `http://${r}`));

const client = createTRPCProxyClient<SchemaBuilderApi>({
links: [
httpLink({
url: host + `/trpc`,
fetch,
}),
],
});

describe('schema service can process metadata', async () => {
const result = await client.composeAndValidate.mutate({
type: 'federation',
native: true,
schemas: [
{
raw: /* GraphQL */ `
extend schema
@link(url: "https://specs.apollo.dev/link/v1.0")
@link(url: "https://specs.apollo.dev/federation/v2.3")
@link(url: "https://specs.graphql-hive.com/hive/v1.0", import: ["@meta"])
@meta(name: "schema", content: "user")

directive @meta(
name: String!
content: String!
) repeatable on SCHEMA | OBJECT | INTERFACE | FIELD_DEFINITION

type Query {
user: User @meta(name: "field", content: "user")
}

type User @meta(name: "type", content: "user") {
id: ID!
name: String @meta(name: "field", content: "User.name")
}
`,
source: 'user.graphql',
url: null,
},
{
raw: /* GraphQL */ `
extend schema
@link(url: "https://specs.apollo.dev/link/v1.0")
@link(url: "https://specs.apollo.dev/federation/v2.1")
@link(url: "https://specs.graphql-hive.com/hive/v1.0", import: ["@meta"])
@meta(name: "schema", content: "foo")

directive @meta(
name: String!
content: String!
) repeatable on SCHEMA | OBJECT | INTERFACE | FIELD_DEFINITION

type Query {
foo: String
}
`,
source: 'foo.graphql',
url: null,
},
],
external: null,
contracts: null,
});

test('@meta does not need to be in supergraph', () => {
expect(result.supergraph).toMatchInlineSnapshot(`
schema
@link(url: "https://specs.apollo.dev/link/v1.0")
@link(url: "https://specs.apollo.dev/join/v0.3", for: EXECUTION)





@link(url: "https://specs.graphql-hive.com/hive/v1.0", import: ["@meta"])
{
query: Query


}


directive @join__enumValue(graph: join__Graph!) repeatable on ENUM_VALUE

directive @join__field(
graph: join__Graph
requires: join__FieldSet
provides: join__FieldSet
type: String
external: Boolean
override: String
usedOverridden: Boolean
) repeatable on FIELD_DEFINITION | INPUT_FIELD_DEFINITION

directive @join__graph(name: String!, url: String!) on ENUM_VALUE

directive @join__implements(
graph: join__Graph!
interface: String!
) repeatable on OBJECT | INTERFACE

directive @join__type(
graph: join__Graph!
key: join__FieldSet
extension: Boolean! = false
resolvable: Boolean! = true
isInterfaceObject: Boolean! = false
) repeatable on OBJECT | INTERFACE | UNION | ENUM | INPUT_OBJECT | SCALAR

directive @join__unionMember(graph: join__Graph!, member: String!) repeatable on UNION

scalar join__FieldSet


directive @link(
url: String
as: String
for: link__Purpose
import: [link__Import]
) repeatable on SCHEMA

scalar link__Import

enum link__Purpose {
"""
\`SECURITY\` features provide metadata necessary to securely resolve fields.
"""
SECURITY

"""
\`EXECUTION\` features provide metadata necessary for operation execution.
"""
EXECUTION
}







enum join__Graph {
FOO_GRAPHQL @join__graph(name: "foo.graphql", url: "")
USER_GRAPHQL @join__graph(name: "user.graphql", url: "")
}

type Query @join__type(graph: FOO_GRAPHQL) @join__type(graph: USER_GRAPHQL) {
foo: String @join__field(graph: FOO_GRAPHQL)
user: User @join__field(graph: USER_GRAPHQL)
}

type User @join__type(graph: USER_GRAPHQL) {
id: ID!
name: String
}
`);
});

test('metadata is a union from schema, type, and field @meta', () => {
expect(result.schemaMetadata).toMatchInlineSnapshot(`
{
Query.foo: [
{
content: foo,
name: schema,
source: foo.graphql,
},
],
Query.user: [
{
content: user,
name: field,
source: user.graphql,
},
{
content: user,
name: schema,
source: user.graphql,
},
],
User.id: [
{
content: user,
name: type,
source: user.graphql,
},
{
content: user,
name: schema,
source: user.graphql,
},
],
User.name: [
{
content: User.name,
name: field,
source: user.graphql,
},
{
content: user,
name: type,
source: user.graphql,
},
{
content: user,
name: schema,
source: user.graphql,
},
],
}
`);
});
});
112 changes: 63 additions & 49 deletions packages/libraries/cli/examples/federation.products.graphql
Original file line number Diff line number Diff line change
@@ -1,60 +1,74 @@
enum CURRENCY_CODE {
USD
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I assume the prior version was for Federation 1? But I couldn't get the two example schemas working locally. I've opted to update them to Federation 2. Let me know if I should make a new version instead.

extend schema
@link(
url: "https://specs.apollo.dev/federation/v2.3"
import: ["@key", "@shareable", "@inaccessible", "@tag"]
)
@link(url: "https://specs.graphql-hive.com/hive/v1.0", import: ["@meta"])
@meta(name: "priority", content: "tier1")

directive @meta(
name: String!
content: String!
) repeatable on SCHEMA | OBJECT | INTERFACE | FIELD_DEFINITION

directive @myDirective(a: String!) on FIELD_DEFINITION

directive @hello on FIELD_DEFINITION

type Query {
allProducts: [ProductItf] @meta(name: "owner", content: "hive-team")
product(id: ID!): ProductItf
}

type Department {
category: ProductCategory
url: String
interface ProductItf implements SkuItf @meta(name: "domain", content: "products") {
id: ID!
sku: String
name: String
package: String
variation: ProductVariation
dimensions: ProductDimension
createdBy: User
hidden: String @inaccessible
oldField: String @deprecated(reason: "refactored out")
}

type Money {
amount: Float
currencyCode: CURRENCY_CODE
interface SkuItf {
sku: String
}

"""
Here are some helpful details about your type
"""
type Price {
cost: Money
type Product implements ProductItf & SkuItf
@key(fields: "id")
@key(fields: "sku package")
@key(fields: "sku variation { id }")
@meta(name: "owner", content: "product-team") {
id: ID! @tag(name: "hi-from-products")
sku: String @meta(name: "unique", content: "true")
name: String @hello
package: String
variation: ProductVariation
dimensions: ProductDimension
createdBy: User
hidden: String
reviewsScore: Float!
oldField: String
}

"""
A number between 0 and 1 signifying the % discount
"""
deal: Float
dealSavings: Money
enum ShippingClass {
STANDARD
EXPRESS
}

"""
This is an Entity, docs:https://www.apollographql.com/docs/federation/entities/
You will need to define a __resolveReference resolver for the type you define, docs: https://www.apollographql.com/docs/federation/entities/#resolving
"""
type Product @key(fields: "id") {
type ProductVariation {
id: ID!
title: String
url: String
description: String
price: Price
salesRank(category: ProductCategory = ALL): Int
salesRankOverall: Int
salesRankInCategory: Int
category: ProductCategory
images(size: Int = 1000): [String]
primaryImage(size: Int = 1000): String
}

enum ProductCategory {
ALL
GIFT_CARDS
ELECTRONICS
CAMERA_N_PHOTO
VIDEO_GAMES
BOOKS
CLOTHING
}

extend type Query {
bestSellers(category: ProductCategory = ALL): [Product]
categories: [Department]
product(id: ID!): Product
name: String
}

type ProductDimension @shareable {
size: String
weight: Float
}

type User @key(fields: "email") {
email: ID!
totalProductsCreated: Int @shareable
}
Loading
Loading