Dynamic field or field of type (Struct, Map, Any) for custom options - protocol-buffers

Assuming I need to define a custom option for a method, for example:
message MessageBusOptions {
string namespace = 1;
optional string queue = 2;
}
extend google.protobuf.MethodOptions {
optional MessageBusOptions message_bus_options = 50000;
}
So far everything works as expected. I can define my service the following way:
services TestServices {
rpc SendMessage(MessageReq) returns(MessageRes) {
option (message_bus_options) = {
namespace: "abc",
queue: "default"
}
}
}
Now, how I can I add a dynamic field to the MessageBusOptions so that I can provide a dynamic value, for example:
services TestServices {
rpc SendMessage(MessageReq) returns(MessageRes) {
option (message_bus_options) = {
namespace: "abc",
queue: "default",
dynamic_field: {
a: "a",
b: "b"
}
}
}
}
I tried the following types (Map, Any, Struct) but all of them led to this error
Error while parsing option value for "xxx": Message type "xxx" has no field named "xxx".
How can I define such dynamic field in the custom option?

Related

parseValue() in custom graphQL scalar not triggered on mutation input

I have followed the example described here to use a custom directive to change the type of a field to a custom scalar. In the custom scalar I want to override the serialize() and the parseValue() functions, so that I can make REST calls.
When querying for the field, serialize() is triggered and I can do what I expect. However when running a mutation with the field as an input variable, my parseValue() function is never triggered. I've tried this with a custom scalar directly, and it triggered, so what magic am I missing when changing the type prevents this from working as expected?
My custom scalar looks like this
class MyCustomType extends GraphQLScalarType {
constructor(type: GraphQLScalarType, arg: number) {
super({
name: 'custom',
serialize(value: unknown) {
// I want to make an external call here
return value;
},
parseValue(value: unknown) {
// I want to make an external call here, using the arg in the constructor
return type.parseValue(value);
},
});
}
}
The directive is defined like this
`directive #custom(arg: Int) on FIELD_DEFINITION`
My schema looks like this
type SomeType {
field: String #custom(arg: 1)
}
and the mutation I run looks like this
mutation Mutation($input: [SomeTypeInput!]!) {
createSomeTypes(input: $input) {
someTypes {
field
}
}
}
{
"input": [
{
"field": "abc",
}
]
}

Definition nested value in InputObjectType with graphQL nexus

I have an object that has some nested values that I'm trying to define but can't find an example that has this setup.
mockObj = {
name: "John",
cars: { van: "", truck: "" }
}
const InitialQuestions = inputObjectType({
name: 'InitialQuestions',
definition: (t) => {
t.string('name')
** DEFINE CARS PROPERTY HERE **
}
})
In the above code, I'd like to define that values that come in the cars property. Since it's nested Im not sure how I can do that.

Is there a way to pass a parameter into a GraphQL query to specify the GraphQL type it should be run on?

I'm new to GraphQL and would like to be able to use a variable for a GraphQL name in a query.
I've attempted to use the standard $ syntax but with no luck.
Working query:
query Tryptych($section: SectionsEnum = home) {
enGB: entries(section: [$section], site: "enGB") {
... on Home {
tryptych {
...tryptychFields
}
}
}
}
What I'd like to be able to do:
query Tryptych($section: SectionsEnum = home, $interface: SomeType = Home) {
enGB: entries(section: [$section], site: "enGB") {
... on $interface {
tryptych {
...tryptychFields
}
}
}
}
Fragment for reference:
fragment tryptychFields on TryptychTryptych {
__typename
theme
tagline
firstImageTitle
firstImageContent
firstImageAsset {
url
}
firstImageLink
secondImageTitle
secondImageContent
secondImageAsset {
url
}
secondImageLink
thirdImageTitle
thirdImageContent
thirdImageAsset {
url
}
thirdImageLink
}
In the code snippet for what I'd like to achieve I get the error message:
Expected Name, found $
Thanks for the help.
A variable can only have one type, that type must be an input type (i.e. a scalar, enum or input object type), and it can only be used where an input type would be expected (i.e. a field or directive argument). In other words, the syntax you're suggesting is not supported.
If you have multiple types that may be returned by the same field, you may use any number of fragments to specify the selection set by type. The actual selection set will be determined at runtime when the type of the field is evaluated. For example, if the animal field returns a union of Cat, Dog and Bird types:
query {
animal {
... on Cat {
meows
}
... on Dog {
barks
}
... on Bird {
chirps
}
}
}
You may also use the #skip and #include directives to control which fields are selected:
query ($inAHouse: Boolean!, $withAMouse: Boolean!) {
greenEggs #skip(if: $inAHouse)
ham #include(if: $withAMouse)
}
And you may include multiple operations in a single document, and then specify an operationName with your request to tell the server which operation to run:
query OperationA {
foo
}
query OperationB {
bar
}

