Skip to content

Commit

Permalink
feat(grpc): selectQueryOrMutationField
Browse files Browse the repository at this point in the history
  • Loading branch information
ardatan committed Jan 17, 2025
1 parent 417770c commit b3ddf76
Show file tree
Hide file tree
Showing 17 changed files with 298 additions and 59 deletions.
46 changes: 46 additions & 0 deletions .changeset/large-oranges-draw.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
---
'@graphql-mesh/openapi': patch
'@graphql-mesh/grpc': patch
'@graphql-mesh/raml': patch
'@omnigraph/openapi': patch
'@graphql-mesh/types': patch
'@omnigraph/grpc': patch
'@omnigraph/raml': patch
---

New option `selectQueryOrMutationField` to decide which field belongs to which root type explicitly.

```ts filename="mesh.config.ts"
import loadGrpcSubgraph from '@omnigraph/grpc'
import { defineConfig } from '@graphql-mesh/compose-cli'

export const composeConfig = defineConfig({
subgraphs: [
{
sourceHandler: loadGrpcSubgraph('MyGrpcApi', {
/** .. **/

// Prefix to collect Query method default: list, get
prefixQueryMethod: ['list', 'get'],

// Select certain fields as Query or Mutation
// This overrides `prefixQueryMethod`
selectQueryOrMutationField: [
{
// You can use a pattern matching with *
fieldName: '*RetrieveMovies',
type: 'Query',
},
// Or you can use a specific field name
// This will make the field GetMovie available as a Mutation
// Because it would be Query because of `prefixQueryMethod`
{
fieldName: 'GetMovie',
type: 'Mutation'
}
]
})
}
]
});
```
124 changes: 123 additions & 1 deletion packages/legacy/handlers/grpc/test/__snapshots__/handler.spec.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -1102,7 +1102,7 @@ type Subscription {
scalar TransportOptions"
`;

exports[`gRPC Handler Load proto with prefixQueryMethod should load the retrieve-movie.proto 1`] = `
exports[`gRPC Handler Load proto with prefixQueryMethod and selectQueryOrMutationField should load the retrieve-movie.proto with prefixQueryMethod 1`] = `
"schema @transport(subgraph: "prefixQueryMethod", kind: "grpc", location: "localhost", options: {requestTimeout: 200000, roots: [{name: "Root0", rootJson: "{\\"options\\":{\\"syntax\\":\\"proto3\\"},\\"nested\\":{\\"io\\":{\\"nested\\":{\\"xtech\\":{\\"nested\\":{\\"Genre\\":{\\"values\\":{\\"UNSPECIFIED\\":0,\\"ACTION\\":1,\\"DRAMA\\":2},\\"comment\\":null,\\"comments\\":{\\"UNSPECIFIED\\":null,\\"ACTION\\":null,\\"DRAMA\\":null}},\\"Movie\\":{\\"fields\\":{\\"name\\":{\\"type\\":\\"string\\",\\"id\\":1,\\"comment\\":null},\\"year\\":{\\"type\\":\\"int32\\",\\"id\\":2,\\"comment\\":null},\\"rating\\":{\\"type\\":\\"float\\",\\"id\\":3,\\"comment\\":null},\\"cast\\":{\\"rule\\":\\"repeated\\",\\"type\\":\\"string\\",\\"id\\":4,\\"comment\\":\\"list of cast\\"},\\"time\\":{\\"type\\":\\"google.protobuf.Timestamp\\",\\"id\\":5,\\"comment\\":null},\\"genre\\":{\\"type\\":\\"Genre\\",\\"id\\":6,\\"comment\\":null}},\\"comment\\":\\"movie message payload\\"},\\"EmptyRequest\\":{\\"fields\\":{},\\"comment\\":null},\\"movie_request\\":{\\"fields\\":{\\"movie\\":{\\"type\\":\\"Movie\\",\\"id\\":1,\\"comment\\":null}},\\"comment\\":null},\\"movie_request_by_ids\\":{\\"fields\\":{\\"movieIds\\":{\\"rule\\":\\"repeated\\",\\"type\\":\\"string\\",\\"id\\":1,\\"comment\\":null}},\\"comment\\":null},\\"SearchByCastRequest\\":{\\"fields\\":{\\"castName\\":{\\"type\\":\\"string\\",\\"id\\":1,\\"comment\\":null}},\\"comment\\":null},\\"MoviesResult\\":{\\"fields\\":{\\"result\\":{\\"rule\\":\\"repeated\\",\\"type\\":\\"Movie\\",\\"id\\":1,\\"comment\\":\\"list of movies\\"}},\\"comment\\":\\"movie result message, contains list of movies\\"},\\"Example\\":{\\"methods\\":{\\"GetMovies\\":{\\"requestType\\":\\"movie_request\\",\\"responseType\\":\\"MoviesResult\\",\\"comment\\":\\"get all movies\\"},\\"RetrieveMovies\\":{\\"requestType\\":\\"movie_request_by_ids\\",\\"responseType\\":\\"MoviesResult\\",\\"comment\\":\\"get movies\\"},\\"SearchMoviesByCast\\":{\\"requestType\\":\\"SearchByCastRequest\\",\\"responseType\\":\\"Movie\\",\\"responseStream\\":true,\\"comment\\":\\"search movies by the name of the cast\\"}},\\"comment\\":null},\\"AnotherExample\\":{\\"methods\\":{\\"GetMovies\\":{\\"requestType\\":\\"movie_request\\",\\"responseType\\":\\"MoviesResult\\",\\"comment\\":\\"get all movies\\"},\\"RetrieveMovies\\":{\\"requestType\\":\\"movie_request_by_ids\\",\\"responseType\\":\\"MoviesResult\\",\\"comment\\":\\"get movies\\"},\\"SearchMoviesByCast\\":{\\"requestType\\":\\"SearchByCastRequest\\",\\"responseType\\":\\"Movie\\",\\"responseStream\\":true,\\"comment\\":\\"search movies by the name of the cast\\"}},\\"comment\\":null}}}}},\\"google\\":{\\"nested\\":{\\"protobuf\\":{\\"nested\\":{\\"Timestamp\\":{\\"fields\\":{\\"seconds\\":{\\"type\\":\\"int64\\",\\"id\\":1},\\"nanos\\":{\\"type\\":\\"int32\\",\\"id\\":2}},\\"comment\\":null}}}}}}}"}]}) {
query: Query
mutation: Mutation
Expand Down Expand Up @@ -1227,3 +1227,125 @@ type Subscription {
scalar TransportOptions"
`;

exports[`gRPC Handler Load proto with prefixQueryMethod and selectQueryOrMutationField should load the retrieve-movie.proto with selectQueryOrMutationField 1`] = `
"schema @transport(subgraph: "selectQueryOrMutationField", kind: "grpc", location: "localhost", options: {requestTimeout: 200000, roots: [{name: "Root0", rootJson: "{\\"options\\":{\\"syntax\\":\\"proto3\\"},\\"nested\\":{\\"io\\":{\\"nested\\":{\\"xtech\\":{\\"nested\\":{\\"Genre\\":{\\"values\\":{\\"UNSPECIFIED\\":0,\\"ACTION\\":1,\\"DRAMA\\":2},\\"comment\\":null,\\"comments\\":{\\"UNSPECIFIED\\":null,\\"ACTION\\":null,\\"DRAMA\\":null}},\\"Movie\\":{\\"fields\\":{\\"name\\":{\\"type\\":\\"string\\",\\"id\\":1,\\"comment\\":null},\\"year\\":{\\"type\\":\\"int32\\",\\"id\\":2,\\"comment\\":null},\\"rating\\":{\\"type\\":\\"float\\",\\"id\\":3,\\"comment\\":null},\\"cast\\":{\\"rule\\":\\"repeated\\",\\"type\\":\\"string\\",\\"id\\":4,\\"comment\\":\\"list of cast\\"},\\"time\\":{\\"type\\":\\"google.protobuf.Timestamp\\",\\"id\\":5,\\"comment\\":null},\\"genre\\":{\\"type\\":\\"Genre\\",\\"id\\":6,\\"comment\\":null}},\\"comment\\":\\"movie message payload\\"},\\"EmptyRequest\\":{\\"fields\\":{},\\"comment\\":null},\\"movie_request\\":{\\"fields\\":{\\"movie\\":{\\"type\\":\\"Movie\\",\\"id\\":1,\\"comment\\":null}},\\"comment\\":null},\\"movie_request_by_ids\\":{\\"fields\\":{\\"movieIds\\":{\\"rule\\":\\"repeated\\",\\"type\\":\\"string\\",\\"id\\":1,\\"comment\\":null}},\\"comment\\":null},\\"SearchByCastRequest\\":{\\"fields\\":{\\"castName\\":{\\"type\\":\\"string\\",\\"id\\":1,\\"comment\\":null}},\\"comment\\":null},\\"MoviesResult\\":{\\"fields\\":{\\"result\\":{\\"rule\\":\\"repeated\\",\\"type\\":\\"Movie\\",\\"id\\":1,\\"comment\\":\\"list of movies\\"}},\\"comment\\":\\"movie result message, contains list of movies\\"},\\"Example\\":{\\"methods\\":{\\"GetMovies\\":{\\"requestType\\":\\"movie_request\\",\\"responseType\\":\\"MoviesResult\\",\\"comment\\":\\"get all movies\\"},\\"RetrieveMovies\\":{\\"requestType\\":\\"movie_request_by_ids\\",\\"responseType\\":\\"MoviesResult\\",\\"comment\\":\\"get movies\\"},\\"SearchMoviesByCast\\":{\\"requestType\\":\\"SearchByCastRequest\\",\\"responseType\\":\\"Movie\\",\\"responseStream\\":true,\\"comment\\":\\"search movies by the name of the cast\\"}},\\"comment\\":null},\\"AnotherExample\\":{\\"methods\\":{\\"GetMovies\\":{\\"requestType\\":\\"movie_request\\",\\"responseType\\":\\"MoviesResult\\",\\"comment\\":\\"get all movies\\"},\\"RetrieveMovies\\":{\\"requestType\\":\\"movie_request_by_ids\\",\\"responseType\\":\\"MoviesResult\\",\\"comment\\":\\"get movies\\"},\\"SearchMoviesByCast\\":{\\"requestType\\":\\"SearchByCastRequest\\",\\"responseType\\":\\"Movie\\",\\"responseStream\\":true,\\"comment\\":\\"search movies by the name of the cast\\"}},\\"comment\\":null}}}}},\\"google\\":{\\"nested\\":{\\"protobuf\\":{\\"nested\\":{\\"Timestamp\\":{\\"fields\\":{\\"seconds\\":{\\"type\\":\\"int64\\",\\"id\\":1},\\"nanos\\":{\\"type\\":\\"int32\\",\\"id\\":2}},\\"comment\\":null}}}}}}}"}]}) {
query: Query
subscription: Subscription
}
directive @enum(subgraph: String, value: String) on ENUM_VALUE
directive @grpcMethod(subgraph: String, rootJsonName: String, objPath: String, methodName: String, responseStream: Boolean) on FIELD_DEFINITION
directive @grpcConnectivityState(subgraph: String, rootJsonName: String, objPath: String) on FIELD_DEFINITION
"""
Directs the executor to stream plural fields when the \`if\` argument is true or undefined.
"""
directive @stream(
"""Stream when true or undefined."""
if: Boolean! = true
"""Unique name"""
label: String
"""Number of items to return immediately"""
initialCount: Int = 0
) on FIELD
directive @transport(subgraph: String, kind: String, location: String, options: TransportOptions) repeatable on SCHEMA
type Query {
"""get all movies"""
io_xtech_Example_GetMovies(input: io__xtech__movie_request_Input): io__xtech__MoviesResult @grpcMethod(subgraph: "selectQueryOrMutationField", rootJsonName: "Root0", objPath: "io.xtech.Example", methodName: "GetMovies", responseStream: false)
"""get movies"""
io_xtech_Example_RetrieveMovies(input: io__xtech__movie_request_by_ids_Input): io__xtech__MoviesResult @grpcMethod(subgraph: "selectQueryOrMutationField", rootJsonName: "Root0", objPath: "io.xtech.Example", methodName: "RetrieveMovies", responseStream: false)
"""search movies by the name of the cast"""
io_xtech_Example_SearchMoviesByCast(input: io__xtech__SearchByCastRequest_Input): [io__xtech__Movie] @grpcMethod(subgraph: "selectQueryOrMutationField", rootJsonName: "Root0", objPath: "io.xtech.Example", methodName: "SearchMoviesByCast", responseStream: true)
io_xtech_Example_connectivityState(tryToConnect: Boolean): ConnectivityState @grpcConnectivityState(subgraph: "selectQueryOrMutationField", rootJsonName: "Root0", objPath: "io.xtech.Example")
"""get all movies"""
io_xtech_AnotherExample_GetMovies(input: io__xtech__movie_request_Input): io__xtech__MoviesResult @grpcMethod(subgraph: "selectQueryOrMutationField", rootJsonName: "Root0", objPath: "io.xtech.AnotherExample", methodName: "GetMovies", responseStream: false)
"""get movies"""
io_xtech_AnotherExample_RetrieveMovies(input: io__xtech__movie_request_by_ids_Input): io__xtech__MoviesResult @grpcMethod(subgraph: "selectQueryOrMutationField", rootJsonName: "Root0", objPath: "io.xtech.AnotherExample", methodName: "RetrieveMovies", responseStream: false)
"""search movies by the name of the cast"""
io_xtech_AnotherExample_SearchMoviesByCast(input: io__xtech__SearchByCastRequest_Input): [io__xtech__Movie] @grpcMethod(subgraph: "selectQueryOrMutationField", rootJsonName: "Root0", objPath: "io.xtech.AnotherExample", methodName: "SearchMoviesByCast", responseStream: true)
io_xtech_AnotherExample_connectivityState(tryToConnect: Boolean): ConnectivityState @grpcConnectivityState(subgraph: "selectQueryOrMutationField", rootJsonName: "Root0", objPath: "io.xtech.AnotherExample")
}
"""movie result message, contains list of movies"""
type io__xtech__MoviesResult {
"""list of movies"""
result: [io__xtech__Movie]
}
"""movie message payload"""
type io__xtech__Movie {
name: String
year: Int
rating: Float
"""list of cast"""
cast: [String]
time: google__protobuf__Timestamp
genre: io__xtech__Genre
}
type google__protobuf__Timestamp {
seconds: BigInt
nanos: Int
}
"""
The \`BigInt\` scalar type represents non-fractional signed whole numeric values.
"""
scalar BigInt
enum io__xtech__Genre {
UNSPECIFIED @enum(subgraph: "selectQueryOrMutationField", value: "0")
ACTION @enum(subgraph: "selectQueryOrMutationField", value: "1")
DRAMA @enum(subgraph: "selectQueryOrMutationField", value: "2")
}
input io__xtech__movie_request_Input {
movie: io__xtech__Movie_Input
}
"""movie message payload"""
input io__xtech__Movie_Input {
name: String
year: Int
rating: Float
"""list of cast"""
cast: [String]
time: google__protobuf__Timestamp_Input
genre: io__xtech__Genre
}
input google__protobuf__Timestamp_Input {
seconds: BigInt
nanos: Int
}
input io__xtech__movie_request_by_ids_Input {
movieIds: [String]
}
input io__xtech__SearchByCastRequest_Input {
castName: String
}
enum ConnectivityState {
IDLE
CONNECTING
READY
TRANSIENT_FAILURE
SHUTDOWN
}
type Subscription {
"""search movies by the name of the cast"""
io_xtech_Example_SearchMoviesByCast(input: io__xtech__SearchByCastRequest_Input): io__xtech__Movie @grpcMethod(subgraph: "selectQueryOrMutationField", rootJsonName: "Root0", objPath: "io.xtech.Example", methodName: "SearchMoviesByCast", responseStream: true)
"""search movies by the name of the cast"""
io_xtech_AnotherExample_SearchMoviesByCast(input: io__xtech__SearchByCastRequest_Input): io__xtech__Movie @grpcMethod(subgraph: "selectQueryOrMutationField", rootJsonName: "Root0", objPath: "io.xtech.AnotherExample", methodName: "SearchMoviesByCast", responseStream: true)
}
scalar TransportOptions"
`;
38 changes: 36 additions & 2 deletions packages/legacy/handlers/grpc/test/handler.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,8 +68,8 @@ describe('gRPC Handler', () => {
});
});

