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"`
}
Related
In my case, I have an app that users can subscript to some in-app events.
I want to call a mutation from one of my microservices and send several user ids as a list to the mutation, and then all clients that subscript to that mutation receive '[1]'.
schema
type Mutation {
setUsersAlarm(user_id: [Int]): UserIDList
}
type Subscription {
subscripesetUsersAlarm: UserIDList
#aws_subscribe(mutations: ["setUsersAlarm"])
}
type UserIDList {
id_list: [Int]
}
schema {
query: Query
mutation: Mutation
subscription: Subscription
}
Mutation Resolver
request template
{
"version": "2017-02-28",
"payload":$util.toJson($context.args["user_id"])
}
response template
{
"id_list":$util.toJson($context.result)
}
Subscription Resolver
request template
{
"version": "2017-02-28",
"payload": {
"hello": "local",
}
}
response template
$extensions.setSubscriptionFilter({
"filterGroup": [
{
"filters" : [
{
"fieldName" : "id_list",
"operator" : "contains",
#* I can get the value from cognito or from
user input arguments*#
"value" : 10
}
]
}
]
})
#set ($myList = [1])
#set( $ctx.result.id_list =$myList)
$util.toJson($ctx.result)
Query
subscription MySubscription {
subscripesetUsersAlarm {
id_list
}
}
mutation MyMutation {
setUserRefreshToken(user_id: [10, 12]) {
id_list
flg
}
}
Output of mutation
{
"data": {
"setUsersAlarm": {
"id_list": [
10,
12
]
}
}
}
Output of subscription
I want to receive the below result in subscription:
{
"data": {
"subscripesetUsersAlarm": {
"id_list": [1]
}
}
}
but I receive this:
{
"data": {
"subscripesetUsersAlarm": {
"id_list": [
10,
12
]
}
}
}
I want to customize the subscription response depending on my clients
I have very limited knowledge on GraphQL as I am still in the learning process. Now I stumbled upon an issue that I cannot resolve by myself without some help.
I'm using HotChocolate in my service.
I have a class ConsumerProductCategory with a Guid as Id which has a parent that is also a ConsumerProductCategory (think category > sub-category > ...)
Now I want to get the sub categories for a specific category, in linq you would write:
.Where(cat => cat.Parent.Id == id)
First of all lets start with our classes:
public class BaseViewModel : INode
{
public virtual Guid Id { get; set; }
}
public class ConsumerProductCategory : BaseViewModel
{
public ConsumerProductCategory()
{
}
public string Name { get; set; }
[UsePaging]
[UseFiltering]
[UseSorting]
public List<ConsumerProduct> Products { get; set; } = new List<ConsumerProduct>();
public ConsumerProductCategoryImage Image { get; set; }
public ConsumerProductCategory Parent { get; set; } = null;
public bool HasParent => this.Parent != null;
}
The object type definition is like this:
public class ConsumerProductCategoryType : ObjectType<ConsumerProductCategory>
{
protected override void Configure(IObjectTypeDescriptor<ConsumerProductCategory> descriptor)
{
descriptor
.Name(nameof(ConsumerProductCategory));
descriptor
.Description("Categories.");
descriptor
.Field(x => x.Id)
//.Type<UuidType>()
.Type<IdType>()
.Description($"{nameof(ConsumerProductCategory)} Id.");
descriptor
.Field(x => x.Name)
.Type<StringType>()
.Description($"{nameof(ConsumerProductCategory)} name.");
descriptor
.Field(x => x.Parent)
.Description($"{nameof(ConsumerProductCategory)} parent category.");
descriptor
.Field(x => x.Products)
.Description($"{nameof(ConsumerProductCategory)} products.");
descriptor
.ImplementsNode()
.IdField(t => t.Id)
.ResolveNode((context, id) => context.Service<IConsumerProductCategoryService>().GetByIdAsync(id));
}
}
The query to get the "main" categories would be like this:
query GetAllCategories {
consumerProductCategories(
#request: { searchTerm: "2"}
first: 10
after: null
where: { hasParent: { eq: false } }
order: {
name: ASC
}
) {
nodes {
id
name
image {
url
alt
}
}
pageInfo {
endCursor
hasNextPage
}
}
}
This returns this result:
{
"data": {
"consumerProductCategories": {
"nodes": [
{
"id": "Q29uc3VtZXJQcm9kdWN0Q2F0ZWdvcnkKZ2EyOTYxNmRlMWMzMjQ4ZTU4YTU2YzRjYjdhMGQ5NmY5",
"name": "Category 1",
"image": {
"url": "https://picsum.photos/200",
"alt": "Category 1 Image"
}
},
{
"id": "Q29uc3VtZXJQcm9kdWN0Q2F0ZWdvcnkKZ2NmZWI0YzNiMGQyNjQyOWI4MGU0MmQ1NGNjYWE1N2Q4",
"name": "Category 2",
"image": {
"url": "https://picsum.photos/200",
"alt": "Category 2 Image"
}
},
{
"id": "Q29uc3VtZXJQcm9kdWN0Q2F0ZWdvcnkKZ2I0MjhjYWE2NGMxNTQ4MTdiMjM1ZWFhZWU3OGRhYWYz",
"name": "Category 3",
"image": {
"url": "https://picsum.photos/200",
"alt": "Category 3 Image"
}
}
],
"pageInfo": {
"endCursor": "Mg==",
"hasNextPage": false
}
}
}
}
The first thing I noticed was that the Id's (Guid's) are changed to some base64 encoded strings.
Weird, but if I would do this:
query {
node(
id: "Q29uc3VtZXJQcm9kdWN0Q2F0ZWdvcnkKZ2EyOTYxNmRlMWMzMjQ4ZTU4YTU2YzRjYjdhMGQ5NmY5"
) {
... on ConsumerProductCategory {
id
name
}
}
}
this perfectly works, result:
{
"data": {
"node": {
"id": "Q29uc3VtZXJQcm9kdWN0Q2F0ZWdvcnkKZ2EyOTYxNmRlMWMzMjQ4ZTU4YTU2YzRjYjdhMGQ5NmY5",
"name": "Category 1"
}
}
}
However, now I want to filter on the Parent.Id,
query GetSubcategories {
consumerProductCategories(
first: 10
after: null
where: { parent: { id: { eq: "Q29uc3VtZXJQcm9kdWN0Q2F0ZWdvcnkKZ2NmZWI0YzNiMGQyNjQyOWI4MGU0MmQ1NGNjYWE1N2Q4"}} }
order: {
name: ASC
}
) {
nodes {
id
name
image {
url
alt
}
parent {
id
}
}
pageInfo {
endCursor
hasNextPage
}
}
}
This gives an error that the fieldtype where I do the "eq" is not correct, makes sense because in the data it's actually a Guid.
The result:
{
"errors": [
{
"message": "The specified value type of field `eq` does not match the field type.",
"locations": [
{
"line": 5,
"column": 31
}
],
"path": [
"consumerProductCategories"
],
"extensions": {
"fieldName": "eq",
"fieldType": "UUID",
"locationType": "UUID",
"specifiedBy": "http://spec.graphql.org/June2018/#sec-Values-of-Correct-Type"
}
}
]
}
I understand why it gives me this error, but I have no clue how to resolve this.
I looked everywhere on Google but have not found a similar question and in the official docs of HotChocolate I cannot really find a solution for this issue.
Can anyone point me in the right direction?
By the way, is it a good practice to use these "autogenerated" base64 strings as Id's, or is there some way to specify that this generation should not happen and actually return the Guid's instead?
Thanks in advance!
Ok, I can answer my own question, basically it isn't supported yet: github
What I've done for now is just add a second Guid in the base class:
This results in:
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.
I have written my first script that utilises GraphQL (Still a learning curve)
Currently i am making 3 calls using GraphQL,
First is a product lookup,
Second is a Price Update,
Third is a Inventory Update.
To reduce the number of calls to the end point i wanted to merge both Price update and Inventory, But i am having 0 luck, i dont know if its bad formatting.
Here is my GraphQL Code (I am using Postman to help ensure the schema is correct before taking it to PHP)
mutation productVariantUpdate($input: ProductVariantInput!) {
productVariantUpdate(input: $input) {
product {
id
}
productVariant {
id
price
}
userErrors {
field
message
}}
second: inventoryActivate($inventoryItemId: ID!, $locationId: ID!, $available: Int) {
inventoryActivate(inventoryItemId: $inventoryItemId, locationId: $locationId, available: $available) {
inventoryLevel {
id
available
}
userErrors {
field
message
}
}
}
}
Variables:
{
"inventoryItemId": "gid://shopify/InventoryItem/XXXXXXXXXXX",
"locationId": "gid://shopify/Location/XXXXXXXXXX",
"available": 11 ,
"input": {
"id": "gid://shopify/ProductVariant/XXXXXXXXX",
"price": 55
}
}
Error i keep getting:
{
"errors": [
{
"message": "Parse error on \"$\" (VAR_SIGN) at [29, 29]",
"locations": [
{
"line": 29,
"column": 29
}
]
}
]
}
The way that you'd go about this is by specifying all your arguments at the root of your mutation, just like you did for ProductVariantInput:
mutation batchProductUpdates(
$input: ProductVariantInput!
$inventoryItemId: ID!
$locationId: ID!
$available: Int
) {
productVariantUpdate(input: $input) {
product { id }
productVariant { id price }
...
}
inventoryActivate(
inventoryItemId: $inventoryItemId
locationId: $locationId
available: $available
) {
inventoryLevel { id available }
...
}
}
Here's an example how this would work if you were to use fetch in JavaScript:
fetch("https://example.com/graphql", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
query: `
mutation MyMutation($firstId: Int, $secondId: Int) {
m1: ToggleLike(id: $firstId) {
id
}
m2: ToggleLike(id: $secondId) {
id
}
}
`,
variables: {
firstId: 1,
secondId: 2
}
})
})
Hope this helps.
I'm using GraphQL with Apollo Server and Client in JS and try to introspect my schema.
Simplified I have a schema like:
input LocationInput {
lat: Float
lon: Float
}
input CreateCityInput {
name: String!
location: LocationInput
}
I query this with an introspection like:
fragment InputTypeRef on __Type {
kind
name
ofType {
kind
name
inputFields {
name
type {
name
kind
ofType {
kind
name
inputFields {
name
type {
name
kind
}
}
}
}
}
}
}
query CreateCityInputFields {
input: __type(name: "CreateCityInput") {
inputFields {
name
description
type {
...InputTypeRef
}
}
}
}
As result I receive:
{
"data": {
"input": {
"inputFields": [
{
"name": "name",
"description": "",
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "String",
"inputFields": null
}
}
},
{
"name": "location",
"description": "",
"type": {
"kind": "INPUT_OBJECT",
"name": "LocationInput",
"ofType": null
}
}
]
}
}
}
As one can see: lat and lon are missing. If I set LocationInput as required (location: LocationInput!) in CreateCityInput I receive the missing lat and lon.
How can I query for lat and lon without haven LocationInput required?
Seems like I had a wrong query for the introspection. Moving the inputField part out of ofType to the upper level fix the issue:
kind
name
inputFields {
name
description
type {
kind
name
ofType {
kind
name
}
}
}
ofType {
kind
name
inputFields {
name
description
type {
kind
name
ofType {
kind
name
}
}
}
}
}
query CreateCityInputFields {
input: __type(name: "CreateCityInput") {
inputFields {
name
description
type {
...InputTypeRef
}
}
}
}
Solved the issue.