PHPFixing
  • Privacy Policy
  • TOS
  • Ask Question
  • Contact Us
  • Home
  • PHP
  • Programming
  • SQL Injection
  • Web3.0
Showing posts with label graphql. Show all posts
Showing posts with label graphql. Show all posts

Sunday, November 6, 2022

[FIXED] How do I normalize deeply nested JSON results that has a lot of different collections?

 November 06, 2022     dataframe, graphql, json, monday.com, python     No comments   

Issue

What I have is a result from monday.com via a graphql query. The data comes back with a lot of different collections such as a dictionary of a list of dictionaries... eg.

{
  "data": {
    "boards": [
      {
        "name": "board name",
        "groups": [
                    {"title": "group title 1"},
                    {"title": "group title 2"},
                    {"title": "group title 3"},
                    {"title": "group title 4"}
                  ]
       }
     ]
   },
  "account_id": "0000000"
}

and I want to make a pandas dataframe like

     group.board    group.title
0    'board_name'   'group title 1'
1    'board_name'   'group title 2'
2    'board_name'   'group title 3'
3    'board_name'   'group title 4'

I've tried

pd.json_normalize(json_data, 'boards')

But I keep getting KeyError: 'boards'


Solution

Your json dict is invalid (missing a closing brace, so I fixed it). I would do it like this. Here we are going into the dict at json_data["data"]["boards"] because that's where the data is, using "groups" as the records key, and using the field "name" as one of the metadata fields.

import pandas as pd

json_data = {
    "data": {
        "boards": [
            {
                "name": "board name",
                "groups": [
                    { "title": "group title 1" },
                    { "title": "group title 2" },
                    { "title": "group title 3" },
                    { "title": "group title 4" }
                ]
            },
        ]
    },
    "account_id": "0000000"
}

pd.json_normalize(json_data["data"]["boards"], "groups", ["name"])

Output:


    title           name
0   group title 1   board name
1   group title 2   board name
2   group title 3   board name
3   group title 4   board name


Answered By - bherbruck
Answer Checked By - David Goodson (PHPFixing Volunteer)
Read More
  • Share This:  
  •  Facebook
  •  Twitter
  •  Stumble
  •  Digg

Monday, September 5, 2022

[FIXED] Why does it throw an error cannot read property of undefined (reading 'length') when running a GraphQL Subscription with Redis?

 September 05, 2022     graphql, nestjs, node.js, redis, typescript     No comments   

Issue

Overview

  • Working on a NestJS project with GraphQL using a laptop with Window OS
  • Experimenting with GraphQL Subscriptions using graphql-redis-subscription@2.5.0 package
  • Redis is used in a docker container, see the docker-compose.yml below
  • The problem arose when the subscription postAdded is executed in GraphQL Playground. Instead of hanging to listen for events, it had crashed before I performed createPost mutation.

My code (I only include some important details)

posts.resolver.ts

import { Inject, UseGuards } from '@nestjs/common';
import { Args, Context, Mutation, Resolver, Subscription } from '@nestjs/graphql';
import { RedisPubSub } from 'graphql-redis-subscriptions';
import { GraphqlJwtAuthGuard } from '../auth/guards';
import { RequestWithUser } from '../auth/interfaces';
import { PUB_SUB } from '../pubsub/pubsub.module'

const POST_ADDED_EVENT = 'postAdded';

@Resolver(() => Post)
export class PostsResolver {
  constructor(
    private postsService: PostsService,
    @Inject(PUB_SUB) private pubSub: RedisPubSub,
  ) {}

  // my subscription (issue)
  @Subscription(() => Post)
  postAdded() {
    return this.pubSub.asyncIterator(POST_ADDED_EVENT);
  }

  // createPost method
  @Mutation(() => Post)
  @UseGuards(GraphqlJwtAuthGuard)
  async createPost(
    @Args('input') createPostInput: CreatePostInput,
    @Context() context: { req: RequestWithUser },
  ) {
    // just create a new post (assuming it works)
    const newPost = await this.postsService.create(
      createPostInput,
      context.req.user,
    );
    this.pubSub.publish(POST_ADDED_EVENT, { postAdded: newPost });
    return newPost;
  }
}

pubsub.module.ts

import { ConfigService } from '@nestjs/config';
import { RedisPubSub } from 'graphql-redis-subscriptions';
import { Global, Module } from '@nestjs/common';

export const PUB_SUB = 'PUB_SUB';

@Global()
@Module({
  providers: [
    {
      provide: PUB_SUB,
      useFactory: (configService: ConfigService) =>
        new RedisPubSub({
          connection: {
            host: configService.get('REDIS_HOST'),
            port: configService.get('REDIS_PORT'),
          },
        }),
      inject: [ConfigService],
    },
  ],
  exports: [PUB_SUB],
})
export class PubSubModule {}

app.module.ts

import { PubSubModule } from './pubsub/pubsub.module';

@Module({
  imports: [
    GraphQLModule.forRoot<ApolloDriverConfig>({
      driver: ApolloDriver,
      playground: true,
      autoSchemaFile: path.join(process.cwd(), 'src/schema.gql'),
      installSubscriptionHandlers: true,
    }),
    PubSubModule,
    ConfigModule.forRoot({
      isGlobal: true,
      validationSchema: Joi.object({
        REDIS_HOST: Joi.string().required(),
        REDIS_PORT: Joi.number().required()
      }),
    }),
  ],
  providers: [AppService, AppResolver],
})
export class AppModule {}
version: '3'
services:
  redis:
    image: 'redis:alpine'
    ports:
      - '6379:6379'

  redis-commander:
    image: rediscommander/redis-commander:latest
    environment:
      - REDIS_HOSTS=local:redis:6379
    ports:
      - '8081:8081'
    depends_on:
      - redis

All the environment variables have already been defined in .env file.

REDIS_HOST="localhost"
REDIS_PORT=6379

When I run yarn start:dev and execute the subscription in GraphQL Playground

subscription {
  postAdded {
    id
    title
    paragraphs
  }
}

it raises an error like this:

{
  "errors": [
    {
      "message": "Cannot read properties of undefined (reading 'length')",
      "locations": [
        {
          "line": 2,
          "column": 3
        }
      ],
      "path": [
        "postAdded"
      ]
    }
  ]
}

The terminal that monitors NestJS also raises an error like this:

[Nest] 8080  - 07/21/2022, 9:30:24 AM   ERROR [ExceptionsHandler] Cannot read properties of undefined (reading 'length')
TypeError: Cannot read properties of undefined (reading 'length')
    at JavascriptRedisParser.execute (C:\Users\HP\nestjs-project\node_modules\redis-parser\lib\parser.js:530:38)
    at Object.data (C:\Users\HP\nestjs-project\node_modules\ioredis\built\DataHandler.js:25:20)  
    at TransformOperationExecutor.transform (C:\Users\HP\nestjs-project\node_modules\src\TransformOperationExecutor.ts:207:39)
    at TransformOperationExecutor.transform (C:\Users\HP\nestjs-project\node_modules\src\TransformOperationExecutor.ts:327:31)
    at TransformOperationExecutor.transform (C:\Users\HP\nestjs-project\node_modules\src\TransformOperationExecutor.ts:327:31)
    at TransformOperationExecutor.transform (C:\Users\HP\nestjs-project\node_modules\src\TransformOperationExecutor.ts:327:31)
    at TransformOperationExecutor.transform (C:\Users\HP\nestjs-project\node_modules\src\TransformOperationExecutor.ts:327:31)
    at TransformOperationExecutor.transform (C:\Users\HP\nestjs-project\node_modules\src\TransformOperationExecutor.ts:327:31)
    at ClassTransformer.instanceToPlain (C:\Users\HP\nestjs-project\node_modules\src\ClassTransformer.ts:25:21)
    at Object.classToPlain (C:\Users\HP\nestjs-project\node_modules\src\index.ts:23:27)

I have installed all the necessary dependencies like ioredis, graphql-redis-subscriptions and even graphql-subscriptions but the errors still exist. Redis also seems to be running properly.

I have tried reading the error logs but it did not occur in my source code and doing some research on StackOverFlow but none seems to have solved the problem.


Solution

