how to create a struct/model in golang with dynamic/arbitrary fields - go

Is it possible to create a struct with dynamic/arbitrary fields and values?
My app will receive request with JSON body:
{
"Details": {
"Id": “123”,
},
"Event": {
"Event": "Event",
},
“RequestValues”: [
{
“Name": "Name1",
"Value": "Val1"
},
{
"Name": "Name2",
"Value": 2
},
{
"Name": “Foo”,
"Value": true
}
]
}
This will be unmarshalled to my model 'Request':
type Request struct {
Details Details `json:"Details"`
Event Event `json:"Event"`
RequestValues []RequestValues `json:"RequestValues"`
}
type Details struct {
Id string `json:"Id"`
}
type Event struct {
Event string `json:"Event"`
}
type RequestValues struct {
Name string `json:"Name"`
Value string `json:"Value"`
}
I have to re-map model 'Request' to a new model 'Event' with arbitrary fields in "Values". After marshalling new re- mapped model 'Event' I should get this JSON output that corresponds to the request:
{
"Event": "Event"
"Values": {
“Id": "123", <= non arbitrary mapping from Request.Detail.Id
"Name1": "Val1", <= arbitrary
"Name2": 2, <= arbitrary
“Foo”: true <= arbitrary
}
}
Arbitrary values will be mapped from "RequestValues". Names of those fields should be the values of Request.RequestValues.Name and their values should be the values of Request.RequestValues.Value
Here is my 'Event' model:
type Event struct {
Event string `json:"Event"`
Values Values `json:"Values"`
}
type Values struct{
Id string `json:"Id"`
}

Firstly, here's a JSON-valid copy of your JSON:
{
"Details": {
"Id": "123"
},
"Event": {
"Event": "Event"
},
"RequestValues": [
{
"RequestValueName": "Name1",
"RequestValue": "Val1"
},
{
"RequestValueName": "Name2",
"RequestValue": 2
},
{
"RequestValueName": "Foo",
"RequestValue": true
}
]
}
Start by creating a type Input struct{} to describe the JSON that you're looking to parse, and a type Output struct{} for the JSON that you're looking to generate, and write a little code to convert from one to the other. You don't have to add all of the fields right away - you can start with just Event for example and add more until you've got them all.
I've done this in https://play.golang.org/p/PvpKnFMrJjN to show you, but I would recommend having only a quick read of it before trying to recreate it yourself.
A useful tool to convert JSON into Go structs is https://mholt.github.io/json-to-go/ but it will trip on RequestValue in your example which has several data types (and is therefore where we use interface{}).

I thing that you can use map like this:
package main
import (
"fmt"
)
type Event struct {
event string
values map[string]string
}
func main() {
eventVar := Event{event: "event", values: map[string]string{}}
eventVar.values["Id"] = "12345"
eventVar.values["vale1"] = "value"
fmt.Println(eventVar)
}
You just need to validate somehow that the id it's in there, this if you need values in the same level.
I hope this works for you.

Related

Go Swagger using map[string]interface{} as response

I'm trying to construct a Go Swagger response to my existing code without changing a bunch of it and I currently have:
// DataExpressionInput - only public so we can use it embedded in dataExpression
// swagger:model
type DataExpressionInput struct {
Name string `json:"name"`
Expression string `json:"expression"`
Type expressionType `json:"type"`
Comments string `json:"comments"`
Tags []string `json:"tags"`
}
// swagger:model dataExpressionModel
type DataExpression struct {
// The main data expression information
//
// Required: true
*DataExpressionInput
// Additional metadata
*pixlUser.APIObjectItem
}
//swagger:response dataExpressionLookup
type DataExpressionLookup map[string]DataExpression
I'm trying to return a dataExpressionLookup Object via my API but when I export the swagger definition i get:
"definitions": {
"DataExpressionInput": {
"description": "DataExpressionInput - only public so we can use it embedded in dataExpression",
"x-go-package": "github.com/pixlise/core/v2/core/expression"
},
"dataExpressionModel": {
"x-go-name": "DataExpression",
"x-go-package": "github.com/pixlise/core/v2/core/expression"
}
},
"responses": {
"dataExpressionLookup": {
"description": "",
"schema": {}
},
"deleteResponse": {
"description": "",
"schema": {}
},
"genericError": {
"description": "",
"schema": {}
},
"shareResponse": {
"description": "",
"schema": {}
}
},

How to read GraphQL enum values in AppSync resolver template?

I have an enum in my schema in the following way:
type Item {
name: String!
}
enum ItemType {
Simple
Advanced
}
input ValueSimple { simple: Int }
input ValueAdvanced { advanced: Int }
input InputItemSchedule {
type: ItemType!
valueSimple: ValueSimple
valueAdvanced: ValueAdvanced
}
mutation Mutation {
addItem(name: String!, schedule: [InputItemSchedule]!): Item
}
I am trying to do conditional checks in the AppSync resolver template (data source id DynamoDB) but I don't know how to get the value for the item type:
#set($schedule = {})
#foreach($item in $context.args.schedule)
#set($scheduleItem = { })
#if($item.type == "Simple")
#set($scheduleItem.value = $item.valueSimple)
#elseif($item.type == "Advanced")
#set($scheduleItem.value = $item.valueAdvanced)
#else
## Set the type value to value for testing purposes
#set($scheduleItem.value = $util.dynamodb.toDynamoDBJson($item.type))
#end
$util.qr($schedule.add($scheduleItem))
#end
{
"version" : "2017-02-28",
"operation" : "PutItem",
"key" : {
"id": $util.dynamodb.toDynamoDBJson($util.autoId()),
},
"attributeValues" : {
"name": $util.dynamodb.toDynamoDBJson($ctx.args.name),
"schedule": $util.dynamodb.toDynamoDBJson($schedule)
}
}
When I make a request that triggers the resolver regardless of the enum value, I keep getting the following in DynamoDB:
...
"value": {
"S": "null"
}
How am I supposed to read the GraphQL enum value from the resolver template?

Define structure in golang

I am working on a project in which I receive payload data in the following format:
"name":"John",
"date":"2022-04-14",
"price":200,
"dependencies":[
{
"element_1":{
"items":[2,4],
"pricing":[1,2]
}
},
{
"element_2":{
"items":[5,4],
"pricing":[1,6]
}
}
]
How should I define the struct for this? If you know please let me know. Thanks!
This part
"dependencies":[
{
"element_1":{
"items":[2,4],
"pricing":[1,2]
}
},
{
"element_2":{
"items":[5,4],
"pricing":[1,6]
}
}
]
Looks like
type XXX struct {
Dependencies []map[string]Dependency `json:"dependencies"`
}
type Dependency struct {
Items []int // json…
Pricing []int // json…
}
But I am not sure… if element_x is dynamic or not. If it is predefined, ok.
If your json is
{
"name": "John",
"date": "2022-04-14",
"price": 200,
"dependencies":
[
{
"element_1":
{
"items":
[
2,
4
],
"pricing":
[
1,
2
]
}
},
{
"element_2":
{
"items":
[
5,
4
],
"pricing":
[
1,
6
]
}
}
]
}
Then you can create struct like this
type DemoStruct struct {
Name string `json:"name"`
Date string `json:"date"`
Price int64 `json:"price"`
Dependencies []Dependency `json:"dependencies"`
}
type Dependency struct {
Element1 *Element `json:"element_1,omitempty"`
Element2 *Element `json:"element_2,omitempty"`
}
type Element struct {
Items []int64 `json:"items"`
Pricing []int64 `json:"pricing"`
}

Creating GraphQl Request for a list of inputs

I've a query which I want to run over a list of different input. How can I do this in a single query request ?
My query looks like :
QuerySingle
query($personId:ID!, $vehicleId: ID!) {
person(personID: $personId) {
name,
gender,
hairColor
},
vehicle(id: $vehicleId) {
name,
model,
vehicleClass,
}
}
and arguments are like
{ "personId": 1, "vehicleId": "dmVoaWNsZXM6NA==" }
This runs well for a single input. Single Input Example
But I want to run it over a list of inputs I've like following :
[
{ "personId": 1, "vehicleId": "dmVoaWNsZXM6NA==" },
{ "personId": 2, "vehicleId": "dmVoaWNsZXM6Ng==" },
{ "personId": 3, "vehicleId": "dmVoaWNsZXM6Nw==" },
{ "personId": 4, "vehicleId": "dmVoaWNsZXM6OA==" },
{ "personId": 5, "vehicleId": "dmVoaWNsZXM6MTQ=" },
{ "personId": 6, "vehicleId": "dmVoaWNsZXM6MTY=" },
{ "personId": 7, "vehicleId": "dmVoaWNsZXM6MTg=" },
{ "personId": 8, "vehicleId": "dmVoaWNsZXM6MTk=" },
{ "personId": 9, "vehicleId": "dmVoaWNsZXM6MjA=" },
{ "personId": 10, "vehicleId": "dmVoaWNsZXM6MjQ=" }
]
How can I do this ?
One way which could be use aliases on person and vehicle and provide different input as different variable names. Example Query with different var aliases
QueryMultiUsingAlias
query($personId_1:ID!, $vehicleId_1: ID!,$personId_2:ID!, $vehicleId_2: ID!) {
person1: person(personID: $personId_1) {
...personData
},
vehicle1: vehicle(id: $vehicleId_1) {
...vehicleData
}
person2: person(personID: $personId_2) {
...personData
},
vehicle2: vehicle(id: $vehicleId_2) {
...vehicleData
}
}
fragment vehicleData on Vehicle {
name,
model,
vehicleClass
}
fragment personData on Person {
name,
gender,
hairColor
}
Parameters:
{
"personId_1": 1,
"vehicleId_1": "dmVoaWNsZXM6NA==",
"personId_2": 2,
"vehicleId_2": "dmVoaWNsZXM6Ng=="
}
Question I've are :
This query becomes too gross and hard to extend as more different type of data is added per input. or as more number of input parameters are added. Is there a cleaner way to represent this query ?
If there's no other way, Is there a functionality available in graphql-java to generate QueryMultiUsingAlias from QuerySingle based on input length ?
First, I'd first consider making the Vehicle type a field on Person (it seems like there is a relationship between and a vehicle maps to a person). You can then define your schema as follows:
type Vehicle {
// fields defining Vehicle type
}
type Person {
vehicle: Vehicle
//other fields defining Person type
}
Query {
person(personId: Int, vehicleId: Int): Person
}
Your resolvers could follow a similar implementation:
Query {
async person(obj, args, context, info) {
return await context.db.loadPersonByID(args.personId)
}
},
Person {
async vehicle(obj, args, context, info) {
return await context.db.loadVehicleByID(args.vehicleId)
}
}
Alternatively, if the result from your person db query includes the vehicleId as a property, the vehicleId should be accessible from the “obj” argument when resolving vehicle, negating the need for the vehicleId argument in the query (this is unclear though).
Finally, rather than passing an array of arguments and modifying your graph input schema in this respect, it may be cleanest to fire off a number of discrete async queries from the client using your array of arguments and wait for alll of those discrete requests to be resolved with Promise.all([query1, query2, etc.]) or equivalent. Each query would pass a personId and vehicleId from your array (assuming vehicleId is even still necessary given guidance above). You can map over your array in the following way in js as follows:
const queryResults = await Promise.all(argumentArray.map(async ({personId, vehicleId }) {
return await // fire query using personId and vehicleId
}))
}

GraphQL Mutation with JSON Patch

Are there any data types in GraphQL that can be used to describe a JSON Patch operation?
The structure of a JSON Patch operation is as follows.
{ "op": "add|replace|remove", "path": "/hello", "value": ["world"] }
Where value can be any valid JSON literal or object, such as.
"value": { "name": "michael" }
"value": "hello, world"
"value": 42
"value": ["a", "b", "c"]
op and path are always simple strings, value can be anything.
If you need to return JSON type then graphql have scalar JSON
which return any JSON type where you want to return it.
Here is schema
`
scalar JSON
type Response {
status: Boolean
message: String
data: JSON
}
type Test {
value: JSON
}
type Query {
getTest: Test
}
type Mutation {
//If you want to mutation then pass data as `JSON.stringify` or json formate
updateTest(value: JSON): Response
}
`
In resolver you can return anything in json format with key name "value"
//Query resolver
getTest: async (_, {}, { context }) => {
// return { "value": "hello, world" }
// return { "value": 42 }
// return { "value": ["a", "b", "c"] }
// return anything in json or string
return { "value": { "name": "michael" } }
},
// Mutation resolver
async updateTest(_, { value }, { }) {
// Pass data in JSON.stringify
// value : "\"hello, world\""
// value : "132456"
// value : "[\"a\", \"b\", \"c\"]"
// value : "{ \"name\": \"michael\" }"
console.log( JSON.parse(value) )
//JSON.parse return formated required data
return { status: true,
message: 'Test updated successfully!',
data: JSON.parse(value)
}
},
the only thing you need to specifically return "value" key to identify to get in query and mutation
Query
{
getTest {
value
}
}
// Which return
{
"data": {
"getTest": {
"value": {
"name": "michael"
}
}
}
}
Mutation
mutation {
updateTest(value: "{ \"name\": \"michael\" }") {
data
status
message
}
}
// Which return
{
"data": {
"updateTest": {
"data": null,
"status": true,
"message": "success"
}
}
}

Resources