Skip to content

Instantly share code, notes, and snippets.

@JingwenTian
Created April 14, 2022 15:25
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save JingwenTian/a614c31107efccbac35b3a369708ba7d to your computer and use it in GitHub Desktop.
Save JingwenTian/a614c31107efccbac35b3a369708ba7d to your computer and use it in GitHub Desktop.
Produce the GraphQL query recommended for a full schema introspection.
query IntrospectionQuery {
__schema {
queryType {
name
}
mutationType {
name
}
subscriptionType {
name
}
types {
...FullType
}
directives {
name
description
locations
args {
...InputValue
}
}
}
}
fragment FullType on __Type {
kind
name
description
fields(includeDeprecated: true) {
name
description
args {
...InputValue
}
type {
...TypeRef
}
isDeprecated
deprecationReason
}
inputFields {
...InputValue
}
interfaces {
...TypeRef
}
enumValues(includeDeprecated: true) {
name
description
isDeprecated
deprecationReason
}
possibleTypes {
...TypeRef
}
}
fragment InputValue on __InputValue {
name
description
type {
...TypeRef
}
defaultValue
}
fragment TypeRef on __Type {
kind
name
ofType {
kind
name
ofType {
kind
name
ofType {
kind
name
ofType {
kind
name
ofType {
kind
name
ofType {
kind
name
ofType {
kind
name
}
}
}
}
}
}
}
}
@JingwenTian
Copy link
Author

<?php 

ini_set ('memory_limit', '6000M');
class Client 
{
    private $url;

    private $query;

    public function __construct(string $url, string $query = '')
    {
        $this->url = $url;
        $this->query = $query;
    }

    public function run()
    {
        $data = $this->curl($this->url);
        $schema = $data['data']['__schema'] ?? [];
        if (!$schema) throw new \Exception("can't get schema");

        $queryTypeName = $schema['queryType']['name'] ?? '';
        if (!$queryTypeName) throw new \Exception("can't get query type name");

        $queryType = $this->_findTypeByName($schema['types'], $queryTypeName);
        if (!$queryType) throw new \Exception("can't get query type");

        $rqQueryName = $this->_parseQueryName();
        
        $queryObject = $this->_findTypeByName($queryType['fields'], $rqQueryName);
        if (!$queryObject) throw new \Exception("can't find query $rqQueryName");

        $responseType = $this->_findResponseType($queryObject['type']);
        if (!$responseType) throw new \Exception("can't find response type for query ${rqQueryName}");
        
        $responseObject = $this->_findTypeByName($schema['types'], $responseType);
        if (!$responseObject) throw new \Exception("can't find response for type ${responseType}");
        // var_dump($rqQueryName, $queryObject, $responseType, $responseObject);
        $q = $this->_buildQuery($schema, $responseObject);
        if (!$q) throw new \Exception("can't build query for ${rqQueryName}");
        echo $q . PHP_EOL;
        $response = $this->curl($this->url, $q);
        if (!$response) throw new \Exception("no response from query ${rqQueryName})");
        return $response;
    }


    private function _findTypeByName($types, $name) {
        if ($types) {
            $found = array_filter($types, function($type) use ($name) {
                return $type['name'] === $name;
            });
            $found = array_values($found);
            return count($found) === 1 ? $found[0] : null;
        }
    }

    private function _parseQueryName() {
        return explode('(', $this->query)[0] ?? null;
    }

    private function _findResponseType($type) {
        if ($type) {
            if (isset($type['name']) && !empty($type['name'])) {
                return $type['name'];
            }
            if (isset($type['ofType']) && !empty($type['ofType'])) {
                return $this->_findResponseType($type['ofType']);
            }
        }
    }

    private function _buildQuery($schema, $type) {
        // return `query { ${this.query} ${this._buildFields(schema, type)}}`;
        return sprintf('query { %s %s }', $this->query, $this->_buildFields($schema, $type));
    }

    private function _findResponseKind($type) {
        if ($type) {
            if (isset($type['kind']) && !empty($type['kind']) && $type['kind'] !== 'NON_NULL') {
                return $type['kind'];
            }
            if (isset($type['ofType']) && !empty($type['ofType'])) {
                return $this->_findResponseKind($type['ofType']);
            }
        }
    }

    private function _buildFields($schema, $type) {
        $q = "";
        if (isset($type['fields']) && !empty($type['fields'])) {
            $q .= "{ ";
            foreach ($type['fields'] as $field) {
                $q .= $field['name'];
                $kind = $this->_findResponseKind($field['type']);
                switch ($kind) {
                    case 'LIST':
                    case 'OBJECT':
                        $lName = $this->_findResponseType($field['type']);
                        $lType = $this->_findTypeByName($schema['types'], $lName);
                        $q .= $this->_buildFields($schema, $lType);
                        break;
                }
            }
            $q .= "} ";
        }
        return $q;
    }

    private function curl(string $endpoint, string $query = '', string $variables = null)
    {   
        $schemaQuery = <<<GQL
        query IntrospectionQuery {
            __schema {
              queryType {
                name
              }
              mutationType {
                name
              }
              subscriptionType {
                name
              }
              types {
                ...FullType
              }
              directives {
                name
                description
                locations
                args {
                  ...InputValue
                }
              }
            }
          }
          fragment FullType on __Type {
            kind
            name
            description
            fields(includeDeprecated: true) {
              name
              description
              args {
                ...InputValue
              }
              type {
                ...TypeRef
              }
              isDeprecated
              deprecationReason
            }
            inputFields {
              ...InputValue
            }
            interfaces {
              ...TypeRef
            }
            enumValues(includeDeprecated: true) {
              name
              description
              isDeprecated
              deprecationReason
            }
            possibleTypes {
              ...TypeRef
            }
          }
          fragment InputValue on __InputValue {
            name
            description
            type {
              ...TypeRef
            }
            defaultValue
          }
          fragment TypeRef on __Type {
            kind
            name
            ofType {
              kind
              name
              ofType {
                kind
                name
                ofType {
                  kind
                  name
                  ofType {
                    kind
                    name
                    ofType {
                      kind
                      name
                      ofType {
                        kind
                        name
                        ofType {
                          kind
                          name
                        }
                      }
                    }
                  }
                }
              }
            }
          }
GQL;
        $query = $query ?: $schemaQuery;
        $json = json_encode(['query' => $query, 'variables' => $variables]);

        $headers = [];
        $headers[] = 'Content-Type: application/json';
        // $headers[] = 'Authorization: Bearer '.$authToken;

        $curl = curl_init();

        curl_setopt_array($curl, [
            CURLOPT_URL => $endpoint,
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_ENCODING => '',
            CURLOPT_MAXREDIRS => 10,
            CURLOPT_TIMEOUT => 0,
            CURLOPT_FOLLOWLOCATION => true,
            CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
            CURLOPT_CUSTOMREQUEST => 'POST',
            CURLOPT_POSTFIELDS => $json,            
            CURLOPT_HTTPHEADER => $headers,
        ]);

        $response = curl_exec($curl);   

        return json_decode($response, true);
    }
}

$client = new Client('https://mirror-api.com/graphql', "topEntries()");
$client->run();

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment