Protobuf oneof JSON syntax issue - protocol-buffers

I have a proto which is something like (not the exact case but very similair)
message football {
repeated TeamDetails teamInfo= 1;
}
message TeamDetails {
string position = 1;
map<string, points> params = 2;
}
message points {
oneof value {
string string_value = 1;
double number_value = 2;
int32 int_value = 3;
}
}
and I have a few questions about this;
Is my oneof here okay and usable?
I keep getting an issue when trying to run the following JSON to the end point, I'm not sure what I'm doing wrong
"teamInfo": [
{
"position":"7th",
"params": {"Manchester United": 51}
}
]
gives me
"Error getting request data: bad input: expecting start of JSON object: '{' ; instead got 51"
I get the same error even after adding quotes around the 51, and if I replace the 'points' in the proto with <string, string> it would successfully work for the same JSON (but I don't want a string everytime, hence I'm trying to use the oneof but I'm getting this issue)

Please try:
"teamInfo": [
{
"position":"7th",
"params": {"Manchester United": { "int_value": 51 } }
}
]
The type of params is map<string, points> but your JSON has the equivalent of map<string, int32>. That's valid JSON but it's not the equivalent of the proto type.
Because points is oneof i.e. string (or) double (or) int32, I think you need to replace (any) oneof with a generic JSON object that can represent multiple (possible) types i.e. {...} and, in this case, you want the int32 field called int_value.

Related

grpc error: packets.SubmitCustomerRequest.details: object expected

I just created a proto file like this
service Customer {
rpc SubmitCustomer(SubmitCustomerRequest) returns (SubmitCustomerResponse) {}
}
message SubmitCustomerRequest {
string name = 1;
map<string, google.protobuf.Any> details = 2;
}
message SubmitCustomerResponse {
int64 id = 1;
}
The code itself works when I called this from the client. But, I'm having trouble with testing it directly from bloomrpc or postman.
{
"name": "great name",
"details": {
"some_details": "detail value",
"some_int": 123
}
}
it throws me this error when I tried to hit it
.packets.SubmitCustomerRequest.details: object expected
I think I'm aware that the problem is with the details syntax format when I hit it on postman, but I'm not sure what the correct format is supposed to be. I've tried modifying it with any other possible format and none works either.

GO is a complex nested structure

I wanted to clarify how to set values for
type ElkBulkInsert struct {
Index []struct {
_Index string `json:"_index"`
_Id string `json:"_id"`
} `json:"index"`
}
to make json.Marshall
there were no problems for the usual structure
package main
import (
"encoding/json"
"fmt"
)
type ElkBulkInsert struct {
Index []struct {
_Index string `json:"_index"`
_Id string `json:"_id"`
} `json:"index"`
}
type ElkBulIsertUrl struct {
Url string `json:"url"`
}
func main() {
sf := ElkBulIsertUrl{
Url: "http://mail.ru",
}
dd, _ := json.Marshal(sf)
fmt.Println(string(dd))
}
It's really unclear what you're asking here. Are you asking why the JSON output doesn't match what you expect? Are you unsure how to initialise/set values on the Index field of type []struct{...}?
Because it's quite unclear, I'll attempt to explain why your JSON output may appear to have missing fields (or why not all fields are getting populated), how you can initialise your fields, and how you may be able to improve the types you have.
General answer
If you want to marshal/unmarshal into a struct/type you made, there's a simple rule to keep in mind:
json.Marshal and json.Unmarshal can only access exported fields. An exported field have Capitalised identifiers. Your Index fieldin the ElkBulkInsert is a slice of an anonymous struct, which has no exported fields (_Index and _Id start with an underscore).
Because you're using the json:"_index" tags anyway, the field name itself doesn't have to even resemble the fields of the JSON itself. It's obviously preferable they do in most cases, but it's not required. As an aside: you have a field called Url. It's generally considered better form to follow the standards WRT initialisms, and rename that field to URL:
Words in names that are initialisms or acronyms (e.g. "URL" or "NATO") have a consistent case. For example, "URL" should appear as "URL" or "url" (as in "urlPony", or "URLPony"), never as "Url". Here's an example: ServeHTTP not ServeHttp.
This rule also applies to "ID" when it is short for "identifier," so write "appID" instead of "appId".
Code generated by the protocol buffer compiler is exempt from this rule. Human-written code is held to a higher standard than machine-written code.
With that being said, simply changing the types to this will work:
type ElkBulkInsert struct {
Index []struct {
Index string `json:"_index"`
ID string `json:"_id"`
} `json:"index"`
}
type ElkBulIsertUrl struct {
URL string `json:"url"`
}
Of course, this implies the data for ElkBulkInsert looks something like:
{
"index": [
{
"_index": "foo",
"_id": "bar"
},
{
"_index": "fizz",
"_id": "buzz"
}
]
}
When you want to set values for a structure like this, I generally find it easier to shy away from using anonymous struct fields like the one you have in your Index slice, and use something like:
type ElkInsertIndex struct {
ID string `json:"_id"`
Index string `json:"_index"`
}
type ElkBulkInsert struct {
Index []ElkInsertIndex `json:"index"`
}
This makes it a lot easier to populate the slice:
bulk := ElkBulkInsert{
Index: make([]ElkInsertIndex, 0, 10), // as an example
}
for i := 0; i < 10; i++ {
bulk.Index = append(bulk.Index, ElkInsertIndex{
ID: fmt.Sprintf("%d", i),
Index: fmt.Sprintf("Idx#%d", i), // wherever these values come from
})
}
Even easier (for instance when writing fixtures or unit tests) is to create a pre-populated literal:
data := ElkBulkInsert{
Index: []ElkInsertIndex{
{
ID: "foo",
Index: "bar",
},
{
ID: "fizz",
Index: "buzz",
},
},
}
With your current type, using the anonymous struct type, you can still do the same thing, but it looks messier, and requires more maintenance: you have to repeat the type:
data := ElkBulkInsert{
Index: []struct{
ID string `json:"_id"`
Index string `json:"_index"`
} {
ID: "foo",
Index: "bar",
},
{ // you can omit the field names if you know the order, and initialise all of them
"fizz",
"buzz",
},
}
Omitting field names when initialising in possible in both cases, but I'd advise against it. As fields get added/renamed/moved around over time, maintaining this mess becomes a nightmare. That's also why I'd strongly suggest you use move away from the anonymous struct here. They can be useful in places, but when you're representing a known data-format that comes from, or is sent to an external party (as JSON tends to do), I find it better to have all the relevant types named, labelled, documented somewhere. At the very least, you can add comments to each type, detailing what values it represents, and you can generate a nice, informative godoc from it all.

GraphQL doesn't consider dynamic arrays of strings valid strings

I have a query that works when manually typed:
queryName(where: { ids: ["1234567890123456789", "1234567890123456790"] }, offset: 0, max: 10) {
but when the same values are passed in a variable:
const idArr = ["1234567890123456789", "1234567890123456790"];
...
queryName(where: { ids: ${idArr} }, offset: 0, max: 10) {
I get the error:
Uncaught GraphQLError: Syntax Error: Expected Name, found Int "1234567890123456789"
Can anyone explain this?
Using string interpolation like that will result in the following value being inserted inside your string:
"1234567890123456789,1234567890123456790"
This is not valid GraphQL syntax and so results in a syntax error. Instead of using string interpolation, you should use GraphQL variables to provide dynamic values along with your query:
query ($idArr: [ID!]!) {
queryName(where: { ids: $idArr }, offset: 0, max: 10) {
...
}
}
Note that the type of the variable will depend on the argument where it's being used, which depends on whatever schema you're actually querying.
How you include the variables along with your request depends on the client you're using to make that request, which is not clear from your post. If you're using fetch or some other simple HTTP client, you just include the variables alongside the query as another property in the payload you send to the server:
{
"query": "...",
"variables": {
...
}
}

How to change the graphql typedefs name that is coming from json results of external api?

I have encountered an issue with the naming in my typedefs. The error prompting the following.
Syntax Error: Expected Name, found Int "24"
I am using Coinmarketcap Api and accessing it with my apollo graphql server. The api has naming such as 24h_volume_usd, percent_change_1h etc, but as long as integer within the name, it will have this name issue.
I am not really sure how can I fix this issue. Can anyone please help me out? Thank you very much.
Schema.js:
const typeDefs = `
type cryptos {
id: String
name: String
symbol: String
rank: String
price_usd: String
price_btc: String
24h_volume_usd: String
market_cap_usd: String
percent_change_1h: String
available_supply: String
total_supply: String
last_updated: String
}
type Query {
cryptos: [cryptos]
}
`
resolvers.js:
const resolvers = {
Query: {
cryptos: () => {
return axios.get('https://api.coinmarketcap.com/v1/ticker/').
then(result => result.data );
}
}
Process api result using map functions
`var newHashmap = {};
Object.keys(hashmap).forEach(function(key){
var value = hashmap[key];
key = key + "xxx";
console.log("changing:");
console.log(key); newHashmap[key] = value
});`

How to pass GraphQLEnumType in mutation as a string value

I have following GraphQLEnumType
const PackagingUnitType = new GraphQLEnumType({
name: 'PackagingUnit',
description: '',
values: {
Carton: { value: 'Carton' },
Stack: { value: 'Stack' },
},
});
On a mutation query if i pass PackagingUnit value as Carton (without quotes) it works. But If i pass as string 'Carton' it throws following error
In field "packagingUnit": Expected type "PackagingUnit", found "Carton"
Is there a way to pass the enum as a string from client side?
EDIT:
I have a form in my front end, where i collect the PackagingUnit type from user along with other fields. PackagingUnit type is represented as a string in front end (not the graphQL Enum type), Since i am not using Apollo Client or Relay, i had to construct the graphQL query string by myself.
Right now i am collecting the form data as JSON and then do JSON.stringify() and then remove the double Quotes on properties to get the final graphQL compatible query.
eg. my form has two fields packagingUnitType (An GraphQLEnumType) and noOfUnits (An GraphQLFloat)
my json structure is
{
packagingUnitType: "Carton",
noOfUnits: 10
}
convert this to string using JSON.stringify()
'{"packagingUnitType":"Carton","noOfUnits":10}'
And then remove the doubleQuotes on properties
{packagingUnitType:"Carton",noOfUnits:10}
Now this can be passed to the graphQL server like
newStackMutation(input: {packagingUnitType:"Carton", noOfUnits:10}) {
...
}
This works only if the enum value does not have any quotes. like below
newStackMutation(input: {packagingUnitType:Carton, noOfUnits:10}) {
...
}
Thanks
GraphQL queries can accept variables. This will be easier for you, as you will not have to do some tricky string-concatenation.
I suppose you use GraphQLHttp - or similar. To send your variables along the query, send a JSON body with a query key and a variables key:
// JSON body
{
"query": "query MyQuery { ... }",
"variables": {
"variable1": ...,
}
}
The query syntax is:
query MyMutation($input: NewStackMutationInput) {
newStackMutation(input: $input) {
...
}
}
And then, you can pass your variable as:
{
"input": {
"packagingUnitType": "Carton",
"noOfUnits": 10
}
}
GraphQL will understand packagingUnitType is an Enum type and will do the conversion for you.

Resources