Are you by any chance using a global ClassSerializerInterceptor?? Because I was running into the same problem just today and I solved by removing the interceptor. It happened because the subscription needs to receive an instance of AsyncIterable but the class serializer turns it into a plain object.

Apart from that I would recommend you change the GraphQl config, remove the installSubscriptionHandlers and change the config like this:

GraphQLModule.forRoot<ApolloDriverConfig>({
  driver: ApolloDriver,
  playground: true,
  autoSchemaFile: path.join(process.cwd(), 'src/schema.gql'),
  // remove this option: 
  // installSubscriptionHandlers: true,
  // add the following:
  subscriptions: {
    "graphql-ws": true // or config object
  }
}),

You can read more about it in the nestjs docs

I hope this solves your problem.



Answered By - Fran Messina
Answer Checked By - Marie Seifert (PHPFixing Admin)
Read More
  • Share This:  
  •  Facebook
  •  Twitter
  •  Stumble
  •  Digg

Sunday, July 31, 2022

[FIXED] How to upload files with graphql-java?

 July 31, 2022     file-upload, graphql, graphql-java, graphql-java-tools, java     No comments   

Issue

I can't find out how to upload files if i use graphql-java, can someone show me a demo? I will be appreciated!

reference : https://github.com/graphql-java-kickstart/graphql-java-tools/issues/240

I tried it in springboot by using graphql-java-kickstart graphql-java-tools, but it didn't work

@Component
public class FilesUpload implements GraphQLMutationResolver {

    public Boolean testMultiFilesUpload(List<Part> parts, DataFetchingEnvironment env) {
        // get file parts from DataFetchingEnvironment, the parts parameter is not used
        List<Part> attchmentParts = env.getArgument("files");
        System.out.println(attchmentParts);
        return true;
    }
}

this is my schema

type Mutation {
    testSingleFileUpload(file: Upload): UploadResult
}

I expect this resolver can print attchmentParts,so i can get the file part.


Solution

  1. define a scalar type in our schema

    scalar Upload

    and we should configure GraphQLScalarType for Upload, use this below:

    @Configuration
    public class GraphqlConfig {
    
       @Bean
       public GraphQLScalarType uploadScalarDefine() {
          return ApolloScalars.Upload;
       } 
    }
    
  2. then we would define a mutation in schema and a GraphQLMutationResolver for testMultiFilesUpload

    type Mutation {
      testMultiFilesUpload(files: [Upload!]!): Boolean
    }
    

here is Resolver:

public Boolean testMultiFilesUpload(List<Part> parts, DataFetchingEnvironment env) {
    // get file parts from DataFetchingEnvironment, the parts parameter is not use
    List<Part> attachmentParts = env.getArgument("files");
    int i = 1;
    for (Part part : attachmentParts) {
      String uploadName = "copy" + i;
      try {
        part.write("your path:" + uploadName);
      } catch (IOException e) {
        e.printStackTrace();
      }
      i++;
    }
    return true;   
  }
}
  1. configure a jackson deserializer for javax.servlet.http.Part and register it to ObjectMapper

    public class PartDeserializer extends JsonDeserializer<Part> {
    
      @Override
      public Part deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JsonProcessingException {         
         return null;
      }
    }
    

    why we return null? because the List<Part> parts always null ,In the resolver's method, get the parts argument from the DataFetchingEnvironment;

    environment.getArgument("files")

register it to ObjectMapper:

@Bean
public ObjectMapper objectMapper() {
  ObjectMapper objectMapper = new ObjectMapper();
  objectMapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
  SimpleModule module = new SimpleModule();
  module.addDeserializer(Part.class, new PartDeserializer());
  objectMapper.registerModule(module);
  return objectMapper;
}
  1. To test this, post the following form data (we use Postman) to GraphQL endpoint
operations

{ "query": "mutation($files: [Upload!]!) {testMultiFilesUpload(files:$files)}", "variables": {"files": [null,null] } }

map

{ "file0": ["variables.files.0"] , "file1":["variables.files.1"]}

file0

your file

file1

your file

like this:

remember to select the form-data option enter image description here

through this we can upload multiple files



Answered By - EA Ubisoft
Answer Checked By - Cary Denson (PHPFixing Admin)
Read More
  • Share This:  
  •  Facebook
  •  Twitter
  •  Stumble
  •  Digg

Saturday, July 2, 2022

[FIXED] how to access the blog content through shopify API

 July 02, 2022     api, graphql, shopify, shopify-api, shopify-api-node     No comments   

Issue

I recently created a Shopify Admin account and created a private App to access the Shopify store content from an external app. I called the Shopify blogs API from Postman to fetch the blogs using the API key and password, but I am not able to retrieve the blogs' content.

I called this endpoint for accessing the blogs https://${API_KEY}:${PASSWORD}@${STORE_NAME}.myshopify.com/admin/api/2021-01/blogs.json

This is the response I get in the postman:

{
    "blogs": [
        {
            "id": 75486953641,
            "handle": "news",
            "title": "News",
            "updated_at": "2021-01-18T04:58:06-08:00",
            "commentable": "no",
            "feedburner": null,
            "feedburner_location": null,
            "created_at": "2021-01-13T05:03:38-08:00",
            "template_suffix": null,
            "tags": "about",
            "admin_graphql_api_id": "gid://shopify/OnlineStoreBlog/75486953641"
        }
    ]
}

this actually shows that I have a blog, but it does not show the blog content that I wrote, am I missing something here, How can I actually access the blog content?


Solution

A Blog is a thing. You can have 1, 2 or more. You asked for a blog, you got a blog. A blog is a thing that has zero, one or more articles. I think you want the articles belonging to a blog. So if you want the content of a blog, you should look into the articles endpoint perhaps? Is that what you are missing?



Answered By - David Lazar
Answer Checked By - Mary Flores (PHPFixing Volunteer)
Read More
  • Share This:  
  •  Facebook
  •  Twitter
  •  Stumble
  •  Digg

Friday, July 1, 2022

[FIXED] How to get basic Shopify GraphQL Admin API working with nodeJS?

 July 01, 2022     graphql, node-fetch, node.js, shopify, shopify-api     No comments   

Issue

I had a very frustrating evening yesterday, trying to get the basic Shopify GraphQL Admin API example working with nodeJS. The original Shopify example code is here.

The problem is that my code returns a status 400 - Bad Request.

I have enabled a "private App" on my store and enabled all the APIs with read access. I carefully copied the apiKey, accessToken and store name from Shopify.

Can anyone point out if there is something wrong with my code? Many thanks.

Code:

import fetch from 'node-fetch';

const apiKey = 'xxxx';
const accessToken = 'yyyy';
const store = 'zzzz';
const hostName = store + '.myshopify.com';
const apiVersion = '2021-01';
const apiLocation = '/admin/api/';

const rootUrl = 'https://' + apiKey + ':' + accessToken + '@' + hostName + apiLocation + apiVersion + '/';

const shopGraphQl = 'https://' + hostName + apiLocation + apiVersion + '/graphql.json';
//const shopGraphQl2 = rootUrl + 'graphql.json';
//const urlTest = rootUrl + 'orders.json';

const url = shopGraphQl;

const body = {
    query: `{
        shop {
            name
          }
      }`
};

fetch   (
    url,
    {
        method: "POST",
        headers: {
            "Content-Type": "application/json",
            "X-Shopify-Access-Token" : accessToken
        },
        body: JSON.stringify({
            body
        })
    }
)
.then(res => {
    console.log('status = ' + res.status + ' , ' + res.statusText);
})
.then(json => {
    console.log("data returned:\n", json);
})
.catch(err => console.error(err));; 

Solution

It looks like you are sending body incorrectly. The way you are sending your body results in {body: { query: { shop { name } } } }

Instead of:

body: JSON.stringify({body})

change it to:

body: JSON.stringify(body)

Receiving data from fetch

You have noticed that your console.log statement, console.log("data returned:\n", json); is returning undefined. The reason for this is that you need to extract the json from the response (res.json()).

return fetch (
    url,
    {
        method: "POST",
        headers: {
            "Content-Type": "application/json",
            "X-Shopify-Access-Token" : accessToken
        },
        body: JSON.stringify(body)
    }
)
.then(res => {
    console.log(`status = ${res.status}, ${res.statusText}`);
    return res.json();
})
.then(json => {
    console.log("data returned:\n", json);
})
.catch(err => console.error(err));; 