describe('Load proto with prefixQueryMethod', () => {
test(`should load the retrieve-movie.proto`, async () => {
describe('Load proto with prefixQueryMethod and selectQueryOrMutationField', () => {
test(`should load the retrieve-movie.proto with prefixQueryMethod`, async () => {
const file = 'retrieve-movie.proto';
const config: YamlConfig.GrpcHandler = {
endpoint: 'localhost',
Expand All @@ -93,6 +93,40 @@ describe('gRPC Handler', () => {

const { schema } = await handler.getMeshSource();

expect(schema).toBeInstanceOf(GraphQLSchema);
expect(validateSchema(schema)).toHaveLength(0);
expect(printSchemaWithDirectives(schema)).toContain('AnotherExample_RetrieveMovies');
expect(printSchemaWithDirectives(schema)).toMatchSnapshot();
});
test(`should load the retrieve-movie.proto with selectQueryOrMutationField`, async () => {
const file = 'retrieve-movie.proto';
const config: YamlConfig.GrpcHandler = {
endpoint: 'localhost',
source: {
file: join(__dirname, './fixtures/proto-tests', file),
load: { includeDirs: [join(__dirname, './fixtures/proto-tests')] },
},
selectQueryOrMutationField: [
{
fieldName: '*RetrieveMovies',
type: 'Query',
},
],
};
using cache = new InMemoryLRUCache();
const handler = new GrpcHandler({
name: 'selectQueryOrMutationField',
config,
cache,
pubsub,
store,
logger,
importFn: defaultImportFn,
baseDir: __dirname,
});

const { schema } = await handler.getMeshSource();

expect(schema).toBeInstanceOf(GraphQLSchema);
expect(validateSchema(schema)).toHaveLength(0);
expect(printSchemaWithDirectives(schema)).toContain('AnotherExample_RetrieveMovies');
Expand Down
5 changes: 4 additions & 1 deletion packages/legacy/handlers/grpc/yaml-config.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,10 @@ type GrpcHandler @md {
prefix to collect Query method default: list, get
"""
prefixQueryMethod: [String]

"""
Allows to explicitly override the default operation (Query or Mutation) for any gRPC operation
"""
selectQueryOrMutationField: [SelectQueryOrMutationFieldConfig]
schemaHeaders: JSON
}

Expand Down
4 changes: 2 additions & 2 deletions packages/legacy/handlers/openapi/yaml-config.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ type OpenapiHandler @md {
"""
Allows to explicitly override the default operation (Query or Mutation) for any OAS operation
"""
selectQueryOrMutationField: [OASSelectQueryOrMutationFieldConfig]
selectQueryOrMutationField: [SelectQueryOrMutationFieldConfig]
"""
JSON object representing the query search parameters to add to the API calls
"""
Expand All @@ -62,7 +62,7 @@ enum QueryOrMutation {
Mutation
}

type OASSelectQueryOrMutationFieldConfig {
type SelectQueryOrMutationFieldConfig {
type: QueryOrMutation!
fieldName: String!
}
4 changes: 2 additions & 2 deletions packages/legacy/handlers/raml/yaml-config.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ type RAMLHandler {
schemaHeaders: JSON
operationHeaders: JSON
ignoreErrorResponses: Boolean
selectQueryOrMutationField: [RAMLSelectQueryOrMutationFieldConfig]
selectQueryOrMutationField: [SelectQueryOrMutationFieldConfig]
queryParams: Any

"""
Expand All @@ -24,7 +24,7 @@ enum QueryOrMutation {
Mutation
}

type RAMLSelectQueryOrMutationFieldConfig {
type SelectQueryOrMutationFieldConfig {
type: QueryOrMutation!
fieldName: String!
}
32 changes: 12 additions & 20 deletions packages/legacy/types/src/config-schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -963,6 +963,14 @@
"additionalItems": false,
"description": "prefix to collect Query method default: list, get"
},
"selectQueryOrMutationField": {
"type": "array",
"items": {
"$ref": "#/definitions/SelectQueryOrMutationFieldConfig"
},
"additionalItems": false,
"description": "Allows to explicitly override the default operation (Query or Mutation) for any gRPC operation"
},
"schemaHeaders": {
"type": "object",
"properties": {}
Expand Down Expand Up @@ -1980,7 +1988,7 @@
"selectQueryOrMutationField": {
"type": "array",
"items": {
"$ref": "#/definitions/OASSelectQueryOrMutationFieldConfig"
"$ref": "#/definitions/SelectQueryOrMutationFieldConfig"
},
"additionalItems": false,
"description": "Allows to explicitly override the default operation (Query or Mutation) for any OAS operation"
Expand All @@ -1997,10 +2005,10 @@
},
"required": ["source"]
},
"OASSelectQueryOrMutationFieldConfig": {
"SelectQueryOrMutationFieldConfig": {
"additionalProperties": false,
"type": "object",
"title": "OASSelectQueryOrMutationFieldConfig",
"title": "SelectQueryOrMutationFieldConfig",
"properties": {
"type": {
"type": "string",
Expand Down Expand Up @@ -2933,7 +2941,7 @@
"selectQueryOrMutationField": {
"type": "array",
"items": {
"$ref": "#/definitions/RAMLSelectQueryOrMutationFieldConfig"
"$ref": "#/definitions/SelectQueryOrMutationFieldConfig"
},
"additionalItems": false
},
Expand All @@ -2959,22 +2967,6 @@
},
"required": ["source"]
},
"RAMLSelectQueryOrMutationFieldConfig": {
"additionalProperties": false,
"type": "object",
"title": "RAMLSelectQueryOrMutationFieldConfig",
"properties": {
"type": {
"type": "string",
"enum": ["query", "mutation", "Query", "Mutation"],
"description": "Allowed values: query, mutation, Query, Mutation"
},
"fieldName": {
"type": "string"
}
},
"required": ["type", "fieldName"]
},
"SoapHandler": {
"additionalProperties": false,
"type": "object",
Expand Down
Loading

0 comments on commit b3ddf76

Please sign in to comment.