Single value vs Array in GraphQL Enum input types

How do you define an input type that either accepts a single enum value or an array of values in GraphQL?
According to GitHub GraphQL API,
{
securityVulnerabilities(first: 3, ecosystem: RUBYGEMS) {
nodes {
advisory {
description
}
}
}
}
But I think array can be good because user can search across ecosystem.
{
securityVulnerabilities(first: 3, ecosystem: [RUBYGEMS, NPM]) {
nodes {
advisory {
description
}
}
}
}
You can do this by defining the input value as an array [] of your defined Enum, something like:
enum MyEnum {
RUBYGEMS
NPM
}
type Query {
securityVulnerabilities(ecosystem: [MyEnum!]): MyReturnObject
}
And then you can query it like:
{
securityVulnerabilities(ecosystem: [RUBYGEMS, NPM]) {
....
}
}
And it works with both an array or a single value:
{
securityVulnerabilities(ecosystem: RUBYGEMS) {
....
}
}
The GraphQL spec explains the following:
If the value passed as an input to a list type is not a list and not
the null value, then the result of input coercion is a list of size
one [...]

In GraphQL, how to "aggregate" properties

Excuse the vague code, I can't really copy/paste. :)
I have type in GraphQL like this:
type Thing {
toBe: Boolean
orNot: Boolean
}
I'm trying to create a new property on this type that is an... aggregate of those two. Basically return a new value based upon those values. The code would be like:
if (this.toBe && !this.orNot) { return "To be!"; }
if (!this.toBe && !this.orNot) { return "OrNot!"; }
Does this make sense? So it would return something like:
Thing1 {
toBe: true;
orNot: false;
newProp: "To be!"
}
Yes, you can easily create aggregated fields in your graphql Object types by handling your required logic in that aggregated field resolver. While creating object types, you have instance of that object, and therefore, you can easily create aggregated fields which are not present in your domain models using object's data and this is one of the beauty of graphql. Note that this can differ on each implementation of GraphQL libraries. Following is the example for such use case in JavaScript and Scala.
Example in Graphql.js:
var FooType = new GraphQLObjectType({
name: 'Foo',
fields: {
toBe: { type: GraphQLBoolean},
orNot: { type: GraphQLBoolean},
newProp: { type: GraphQLString,
resolve(obj) {
if (obj.toBe && !obj.orNot) { return "To be!"; }
else { return "OrNot!"; }
}
}
});
Example in Sangria-graphql:
ObjectType(
"Foo",
"graphql object type for foo",
fields[Unit, Foo](
Field("toBe",BooleanType,resolve = _.value.name),
Field("orNot",BooleanType,resolve = _.value.path),
Field("newProp",StringType,resolve = c => {
if (c.value.toBe && !c.value.orNot) "To be!" else "OrNot!"
})
)
)
The various GraphQL server library implementations all have ways to provide resolver functions that can provide the value for a field. You'd have to include it in your schema and write the code for it, but this is a reasonable thing to do and the code you quote is a good starting point.
In Apollo in particular, you pass a map of resolvers that get passed as a resolvers: option to the ApolloServer constructor. If a field doesn't have a resolver it will default to returning the relevant field from the native JavaScript object. So you can write
const resolvers = {
Thing: {
newProp: (parent) => {
if (parent.toBe && !parent.orNot) { return "To be!"; }
if (!parent.toBe && !parent.orNot) { return "OrNot!"; }
return "That is the question";
}
}
};

Resources