Here is a decent reference on using fetch



Answered By - Brettski
Answer Checked By - Mildred Charles (PHPFixing Admin)
Read More
  • Share This:  
  •  Facebook
  •  Twitter
  •  Stumble
  •  Digg

[FIXED] How to use Shopify Graphql ProductVariantsBulkInput

 July 01, 2022     graphql, shopify, shopify-api     No comments   

Issue

I am trying to bulk update the price on multiple product variants at once. I'm just copying the "interactive" example from Shopify's page

All I did was copy and paste the code and put in my own id's. And of course it does not work.

It looks like this:

mutation productVariantsBulkUpdate($variants: [ProductVariantsBulkInput!]!, $productId: ID!) {
  productVariantsBulkUpdate(variants: $variants, productId: $productId) {
    product {
      cursor
    }
    productVariants {
      cursor
    }
    userErrors {
      code
      field
      message
    }
  }
}

With Variables like this:

{
  "variants": [
    {
      id:  "gid://shopify/ProductVariant/39369514385591",
      price: "50.00"
    }
  ],
  "productId": "gid://shopify/Product/6591908577463"
}

I'm getting this error: Variables are invalid JSON: Unexpected token i in JSON at position 30.


Solution

It's OK for me. (with some quick tweaks)

I tweaked the request a little since the cursor is not present in the product/variant object, don't know why Shopify has not updated the example in their docs.

mutation productVariantsBulkUpdate($variants: [ProductVariantsBulkInput!]!, $productId: ID!) {
  productVariantsBulkUpdate(variants: $variants, productId: $productId) {
    product {
      id
    }
    productVariants {
      id
      price
    }
    userErrors {
      code
      field
      message
    }
  }
}

So try to fix the query and remove the cursor object and check if you are using the proper end-point since the bulk operation is available in the unstable version only if I'm not mistaken.

See the image below showing that the response is OK for me.

enter image description here



Answered By - drip
Answer Checked By - Terry (PHPFixing Volunteer)
Read More
  • Share This:  
  •  Facebook
  •  Twitter
  •  Stumble
  •  Digg

[FIXED] How to process a Shopify GraphQL Bulk Request and download the response?

 July 01, 2022     bulk, fetch, graphql, reactjs, shopify     No comments   

Issue

I'm very new to javascript and graphQL in general. I've done my best to do my own research but now it's getting a bit more complicated and I'm a little lost.

I'm building an app for Shopify (React/Node) where it basically queries all my store's unfulfilled orders, and retrieves all the items in these orders. I'm only looking for one type of item, and it's coffee -- we roast it, so we need to know the total weights for all our coffee products. It looks a little something like this with a plain graphQL query:

1

I exceed the limits for a plain query though, so I need to make a bulk query request. I've managed poll a bulk query fine and retrieve the data in the console, but this is something I have to actively call within the app - I need to data to be there ready for when my app loads, and then I need the data to be there and manageable so I can map it in the same way I did my plain query... and for the life of me I can't figure it out. Any help would be appreciated, I'm not expecting anyone to do it entirely for me, but I really barely just grasped how to do a .post request. I've only really been pursuing this a month or so now.

I'm trying to get these queries here:

import gql from 'graphql-tag';

const BULK_OP = gql`
mutation {
  bulkOperationRunQuery(
    query:"""
    {
      orders(reverse:true, query: "fulfillment_status:unshipped AND status:open") {
        edges {
          node {
            id
            name
            lineItems {
              edges {
                node {
                  quantity
                  name
                  product {
                    productType
                    metafield(namespace: "global", key:"SO_num") {
                      value
                    }
                  }
                  variant {
                    weight
                  }
                }
              }
            }
          }
        }
      }
      collections(first: 1, query: "id:260752932934") {
        edges {
          node {
            id
            products(first: 3, sortKey: BEST_SELLING) {
              edges {
                node {
                  title
                  metafield(namespace: "global", key: "SO_num") {
                    value
                  }
                }
              }
            }
          }
        }
      }
    }
"""
 ) {
   bulkOperation {
     id
     status
   }
   userErrors {
     field
     message
   }
 }
}
`;

const CHECK_BULK = gql`
  query {
    currentBulkOperation {
      id
      status
      errorCode
      createdAt
      completedAt
      objectCount
      fileSize
      url
      partialDataUrl
    }
  }
`;


export { BULK_OP, CHECK_BULK }

I would post more of my code, but at this point I feel like most of it will be need to be redone on the basis of what I have to actually do to get this data before the app loads.

Any help would be appreciated, even a point in the right direction.

Thanks!


Solution

Bulk operations are designed for long-running processing.

It's just a chain of data-flow formed using separate requests.

Example for Apollo client (see docs for option details):

  • useMutation to 'send' bulk - returns bulk id;

  • useQuery to check processing state ... you can use polling option to automatic re-querying (use network-only fetch policy) ... and skip to stop after finished - returns status, url, partialDataUrl;

  • when a bulk data ready, just fetch (use axios/fetch/whatever) to download prepared data [probably json] file from url (provided with query response) - the bulk response;

  • use your [json decoded] data in component;

Hint: run mutation (on demand, from [some onClick] event handler) in separate component to not mix send/receive operations ... you can store/save bulk id [and processing state] in localStorage to be available on refresh



Answered By - xadm
Answer Checked By - Timothy Miller (PHPFixing Admin)
Read More
  • Share This:  
  •  Facebook
  •  Twitter
  •  Stumble
  •  Digg

[FIXED] Why is my graphql query return all product?

 July 01, 2022     graphiql, graphql, shopify, shopify-storefront-api     No comments   

Issue

I'm testing graphql query on Shopify GraphiQL Explorer for filtering products on Storefront API.

My query is like this:

query ProductType {
  collectionByHandle(handle: "pants") {
    handle
    products(first: 10, filters: {
      productType: "pants"
    }) {
      edges {
        node {
          handle
          productType
        }
      }
    }
  }
}

And got the result like this:

{
  "data": {
    "collectionByHandle": {
      "handle": "pants",
      "products": {
        "edges": [
          {
            "node": {
              "handle": "my-test-product-pants",
              "productType": "pants"
            }
          },
          {
            "node": {
              "handle": "pleated-straight-pants-mens-fashion-elastic-waist-casual-pants-men-streetwear-loose-ice-silk-trousers-mens-wide-leg-pants-s-2xl",
              "productType": ""
            }
          },
          ...

Basically, the result has all the products that I have for that collection. Can anybody help me with this? This is literally the code I got from shopify website


Solution

As we can see in the tutorial filters is an array

{
  "product_filters": [
    {
      "productType": "shoes"
    },
    {
      "productVendor": "bestshop"
    },
    {
      "variantOption": {
        "name": "color",
        "value": "blue"
      }
    }
  ]
}

So, try this instead


query ProductType {
  collectionByHandle(handle: "pants") {
    handle
    products(first:10, filters: [{ productType: "pants" ]}) {
            ...
    }
  }
}


Answered By - Joyescat
Answer Checked By - Timothy Miller (PHPFixing Admin)
Read More
  • Share This:  
  •  Facebook
  •  Twitter
  •  Stumble
  •  Digg

[FIXED] How to pass variables to metafieldsSet mutation in Shopify Api Node js Grahql client?

 July 01, 2022     graphql, node.js, shopify     No comments   

Issue

I was trying to use the metafieldsSet mutation to update metafields in Shopify with the following code:

const client = new Shopify.Clients.Graphql(
        process.env.SHOP,
        process.env.PASSWORD
    )
    try {
        const metafields = await client.query({
            data: `mutation metafieldsSet($metafields: [MetafieldsSetInput!]!) {
                metafieldsSet(metafields: $metafields) {
                    userErrors {
                        field
                        message
                    }
                    metafields {
                        key
                        value
                    }
                }
            }           
            `,
            query: {
                metafields: [
                    {
                        key: 'cb_inventory',
                        namespace: 'my_fields',
                        ownerId: 'gid://shopify/ProductVariant/40576138313890',
                        type: 'number_integer',
                        value: '25',
                    },
                ],
            },
        })
        console.log(metafields)
        res.status(200).json({ values: metafields })
    } catch (error) {
        console.log(error)
        res.status(500).json(error)
    }

However, the above mutation returns the following error:

Expected value to not be null
Variable $metafields of type [MetafieldsSetInput!]! was provided invalid value

I assume the variable metafields failed to pass into the mutation because when I run the exact same mutation in the Shopify Admin API GraphiQL explorer, there was no error Shopify Admin API GraphiQL explorer mutation result

I have also looked into the github repo of @shopify/shopify-api. In my understanding, variables are added to the query object.

What am I missing?

Thanks,

Howard

Environment: Next js 11.1.2,

Dependencies: @shopify/shopify-api 1.4.1


Solution

Turns out the syntax is incorrect. The variables should be placed inside the variables object, while the mutation statement should be placed inside the query object.

The following code works now:

const metafields = await client.query({
   data: {
      query: `mutation metafieldsSet($metafields: [MetafieldsSetInput!]!) {
         metafieldsSet(metafields: $metafields) {
            userErrors {
               field
               message
            }
            metafields {
                key
                value
            }
         }
    }`,
       variables: {
           metafields: [
              {
                 key: 'cb_inventory',
                 namespace: 'my_fields',
                 ownerId: 'gid://shopify/ProductVariant/40576138313890',
                  type: 'number_integer',
                value: '25',
             },
           ],
       },
   },
})

ref: https://github.com/Shopify/shopify-node-api/blob/main/src/clients/graphql/test/graphql_client.test.ts

To use the intended API version, you need to first initialise the context with the following code:

Shopify.Context.initialize({
    API_KEY,
    API_SECRET_KEY,
    SCOPES: ['read_products', 'write_products'],
    HOST_NAME: HOST,
    API_VERSION: '2021-10',
})


Answered By - Yiu Howard
Answer Checked By - Marie Seifert (PHPFixing Admin)
Read More
  • Share This:  
  •  Facebook
  •  Twitter
  •  Stumble
  •  Digg

[FIXED] How to pass variables to metafieldsSet mutation in Shopify Api Node js Grahql client?

 July 01, 2022     graphql, node.js, shopify     No comments   

Issue

I was trying to use the metafieldsSet mutation to update metafields in Shopify with the following code:

const client = new Shopify.Clients.Graphql(
        process.env.SHOP,
        process.env.PASSWORD
    )
    try {
        const metafields = await client.query({
            data: `mutation metafieldsSet($metafields: [MetafieldsSetInput!]!) {
                metafieldsSet(metafields: $metafields) {
                    userErrors {
                        field
                        message
                    }
                    metafields {
                        key
                        value
                    }
                }
            }           
            `,
            query: {
                metafields: [
                    {
                        key: 'cb_inventory',
                        namespace: 'my_fields',
                        ownerId: 'gid://shopify/ProductVariant/40576138313890',
                        type: 'number_integer',
                        value: '25',
                    },
                ],
            },
        })
        console.log(metafields)
        res.status(200).json({ values: metafields })
    } catch (error) {
        console.log(error)
        res.status(500).json(error)
    }

However, the above mutation returns the following error:

Expected value to not be null
Variable $metafields of type [MetafieldsSetInput!]! was provided invalid value

I assume the variable metafields failed to pass into the mutation because when I run the exact same mutation in the Shopify Admin API GraphiQL explorer, there was no error Shopify Admin API GraphiQL explorer mutation result

I have also looked into the github repo of @shopify/shopify-api. In my understanding, variables are added to the query object.

What am I missing?

Thanks,

Howard

Environment: Next js 11.1.2,

Dependencies: @shopify/shopify-api 1.4.1


Solution

Turns out the syntax is incorrect. The variables should be placed inside the variables object, while the mutation statement should be placed inside the query object.

The following code works now:

const metafields = await client.query({
   data: {
      query: `mutation metafieldsSet($metafields: [MetafieldsSetInput!]!) {
         metafieldsSet(metafields: $metafields) {
            userErrors {
               field
               message
            }
            metafields {
                key
                value
            }
         }
    }`,
       variables: {
           metafields: [
              {
                 key: 'cb_inventory',
                 namespace: 'my_fields',
                 ownerId: 'gid://shopify/ProductVariant/40576138313890',
                  type: 'number_integer',
                value: '25',
             },
           ],
       },
   },
})

ref: https://github.com/Shopify/shopify-node-api/blob/main/src/clients/graphql/test/graphql_client.test.ts

To use the intended API version, you need to first initialise the context with the following code:

Shopify.Context.initialize({
    API_KEY,
    API_SECRET_KEY,
    SCOPES: ['read_products', 'write_products'],
    HOST_NAME: HOST,
    API_VERSION: '2021-10',
})


Answered By - Yiu Howard
Answer Checked By - Clifford M. (PHPFixing Volunteer)
Read More
  • Share This:  
  •  Facebook
  •  Twitter
  •  Stumble
  •  Digg

Thursday, June 30, 2022

[FIXED] How to do a basic GraphQL query to Shopify with Ruby

 June 30, 2022     graphql, ruby, shopify, shopify-app, sinatra     No comments   

Issue

I'm trying to do a basic GraphQL query to a Shopify store with Sinatra. Could someone help me figure out what I'm doing wrong? I looked at their API to do this:

require 'shopify_api'
require 'sinatra'

class App < Sinatra::Base
  get '/' do
    shop  = 'xxxx.myshopify.com'
    token = 'shpat_xxxxxxxxxxxxxxxxxxxxxx'
    session = ShopifyAPI::Session.new(domain: shop, token: token, api_version: "2021-04")
    ShopifyAPI::Base.activate_session(session)
    
    ShopifyAPI::GraphQL.initialize_clients
    client = ShopifyAPI::GraphQL.client

    SHOP_NAME_QUERY = client.parse <<-'GRAPHQL'
      {
        shop {
          name
        }
      }
    GRAPHQL

    result = client.query(SHOP_NAME_QUERY)
    result.data.shop.name
  end
end

Which gives this error but I don't want to use Rake or Rails. Is it possible to do a GraphQL query to Shopify with Ruby?

ShopifyAPI::GraphQL::InvalidClient at /
Client for API version 2021-04 does not exist because no schema file exists at `shopify_graphql_schemas/2021-04.json`. To dump the schema file, use the `rake shopify_api:graphql:dump` task

Solution

As the error states, you need to first dump the schema (see this link: https://github.com/Shopify/shopify_api/blob/master/docs/graphql.md#dump-the-schema).

Then you create a shopify_graphql_schemas directory in the same root as your ruby script, and put the generated JSON there.

Like stated in the comments, this requires a Rake task, so you need to be using Rails. If your project doesn't use Rails, you need to do a quick workaround.

You create a temporary barebones Rails project, then generate the dump using that project (you can delete the project when you're done with this). It's a bit hacky, but it's the only thing I can see that would work.



Answered By - yaken
Answer Checked By - Timothy Miller (PHPFixing Admin)
Read More
  • Share This:  
  •  Facebook
  •  Twitter
  •  Stumble
  •  Digg

[FIXED] How to create a cart on the shopify storefront API

 June 30, 2022     graphql, shopify     No comments   

Issue

I am building a headless shopify site using remix. Fro reading the documentation on storefront in order for you to do a a checkout you must create a cart and send a post request to the api.

I am sending this graphql request which I found off the official docs to create a cart https://shopify.dev/api/examples/cart. Where the merchadise id is the id I get back from when i call get products

mutation {
  cartCreate(
    input: {
      lines: [
        {
          quantity: 2
          merchandiseId: "gid://shopify/ProductVariant/6964601651340"
        }
      ]
      attributes: { key: "cart_attribute", value: "This is a cart attribute" }
    }
  ) {
    cart {
      id
      createdAt
      updatedAt
      lines(first: 10) {
        edges {
          node {
            id
            merchandise {
              ... on ProductVariant {
                id
              }
            }
          }
        }
      }


    }
  }
}

Here is a response of getProducts

{
  "node": {
    "id": "gid:\/\/shopify\/Product\/6964601651340",
    "handle": "vans-authentic-butterfly-true-white-black",
    "title": "VANS | AUTHENTIC (BUTTERFLY) TRUE | WHITE \/ BLACK",
    "description": "The forefather of the Vans family, the Vans Authentic was introduced in 1966 and nearly 4 decades later is still going strong, its popularity extending from the original fans - skaters and surfers to all sorts. The Vans Authentic is constructed from canvas and Vans' signature waffle outsole construction.",
    "variants": {
      "edges": [
        {
          "node": {
            "price": "109.95"
          }
        }
      ]
    }
  }
}

If I change "gid://shopify/ProductVariant/6964601651340" to gid://shopify/Product/6964601651340 - i get invalid id. But if I make the request with productVariant I get the response

{
  "data": {
    "cartCreate": {
      "cart": null
    }
  }
}

what am I doing wrong - how do i create a cart?


Solution

You need to use the id of the Variant. Not the product.

Each product (Vans) can have several variants, typically sizes and colors (39 black,40 black, 39 red etc)

You can retrive the Variant id with a query like this

{
  products(first:100){
    edges{
      node{
        variants(first:100){
          edges{
            node{
              id
            }
          }
        }
      }
    }
  }
}


Answered By - Fabio Filippi
Answer Checked By - Pedro (PHPFixing Volunteer)
Read More
  • Share This:  
  •  Facebook
  •  Twitter
  •  Stumble
  •  Digg

Wednesday, May 11, 2022

[FIXED] how to use graphiql when route are secured?

 May 11, 2022     api-platform.com, graphiql, graphql, symfony     No comments   

Issue

i have an application based on api-platform with secured route using JWT (and the LexikJWTBundle). With the Swagger interface it's easy to call secured route providing a valid bearer. But with GraphiQL i don't see anything about security so when a call a secured route it fails.

Any idea ? or shall we prevent graphiql usage in dev ?

Thanks


Solution

If you're using the standalone GraphiQL app, there's an "Edit HTTP Headers" button at the top-right corner. Click that, click "+ Add Header", and enter a Header name "Authorization" and Header value "Bearer eyJh..." where the last part is your access token.

For GraphiQL embedded in a Web site, it's often configured so that it targets the same site, and whatever authentication you need to reach the GraphQL endpoint is the same authentication you need to reach the GraphiQL app. There's not specifically a path to add custom headers here, but the embedding application server might have a way to provide them.



Answered By - David Maze
Answer Checked By - Candace Johnson (PHPFixing Volunteer)
Read More
  • Share This:  
  •  Facebook
  •  Twitter
  •  Stumble
  •  Digg

Monday, April 18, 2022

[FIXED] How can I convert the object array to GraphQL format in Javascript?

 April 18, 2022     graphql, laravel, reactjs     No comments   

Issue

I'm working with React, and I send this information:

const imageServicesClean = JSON.stringify(imageServices);
const query = `
    mutation {
        companyUpdate(
                idCompany:${idCompany},
                name:${nameClean},
                imageServices:${imageServicesClean})
            {
            idCompany
            name
            imageServices {
                idImageService
                name
                url
                key
            }
        }
    }`;

And the imageServicesClean is sent in this way, but return error:

[{
    "idImageService": 1,
    "name": "Service1",
    "url": "",
    "key": "asdasdas"
}, {
    "idImageService": 2,
    "name": "Service2",
    "url": "sdsads",
    "key": "sddsfsds_"
}]

Because my GraphQL server (Laravel) just allows the variable without quotes, in this way:

[{
    idImageService: 1,
    name: "Service1",
    url: "",
    key: "sdofunc4938urcnnwikk"
}, {
    idImageService: 2,
    name: "Service2",
    url: "sdsads",
    key: "sddsfsdssss8347yuirh"
}]

So the function JSON.stringify don't work for build format in GraphQL. How can I convert the object array to GraphQL format in Javascript?


Solution

Finally this was my solution:

const imageServicesClean = JSON.stringify(imageServices);
const graphQLImageServices = imageServicesClean.replace(/"([^(")"]+)":/g,"$1:");

But finally I'm working with this library, it does everything for me: https://github.com/atulmy/gql-query-builder



Answered By - Albert Tjornejoj
Answer Checked By - Pedro (PHPFixing Volunteer)
Read More
  • Share This:  
  •  Facebook
  •  Twitter
  •  Stumble
  •  Digg

Friday, March 4, 2022

[FIXED] How can I Unit Test GraphQL file Upload with API Platform?

 March 04, 2022     api-platform.com, graphql, phpunit, symfony     No comments   

Issue

In addition to my other tests against my GraphQL API Platform backend, I am attempting to test file uploads. I'm not quite sure whether the ApiPlatform\Core\Bridge\Symfony\Bundle\Test\Client class has the ability to facilitate this test, or if Symfony\Component\HttpFoundation\File\UploadedFile should be used to provide the test file as it is for a REST operation.

Here is roughly where I am at in terms of putting together this test. (With some irrelevant parts removed for simplification)

 public function testCreateMediaObject(): void {
    $file = new UploadedFile('fixtures/files/logo.png', 'logo.png');
    $client = self::createClient();

    $gql = <<<GQL
    mutation UploadMediaObject(\$file: Upload!) {
      uploadMediaObject(input: {file: \$file}) {
        mediaObject {
          id
          contentUrl
        }
      }
    }
    GQL;

    $response = $client->request('POST', '/api/graphql', [
      'headers' => ['Content-Type' => 'application/json'],
      'json' => [
        'query' => $gql,
        'variables' => ["file" => null],
        'map' => ['0' => ['variables.file']],
        '0' => $file,
      ]
    ]);
    dd($response);

  }

The response I get seems to indicate that the file isn't being included as expected. Something like...

Variable "$file" of non-null type "Upload!" must not be null.

Or.. if I try to attach the file by simply assigning it directly in the variables property...

    $response = $client->request('POST', '/api/graphql', [
      'headers' => ['Content-Type' => 'application/json'],
      'json' => [
        'query' => $gql,
        'variables' => ["file" => $file],
      ]
    ]);

then...

Variable "$file" got invalid value []; Expected type Upload; Could not get uploaded file, be sure to conform to GraphQL multipart request specification. Instead got: []

In my most recent attempt, I changed things up quite a bit after sifting through the graphql code...

    $formFields = [
      'operations' => '{ "query": "mutation ($file: Upload!) { uploadMediaObject(input: {file: $file}) { mediaObject { id contentUrl } } }", "variables": { "file": null } }',
      'map' => "{'0': ['variables.file']}",
      '0' => DataPart::fromPath(__DIR__.'/../../fixtures/files/logo.png'),
    ];
    $formData = new FormDataPart($formFields);
    $response = $client->request('POST', '/api/graphql', [
      'headers' => $formData->getPreparedHeaders()->toArray(),
      'body' => $formData->bodyToString(),
    ]);

The problem with this last attempt is that the server isn't seeing any body parameters. So I receiving the exception

'GraphQL multipart request does not respect the specification.'

Which is found in /api-platform/core/src/GraphQl/Action/EntrypointAction.php within the parseMultipartRequest method.


Solution

After a few hours of debugging I found this solution:

$formData = new FormDataPart();
$file = new UploadedFile('src/DataFixtures/files/test.txt', 'test.txt');
    
$response = $this->$client->request('POST', 'api/graphql', [
    'headers' => $formData->getPreparedHeaders()->toArray(),
    'extra' => [
        'parameters' => [
        'operations' => '{ "query": "mutation UploadMediaObject($file: Upload!) { uploadMediaObject(input: {file: $file}) { mediaObject { id contentUrl } } }", "variables": { "file": null } }',
            'map' => '{ "0": ["variables.file"] }',
            '0' => @$file,
        ],
        'files' => [
            $file,
        ],
    ],
]);

Refrence:

  • https://github.com/jaydenseric/graphql-multipart-request-spec
  • https://api-platform.com/docs/core/graphql/


Answered By - Dario
Read More
  • Share This:  
  •  Facebook
  •  Twitter
  •  Stumble
  •  Digg

Sunday, January 30, 2022

[FIXED] GraphQL mutation in Svelte to submit a comment in Wordpress

 January 30, 2022     graphql, svelte, wordpress, wp-graphql     No comments   

Issue

How to submit a comment in svelte using Graphql?
My svelte code is like this


<script>
    let commenterMessage = '';
    export let status = false;

    export async function submitComment() {
        $: commenterMessage;
        const graphcms = new GraphQLClient(import.meta.env.VITE_GRAPHCMS_URL, {
            headers: {}
        });

        const query = gql`
            mutation CREATE_COMMENT {
                createComment(
                    input: {
                        commentOn: 115
                        content: ${commenterMessage}
                        author: "Sarah Jason"
                    }
                ) {
                    success
                    comment {
                        id
                        content
                        author {
                            node {
                                name
                            }
                        }
                    }
                }
            }
        `;

        const { createComment } = await graphcms.request(query);
        return {
            props: {
                status: createComment.success
            }
        };
    }
</script>

And this is the form that I use binding values from user:

<form id="comment_form" method="post">
    <div class="group-val ct-gr">
        <textarea name="message" placeholder="Comment" bind:value={commenterMessage} />
    </div>
    <a
        href="#"
        class="btn fill"
        on:click|preventDefault={submitComment}
        data-text="Add Comment">
        Add Comment
    </a>
</form>

I have and error for ${commenterMessage} in gql query.
This is the error that I see in console:

Uncaught (in promise) Error: Syntax Error: Cannot parse the unexpected character ".".: {"response":{"errors":[{"message":"Syntax Error: Cannot parse the unexpected character \".\".","extensions":{"category":"graphql"},"locations":[{"line":6,"column":21}]}],"extensions":{"debug":[{"type":"DUPLICATE_FIELD","message":"You cannot register duplicate fields on the same Type. The field 'theme' already exists on the type 'RootQuery'. Make sure to give the field a unique name.","field_name":"theme","type_name":"RootQuery","stack":["E:\\wamp64\\www\\simplecv\\cms\\wp-content\\plugins\\wp-graphql\\src\\Registry\\TypeRegistry.php:1025","E:\\wamp64\\www\\simplecv\\cms\\wp-includes\\class-wp-hook.php:305","E:\\wamp64\\www\\simplecv\\cms\\wp-includes\\plugin.php:189","E:\\wamp64\\www\\simplecv\\cms\\wp-content\\plugins\\wp-graphql\\src\\Type\\WPObjectType.php:226","E:\\wamp64\\www\\simplecv\\cms\\wp-content\\plugins\\wp-graphql\\src\\Type\\WPObjectType.php:126","E:\\wamp64\\www\\simplecv\\cms\\wp-content\\plugins\\wp-graphql\\vendor\\webonyx\\graphql-php\\src\\Type\\Definition\\FieldDefinition.php:96","E:\\wamp64\\www\\simplecv\\cms\\wp-content\\plugins\\wp-graphql\\vendor\\webonyx\\graphql-php\\src\\Type\\Definition\\TypeWithFields.php:26","E:\\wamp64\\www\\simplecv\\cms\\wp-content\\plugins\\wp-graphql\\vendor\\webonyx\\graphql-php\\src\\Type\\Definition\\TypeWithFields.php:61","E:\\wamp64\\www\\simplecv\\cms\\wp-content\\plugins\\wp-graphql\\src\\Utils\\InstrumentSchema.php:32","E:\\wamp64\\www\\simplecv\\cms\\wp-includes\\class-wp-hook.php:303","E:\\wamp64\\www\\simplecv\\cms\\wp-includes\\plugin.php:189","E:\\wamp64\\www\\simplecv\\cms\\wp-content\\plugins\\wp-graphql\\src\\Registry\\TypeRegistry.php:826","E:\\wamp64\\www\\simplecv\\cms\\wp-content\\plugins\\wp-graphql\\src\\Registry\\SchemaRegistry.php:40","E:\\wamp64\\www\\simplecv\\cms\\wp-content\\plugins\\wp-graphql\\src\\WPGraphQL.php:601","E:\\wamp64\\www\\simplecv\\cms\\wp-content\\plugins\\wp-graphql\\src\\Request.php:146","E:\\wamp64\\www\\simplecv\\cms\\wp-content\\plugins\\wp-graphql\\src\\Router.php:464","E:\\wamp64\\www\\simplecv\\cms\\wp-content\\plugins\\wp-graphql\\src\\Router.php:270","E:\\wamp64\\www\\simplecv\\cms\\wp-includes\\class-wp-hook.php:303","E:\\wamp64\\www\\simplecv\\cms\\wp-includes\\class-wp-hook.php:327","E:\\wamp64\\www\\simplecv\\cms\\wp-includes\\plugin.php:518","E:\\wamp64\\www\\simplecv\\cms\\wp-includes\\class-wp.php:388","E:\\wamp64\\www\\simplecv\\cms\\wp-includes\\class-wp.php:750","E:\\wamp64\\www\\simplecv\\cms\\wp-includes\\functions.php:1291","E:\\wamp64\\www\\simplecv\\cms\\wp-blog-header.php:16","E:\\wamp64\\www\\simplecv\\cms\\index.php:17"]},{"type":"DUPLICATE_FIELD","message":"You cannot register duplicate fields on the same Type. The field 'categories' already exists on the type 'RootQuery'. Make sure to give the field a unique name.","field_name":"categories","type_name":"RootQuery","stack":["E:\\wamp64\\www\\simplecv\\cms\\wp-content\\plugins\\wp-graphql\\src\\Registry\\TypeRegistry.php:1025","E:\\wamp64\\www\\simplecv\\cms\\wp-includes\\class-wp-hook.php:305","E:\\wamp64\\www\\simplecv\\cms\\wp-includes\\plugin.php:189","E:\\wamp64\\www\\simplecv\\cms\\wp-content\\plugins\\wp-graphql\\src\\Type\\WPObjectType.php:226","E:\\wamp64\\www\\simplecv\\cms\\wp-content\\plugins\\wp-graphql\\src\\Type\\WPObjectType.php:126","E:\\wamp64\\www\\simplecv\\cms\\wp-content\\plugins\\wp-graphql\\vendor\\webonyx\\graphql-php\\src\\Type\\Definition\\FieldDefinition.php:96","E:\\wamp64\\www\\simplecv\\cms\\wp-content\\plugins\\wp-graphql\\vendor\\webonyx\\graphql-php\\src\\Type\\Definition\\TypeWithFields.php:26","E:\\wamp64\\www\\simplecv\\cms\\wp-content\\plugins\\wp-graphql\\vendor\\webonyx\\graphql-php\\src\\Type\\Definition\\TypeWithFields.php:61","E:\\wamp64\\www\\simplecv\\cms\\wp-content\\plugins\\wp-graphql\\src\\Utils\\InstrumentSchema.php:32","E:\\wamp64\\www\\simplecv\\cms\\wp-includes\\class-wp-hook.php:303","E:\\wamp64\\www\\simplecv\\cms\\wp-includes\\plugin.php:189","E:\\wamp64\\www\\simplecv\\cms\\wp-content\\plugins\\wp-graphql\\src\\Registry\\TypeRegistry.php:826","E:\\wamp64\\www\\simplecv\\cms\\wp-content\\plugins\\wp-graphql\\src\\Registry\\SchemaRegistry.php:40","E:\\wamp64\\www\\simplecv\\cms\\wp-content\\plugins\\wp-graphql\\src\\WPGraphQL.php:601","E:\\wamp64\\www\\simplecv\\cms\\wp-content\\plugins\\wp-graphql\\src\\Request.php:146","E:\\wamp64\\www\\simplecv\\cms\\wp-content\\plugins\\wp-graphql\\src\\Router.php:464","E:\\wamp64\\www\\simplecv\\cms\\wp-content\\plugins\\wp-graphql\\src\\Router.php:270","E:\\wamp64\\www\\simplecv\\cms\\wp-includes\\class-wp-hook.php:303","E:\\wamp64\\www\\simplecv\\cms\\wp-includes\\class-wp-hook.php:327","E:\\wamp64\\www\\simplecv\\cms\\wp-includes\\plugin.php:518","E:\\wamp64\\www\\simplecv\\cms\\wp-includes\\class-wp.php:388","E:\\wamp64\\www\\simplecv\\cms\\wp-includes\\class-wp.php:750","E:\\wamp64\\www\\simplecv\\cms\\wp-includes\\functions.php:1291","E:\\wamp64\\www\\simplecv\\cms\\wp-blog-header.php:16","E:\\wamp64\\www\\simplecv\\cms\\index.php:17"]},{"type":"DUPLICATE_FIELD","message":"You cannot register duplicate fields on the same Type. The field 'themes' already exists on the type 'RootQuery'. Make sure to give the field a unique name.","field_name":"themes","type_name":"RootQuery","stack":["E:\\wamp64\\www\\simplecv\\cms\\wp-content\\plugins\\wp-graphql\\src\\Registry\\TypeRegistry.php:1025","E:\\wamp64\\www\\simplecv\\cms\\wp-includes\\class-wp-hook.php:305","E:\\wamp64\\www\\simplecv\\cms\\wp-includes\\plugin.php:189","E:\\wamp64\\www\\simplecv\\cms\\wp-content\\plugins\\wp-graphql\\src\\Type\\WPObjectType.php:226","E:\\wamp64\\www\\simplecv\\cms\\wp-content\\plugins\\wp-graphql\\src\\Type\\WPObjectType.php:126","E:\\wamp64\\www\\simplecv\\cms\\wp-content\\plugins\\wp-graphql\\vendor\\webonyx\\graphql-php\\src\\Type\\Definition\\FieldDefinition.php:96","E:\\wamp64\\www\\simplecv\\cms\\wp-content\\plugins\\wp-graphql\\vendor\\webonyx\\graphql-php\\src\\Type\\Definition\\TypeWithFields.php:26","E:\\wamp64\\www\\simplecv\\cms\\wp-content\\plugins\\wp-graphql\\vendor\\webonyx\\graphql-php\\src\\Type\\Definition\\TypeWithFields.php:61","E:\\wamp64\\www\\simplecv\\cms\\wp-content\\plugins\\wp-graphql\\src\\Utils\\InstrumentSchema.php:32","E:\\wamp64\\www\\simplecv\\cms\\wp-includes\\class-wp-hook.php:303","E:\\wamp64\\www\\simplecv\\cms\\wp-includes\\plugin.php:189","E:\\wamp64\\www\\simplecv\\cms\\wp-content\\plugins\\wp-graphql\\src\\Registry\\TypeRegistry.php:826","E:\\wamp64\\www\\simplecv\\cms\\wp-content\\plugins\\wp-graphql\\src\\Registry\\SchemaRegistry.php:40","E:\\wamp64\\www\\simplecv\\cms\\wp-content\\plugins\\wp-graphql\\src\\WPGraphQL.php:601","E:\\wamp64\\www\\simplecv\\cms\\wp-content\\plugins\\wp-graphql\\src\\Request.php:146","E:\\wamp64\\www\\simplecv\\cms\\wp-content\\plugins\\wp-graphql\\src\\Router.php:464","E:\\wamp64\\www\\simplecv\\cms\\wp-content\\plugins\\wp-graphql\\src\\Router.php:270","E:\\wamp64\\www\\simplecv\\cms\\wp-includes\\class-wp-hook.php:303","E:\\wamp64\\www\\simplecv\\cms\\wp-includes\\class-wp-hook.php:327","E:\\wamp64\\www\\simplecv\\cms\\wp-includes\\plugin.php:518","E:\\wamp64\\www\\simplecv\\cms\\wp-includes\\class-wp.php:388","E:\\wamp64\\www\\simplecv\\cms\\wp-includes\\class-wp.php:750","E:\\wamp64\\www\\simplecv\\cms\\wp-includes\\functions.php:1291","E:\\wamp64\\www\\simplecv\\cms\\wp-blog-header.php:16","E:\\wamp64\\www\\simplecv\\cms\\index.php:17"]}]},"status":200,"headers":{"map":{"content-length":"7395","content-type":"application/json; charset=UTF-8"}}},"request":{"query":"\n\t\t\tmutation CREATE_COMMENT {\n\t\t\t\tcreateComment(\n\t\t\t\t\tinput: {\n\t\t\t\t\t\tcommentOn: 115\n\t\t\t\t\t\tcontent: Hello. This is the message from the form.\n\t\t\t\t\t\tauthor: \"ali akizade\"\n\t\t\t\t\t}\n\t\t\t\t) {\n\t\t\t\t\tsuccess\n\t\t\t\t\tcomment {\n\t\t\t\t\t\tid\n\t\t\t\t\t\tcontent\n\t\t\t\t\t\tauthor {\n\t\t\t\t\t\t\tnode {\n\t\t\t\t\t\t\t\tname\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t"}}

How can I use the value that user types in form input?


Solution

The problem was content: ${commenterMessage} that I should have written like this:

content: "${commenterMessage}"

as the content is a String we should surround it with double quotes.



Answered By - Sarah Diba
Read More
  • Share This:  
  •  Facebook
  •  Twitter
  •  Stumble
  •  Digg

[FIXED] Using ACF with GraphQL and Gatsby, flexible content is not returning the order of it's child blocks

 January 30, 2022     advanced-custom-fields, gatsby, graphql, javascript, wordpress     No comments   

Issue

I have a Gatsby site that is hooked up to use Wordpress as a headless CMS. I am using ACF to create dynamic layouts in Wordpress then rendering the modules I add to page to the frontend(Gatsby). I am using the flexible content feature in ACF to dynamically create layouts in the cms that can be easily rendered in React. Normally I do this with next.js and Wordpress's REST API which returns the "flexible content" data as an array of objects I can easily iterate over and render the appropriate content with.

My issue is that with using GraphQL and Gatsby, my "flexible content" data I get it back from the API is just a bunch of objects within the parent page's object body. This means there is no relationship to what order the modules/children were placed in the flexible content block since they aren't part of an array. My workaround, for the time being, is to add an extra field to the child modules that specify its' order on the page based on a number value.....but this is gross and would be a horrible experience for a client so I am not happy with that.

Is there a way to either return a value for each item in an AFC flexible content block that directly relates to its index/position. Or is there a way to return the children items in an array when using the Gatsby plugin for returning data.

Currently, my query looks a little like this:

allWordpressPage {
  edges {
    node {
      id
      slug
      status
      template
      title
      childWordPressAcfFeaturedShowcaseCarousel {
        id
        title
        other_stuff
      }
      childWordPressAcfIphonesAndContent {
        id
        title
        other_stuff
      }
      childWordPressAcfContentAndImageSlideShow {
        id
        title
        other_stuff
      }
    }
  }
}

That will return something like:

{
  id: 123,
  slug: 'hello',
  template: 'default',
  title: 'Help Me',
  childWordPressAcfFeaturedShowcaseCarousel: {
    id: 1232
    title: 'Bonjour'
    other_stuff: {....}
  },
  childWordPressAcfIphonesAndContent: {
    id: 1232
    title: 'Bonjour'
    other_stuff: {....}
  },
  childWordPressAcfContentAndImageSlideShow: {
    id: 1232
    title: 'Bonjour'
    other_stuff: {....}
  }
}

But instead, I would want something like:

{
  id: 123,
  slug: 'hello',
  template: 'default',
  title: 'Help Me',
  childWordPressAcfFeaturedShowcaseCarousel: {
    id: 1232,
    index: 1,
    title: 'Bonjour',
    other_stuff: {....}
  },
  childWordPressAcfIphonesAndContent: {
    id: 1232,
    index: 2,
    title: 'Bonjour',
    other_stuff: {....}
  },
  childWordPressAcfContentAndImageSlideShow: {
    id: 1232,
    index: 3,
    title: 'Bonjour'
    other_stuff: {....}
  }
}

Or even better:

{
  id: 123,
  slug: 'hello',
  template: 'default',
  title: 'Help Me',
  module: [
    childWordPressAcfFeaturedShowcaseCarousel: {
      id: 1232,
      title: 'Bonjour',
      other_stuff: {....}
    },
    childWordPressAcfIphonesAndContent: {
      id: 1232,
      title: 'Bonjour',
      other_stuff: {....}
    },
    childWordPressAcfContentAndImageSlideShow: {
      id: 1232,
      title: 'Bonjour'
      other_stuff: {....}
    }
  ]
}

Solution

So I just figured it out. Turns out when querying data for a flexible content block there are a few things to remember. To access flexible content fields, instead of using their field name, you need to use [field_name]_[post_type] (if you have field named page_builder in your WordPress pages you would need to use page_builder_page). Once you do that everything will return in an array in the exact order they are in the ACF block.

So now my query looks like this:

allWordpressPage {
  edges {
    node {
      id
      slug
      status
      template
      title
      acf {
        modules_page {
          ... on WordPressAcf_featured_showcase_carousel {
            __typename
            id
            title
            other_stuff
          }
          ... on WordPressAcf_iphones_and_content {
            __typename
            id
            title
            other_stuff
          }
          ... on WordPressAcf_content_and_image_slide_show {
            __typename
            id
            title
            other_stuff
          }
        }
      }
    }
  }
}


Answered By - Brady Edgar
Read More
  • Share This:  
  •  Facebook
  •  Twitter
  •  Stumble
  •  Digg

Saturday, January 22, 2022

[FIXED] Lighthouse graphql custom resolver

 January 22, 2022     graphql, laravel, laravel-lighthouse, php     No comments   

Issue

Quite new to GraphQL and lighthouse library, don't be too harsh.

Since I can't use any models because my data source is an API. I'm trying to create a custom resolver that will pass data to a service who will do everything necessary to retrieve data from the API.

And it constantly returns me this error: "Field \"address\" of type \"[Address!]\" must have a sub selection.", I believe it's because of the fact I don't use models(just a wild guess)

So far my schema looks like this:

type Query {
    address(address: String!): [Address!] @field(resolver: "Address@resolve")
}

type Address {
    fullAddress: String!
    lowestId: Int!
}

And the mentioned resolver:

    public function resolve($rootValue, array $args, GraphQLContext $context, ResolveInfo $resolveInfo): array
    {
        return array_map(
            function ($address): array {
                return [
                    'fullAddress' => $address->getFullAddress()
                ];
            },
            $this->service->getAddress($args['address'])
        );
    }

Thank you in advance!


Solution

The error is not even specific to Lighthouse, any GraphQL server will produce a similar error for what you are trying to do. I assume you are trying a query like this:

{
  address(address: "foo")
}

Consider the graph in GraphQL: your server describes available data types and relations between them, forming a graph. Each type could have fields that lead to another type, and that type to another type, and so on. Those references can even form cycles. At some points, the graph may end: types such as scalar values mark the leaves of the graph.

Now, how does a server know which part of the graph you want to see and it should resolve? Through a query: a subselection of a part of that graph. That naturally limits how deep the server must go, it can do the minimal amount of work to return the parts of the graph you queried for.

One rule of queries is that you must always end up at leaf nodes. This is where the error message comes into play: the server sees that Address is not a leaf type and thus asks you to specify how deep you want to traverse the graph. A working query could be:

{
  address(address: "foo") {
    fullAddress
  }
}


Answered By - spawnia
Read More
  • Share This:  
  •  Facebook
  •  Twitter
  •  Stumble
  •  Digg

Friday, January 21, 2022

[FIXED] API Platform GraphQL Security in Relationships

 January 21, 2022     api-platform.com, entity-relationship, graphql, security, symfony     No comments   

Issue

My problem is the security implementation of the entities when consumed through GraphQL, the queries are perfect, they return the necessary data, but the problem occurs when a query is generated and the many-to-one relationship is limited to having a role "ROLE_ADMIN", the query returns the data even when the user has the role "IS_AUTHENTICATED_ANONYMOUSLY"

How to add the security layer so that data from protected relationships cannot be obtained?

The product query must be seen without any role.

Additional Information

GraphQL Query User Admin

query {
    userAdmin(id: "api/user_admins/1") {
        id
        name
    }
}

GraphQL Query User Admin Result OK

{
  "errors": [
    {
      "message": "Sorry, but you don't have access",
      "extensions": {
        "category": "graphql"
      }
  ]
}

GraphQL Query Product

query {
    products {
        edges {
            node {
                name
                price
                user {
                    name
                }
            }
        }
    }
}

GraphQL Query Product Result FAILED

{
  "data": {
    "products": {
      "edges": [
        {
          "node": {
            "name": "GERLACH-HAAG",
            "price": "175",
            "user": {
              "name": "Sidney Deane" /** this information should not be seen **/
            }
          }
        }
      ]
    }
  }
}

Entity Product Configuration

<?php

/**
 * @ApiResource(
 *     graphql={
 *          "item_query",
 *          "collection_query",
 *          "delete"={ "security" = "is_granted('ROLE_ADMIN')" },
 *          "create"={ "security" = "is_granted('ROLE_ADMIN')" },
 *          "update"={ "security" = "is_granted('ROLE_ADMIN')" }
 *     }
 * )
 * @ORM\Table(name="TBL_PRODUCTS")
 * @ORM\Entity(repositoryClass=ProductRepository::class)
 * @ORM\HasLifecycleCallbacks()
 */
class Product
{
    /**
     * @ORM\Id()
     * @ORM\GeneratedValue()
     * @ORM\Column(type="bigint", name="ID")
     */
    private $id;

    /**
     * @ORM\Column(type="string", length=180, name="NAME")
     */
    private $name;

    /**
     * @ORM\ManyToOne(targetEntity="App\Entity\UserAdmin")
     * @ORM\JoinColumn(name="USER", referencedColumnName="ID")
     */
    private $user;

Entity User Admin Configuration

<?php

/**
 * @ApiResource(
 *     graphql={
 *          "item_query"={ "security" = "is_granted('ROLE_ADMIN')" },
 *          "collection_query"={ "security" = "is_granted('ROLE_ADMIN')" },
 *          "delete"={ "security" = "is_granted('ROLE_ADMIN')" },
 *          "create"={ "security" = "is_granted('ROLE_ADMIN')" },
 *          "update"={ "security" = "is_granted('ROLE_ADMIN')" }
 *     }
 * )
 * @ORM\Table(name="TBL_USERS_ADMIN")
 * @ORM\Entity(repositoryClass=UserAdminRepository::class)
 * @ORM\HasLifecycleCallbacks()
 */
class UserAdmin implements UserInterface
{
    /**
     * @ORM\Id()
     * @ORM\GeneratedValue()
     * @ORM\Column(type="bigint", name="ID")
     */
    private $id;

    /**
     * @ORM\Column(type="string", length=180, name="USERNAME")
     */
    private $username;

    /**
     * @ORM\Column(type="string", length=180, name="PASSWORD")
     */
    private $password;

    /**
     * @ORM\Column(type="string", length=180, name="NAME")
     */
    private $name;

Please help !!!!


Solution

The security attribute allows to define roles or permissions necessary to execute a given query. However, it doesn't define which relation available trough this query the user can access. To do so, you can use dynamic serialization groups.

Basically, mark the properties requiring a special role to be returned in the response with a specific serialization group, such as @Groups("admin"), then create a dynamic serialization context builder to add the special group if the connected user has this special role:

<?php

namespace App\Serializer;

use ApiPlatform\Core\GraphQl\Serializer\SerializerContextBuilderInterface;
use App\Entity\Book;
use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface;

final class BookContextBuilder implements SerializerContextBuilderInterface
{
    private $decorated;
    private $authorizationChecker;

    public function __construct(SerializerContextBuilderInterface $decorated, AuthorizationCheckerInterface $authorizationChecker)
    {
        $this->decorated = $decorated;
        $this->authorizationChecker = $authorizationChecker;
    }

    public function create(?string $resourceClass, string $operationName, array $resolverContext, bool $normalization): array
    {
        $context = $this->decorated->create($resourceClass, $operationName, $resolverContext, $normalization);
        $resourceClass = $context['resource_class'] ?? null;

        if ($resourceClass === Book::class && isset($context['groups']) && $this->authorizationChecker->isGranted('ROLE_ADMIN') && false === $normalization) {
            $context['groups'][] = 'admin';
        }

        return $context;
    }
}

In the master version (API Platform 2.6), the security attribute is now also available for properties. Unfortunately, this feature is currently available only in the REST subsystem, but we will be very pleased to merge a Pull Request adding support for this attribute to the GraphQL subsystem.



Answered By - Kévin Dunglas
Read More
  • Share This:  
  •  Facebook
  •  Twitter
  •  Stumble
  •  Digg
Older Posts Home
View mobile version

Total Pageviews

Featured Post

Why Learn PHP Programming

Why Learn PHP Programming A widely-used open source scripting language PHP is one of the most popular programming languages in the world. It...

Subscribe To

Posts
Atom
Posts
All Comments
Atom
All Comments

Copyright © PHPFixing