Example #1
0
def query_write(opath, type, qname, content, mode):
    """
    This function is used in order to generate the Queries Mutations & Subscriptions templates.
    Path and file name will be generated as follow:

    :param opath:
        query path template it needs two %s to work

    :param type:
        query, mutation, subscription

    :param qname:
        query, mutation, subscription names

    :param content:
        file content

    :param mode:
        w, a and so on

    :return:
        none
    """
    with open(opath % (type, '%s.query' % qname), mode) as ofile:
        ofile.write(content)
Example #2
0
def generate(argument,
             fpath="endpoints_%.tsv",
             green_print=lambda s: print(s)):
    """
    Generate Cycles Founds file, or stream to stdout

    :param argument: introspection query result
    :param fpath: output result format string, the first %s will be used as query type (mutation, susbscription, ...)
    :return: None
    """
    s = simplify_introspection(argument)

    rev = {}
    for t, v in s.items():
        for k in v.keys():
            rev[k] = t

    for qtype, qvalues in s['schema'].items():
        rec = recurse_fields(s, rev, qvalues['type'], non_required_levels=2)
        path = fpath % qtype
        green_print("Writing %s TSV" % qtype)
        with open(path, "w") as tsv_file:
            tsv_file.write(
                "Operation Name\tArgs Name\tArgs Types\tReturns Name\tReturns Types\n"
            )
            for qname, qval in rec.items():
                print("Writing %s %s in TSV" % (qname, qtype))
                tsv_file.write("%s\t%s\t%s\t%s\t%s\n" %
                               (qname, joinset(extract_args(qval, set())),
                                joinset(extract_args_types(qval, set())),
                                joinset(extract_returns(qval, set())),
                                joinset(extract_returns_types(qval, set()))))
        green_print("DONE")
Example #3
0
def generate(argument,
             qpath="%s/%s",
             detect=True,
             green_print=lambda s: print(s)):
    """
    Generate query templates

    :param argument: introspection query result
    :param qpath:
        directory template where to output the queries, first parameter is type of query and second is query name

    :param detect:
        retrieve placeholders according to arg type

    :param green_print:
        implements print in green

    :return: None
    """
    s = simplify_introspection(argument)

    rev = {
        "String": 'scalar',
        "Int": 'scalar',
        "Float": 'scalar',
        "Boolean": 'scalar',
        "ID": 'scalar',
    }
    for t, v in s.items():
        for k in v.keys():
            rev[k] = t

    for qtype, qvalues in s['schema'].items():
        green_print("Writing %s Templates" % qtype)
        if detect:
            rec = recurse_fields(s,
                                 rev,
                                 qvalues['type'],
                                 non_required_levels=2,
                                 params_replace=preplace)
        else:
            rec = recurse_fields(s,
                                 rev,
                                 qvalues['type'],
                                 non_required_levels=2)
        for qname, qval in rec.items():
            print("Writing %s %s" % (qname, qtype))
            with open(qpath % (qtype, '%s.query' % qname), 'w') as ofile:
                body = "%s {\n\t%s%s\n}" % (qtype, qname,
                                            dict_to_qbody(qval, prefix='\t'))
                if detect:
                    body = body.replace('!', '')
                query = {"query": body}
                ofile.write(json.dumps(query))

    green_print("DONE")
Example #4
0
def generate(argument, fpath="introspection.json"):
    """
    Generate Schema JSON

    :param argument: introspection query output
    :param fpath: file output
    :return: None
    """
    with open(fpath, "w") as schema_file:
        schema_file.write(json.dumps(argument, indent=4, sort_keys=True))
Example #5
0
def generate(argument, fpath="introspection.json", green_print=lambda s: print(s)):
    """
    Generate Schema JSON

    :param argument: introspection query output
    :param fpath: file output
    :return: None
    """
    green_print("Writing Introspection Schema JSON")
    with open(fpath, "w") as schema_file:
        schema_file.write(json.dumps(argument, indent=4, sort_keys=True))
    green_print("DONE")
Example #6
0
def generate(argument,
             fpath,
             custom=False,
             target="empty",
             green_print=lambda s: print(s)):
    """
    Generate HTML Documentation

    :param argument: introspection query result
    :param fpath: output result
    :param custom: enable or disable custom types, disabled by default
    :param target: who is the owner of that graphql endpoint
    :return: None
    """
    green_print("Writing HTML Documentation")
    with open(fpath, 'w') as output_file:
        result = argument.copy()
        # Write HTML header for the documentation
        # --------------------
        output_file.write("<html><head><title>GraphQL Schema</title>")
        # write CSS
        output_file.write(stl)
        # write target URL
        output_file.write(
            "</head><body><h2>GraphQL Schema</h2><h3><a href='{0}'>{0}</a></h3>"
            .format(target))
        # write legend box
        output_file.write(
            "<div class='box'><h4>Legend</h4><ul><li class='query'>Queries</li><li class='mutation'>Mutations</li><"
            "li class='subscription'>Subscriptions</li><li class='argument'>Arguments</li>"
            "<li class='type'>Types: String, Float, not_null!, [list]</li><li class='deprecated'>Deprecated</li>"
            "<li class='field'>Fields</li></ul></div>")
        # --------------------
        output_file.write("<p>Available Operations Types:</p>")
        try:
            # Print available operation types, usually: Query, Mutations & Subscriptions
            # This part also holds custom names (schema[Type]['name'] != 'RootQuery', 'RootMutation', 'Subscriptions')
            # --------------------
            if result['data']['__schema']['mutationType'] is not None:
                output_file.write("<ul><li class='mutation'>{0}</li>".format(
                    result['data']['__schema']['mutationType']['name']))
                Mutation = result['data']['__schema']['mutationType']['name']
            else:
                # Needed since not all GraphQL endpoints use/have all the three types (Query, Mutations & Subscriptions)
                Mutation = None
            if result['data']['__schema']['queryType'] is not None:
                output_file.write("<li class='query'>{0}</li>".format(
                    result['data']['__schema']['queryType']['name']))
                Query = result['data']['__schema']['queryType']['name']
            else:
                Query = None
            if result['data']['__schema']['subscriptionType'] is not None:
                output_file.write(
                    "<li class='subscription'>{0}</li></ul>".format(
                        result['data']['__schema']['subscriptionType']
                        ['name']))
                Subscription = result['data']['__schema']['subscriptionType'][
                    'name']
            else:
                Subscription = None
            # --------------------
            i = 0
            ##########################################################################################
            # Parsing JSON response/file structure as follows
            # data
            #   __schema
            #       directives
            #       mutationType
            #       queryType
            #       subscriptionType
            #       types (kind, name, description)
            #              name (RootQuery, RootMutation, Subscriptions, [custom] OBJECT)
            #              fields
            #                     name (query names)
            #                     args
            #                            name (args names)
            #                            type
            #                                   name (args types)
            ##########################################################################################
            # Start looping trough types
            if result['data']['__schema']['types'] is not None:
                rt = result['data']['__schema']['types']
                # holds the number of custom objects
                xxx = 0
                for types in rt:
                    j = 0
                    # Data -> Schema -> Types (kind, name, description)
                    # filtering out primitive types
                    # TODO: exclude interfaces & union types
                    primitives = [
                        'Int', 'Float', 'String', 'Boolean', 'ID',
                        '__TypeKind', '__Type', '__Schema', '__Field',
                        '__InputValue', '__EnumValue', '__Directive',
                        '__DirectiveLocation'
                    ]
                    advanced_kind = ['INPUT_OBJECT']
                    # This super if is BOOLEAN able to switch between ENABLED custom types parameter (-c)
                    # It will selectively routine trough values needed to print
                    if ((custom is False and
                         ((rt[i]['kind'] is not None
                           and rt[i]['name'] is not None) and
                          (rt[i]['name'] not in primitives) and
                          (rt[i]['kind'] not in advanced_kind) and
                          ((rt[i]['kind'] == "OBJECT") and
                           ((rt[i]['name'] == Query) or
                            (rt[i]['name'] == Mutation) or
                            (rt[i]['name'] == Subscription)))))
                            or (custom is not False and
                                ((rt[i]['kind'] is not None
                                  and rt[i]['name'] is not None) and
                                 (rt[i]['name'] not in primitives) and
                                 (rt[i]['kind'] not in advanced_kind)))):
                        output_file.write("<li>{0}</li>".format(rt[i]['kind']))
                        # Print our types RootQuery, RootMutation, Subscriptions
                        # --------------------
                        if rt[i]['name'] == Mutation:
                            output_file.write(
                                "<li class='mutation'>{0}</li>".format(
                                    rt[i]['name']))
                        elif rt[i]['name'] == Query:
                            output_file.write(
                                "<li class='query'>{0}</li>".format(
                                    rt[i]['name']))
                        elif rt[i]['name'] == Subscription:
                            output_file.write(
                                "<li class='subscription'>{0}</li>".format(
                                    rt[i]['name']))
                        # Handles custom objects (FIELDS)
                        elif rt[i]['kind'] == "OBJECT" and rt[i][
                                'name'] is not None:
                            output_file.write(
                                "<span class='type'>{0}</span><br>".format(
                                    rt[i]['name']))
                            xxx += 1
                        if rt[i]['description'] is not None:
                            output_file.write(
                                "<span class='description'>{0}</span><br>".
                                format(rt[i]['description']))
                        # --------------------
                    k = 0
                    # Retrieving general docs regarding primitives (filtered out from documentation, not needed)
                    # Data -> Schema -> Types -> enumValues (name, description, isDeprecated, deprecationReason)
                    # My super BOOLEAN IF, used to switch between ENABLED custom types parameter (-c)
                    if ((custom is False and
                         (rt[i]['enumValues'] is not None and
                          (rt[i]['name'] not in primitives) and
                          (rt[i]['kind'] not in advanced_kind) and
                          ((rt[i]['kind'] == "OBJECT") and
                           ((rt[i]['name'] == Query) or
                            (rt[i]['name'] == Mutation) or
                            (rt[i]['name'] == Subscription)))))
                            or (custom is not False and
                                ((rt[i]['enumValues'] is not None) and
                                 (rt[i]['name'] not in primitives) and
                                 (rt[i]['kind'] not in advanced_kind)))):
                        for enumValues in rt[i]['enumValues']:
                            # Name
                            if rt[i]['enumValues'][k]['name'] is not None:
                                output_file.write(
                                    "<span>{0}</span><br>".format(
                                        rt[i]['enumValues'][k]['name']))
                            # Description
                            if rt[i]['enumValues'][k][
                                    'description'] is not None:
                                output_file.write(
                                    "<span class='description'>{0}</span><br>".
                                    format(
                                        rt[i]['enumValues'][k]['description']))
                            # Is Deprecated?
                            if rt[i]['enumValues'][k][
                                    'isDeprecated'] is not False and rt[i][
                                        'enumValues'][k][
                                            'isDeprecated'] is not None:
                                output_file.write(
                                    "<span class='deprecated'>Is Deprecated</span><br>"
                                )
                            # Deprecation Reason
                            if rt[i]['enumValues'][k][
                                    'deprecationReason'] is not None:
                                output_file.write(
                                    "<span>Reason: {0}</span><br>".format(
                                        rt[i]['enumValues'][k]
                                        ['deprecationReason']))
                            k = k + 1
                    # Retrieving queries, mutations and subscriptions information
                    # Data -> Schema -> Types -> Fields (name, isDeprecated, deprecationReason, description)
                    # My super BOOLEAN IF, used to switch between ENABLED custom types parameter (-c)
                    if ((custom is False and
                         ((rt[i]['fields'] is not None) and
                          (rt[i]['name'] not in primitives) and
                          (rt[i]['kind'] not in advanced_kind) and
                          ((rt[i]['kind'] == "OBJECT") and
                           ((rt[i]['name'] == Query) or
                            (rt[i]['name'] == Mutation) or
                            (rt[i]['name'] == Subscription)))))
                            or (custom is not False and
                                ((rt[i]['fields'] is not None) and
                                 (rt[i]['name'] not in primitives) and
                                 (rt[i]['kind'] not in advanced_kind)))):
                        # Printing out queries, mutations, subscriptions and custom object names
                        # --------------------
                        # number of fields per obj
                        for fields in result['data']['__schema']['types'][i][
                                'fields']:
                            if rt[i]['fields'][j]['name'] is not None:
                                # Query
                                if rt[i]['name'] == Query:
                                    output_file.write(
                                        "<li class='query'>{0}</li>".format(
                                            rt[i]['fields'][j]['name']))
                                # Mutation
                                elif rt[i]['name'] == Mutation:
                                    output_file.write(
                                        "<li class='mutation'>{0}</li>".format(
                                            rt[i]['fields'][j]['name']))
                                # Subscription
                                elif rt[i]['name'] == Subscription:
                                    output_file.write(
                                        "<li class='subscription'>{0}</li>".
                                        format(rt[i]['fields'][j]['name']))
                                # It handle custom objects
                                elif rt[i]['kind'] == "OBJECT":
                                    output_file.write(
                                        "<span class='field'>{0}</span>&nbsp;&nbsp;"
                                        .format(rt[i]['fields'][j]['name']))
                                # Seems that i do not need the following two lines
                                # else:
                                #    output_file.write("<li>{0}</li>".format(rt[i]['fields'][j]['name']))
                            # --------------------
                            # Printing info regarding the queries, mutations and subscriptions above
                            # --------------------
                            # Deprecated
                            if rt[i]['fields'][j][
                                    'isDeprecated'] is not False and rt[i][
                                        'fields'][j][
                                            'isDeprecated'] is not None:
                                output_file.write(
                                    "<span class='deprecated'>Is Deprecated</span><br>"
                                )
                            # Deprecated Reason
                            if rt[i]['fields'][j][
                                    'deprecationReason'] is not None:
                                output_file.write(
                                    "<span>Reason: {0}</span><br>".format(
                                        rt[i]['fields'][j]
                                        ['deprecationReason']))
                            # Description
                            if rt[i]['fields'][j][
                                    'description'] is not None and rt[i][
                                        'fields'][j]['description'] != '':
                                output_file.write(
                                    "<span class='description'>{0}</span><br>".
                                    format(rt[i]['fields'][j]['description']))
                            # Name (fields type)
                            if rt[i]['fields'][j]['type'] is not None:
                                if rt[i]['fields'][j]['type'][
                                        'name'] is not None:
                                    output_file.write(
                                        "<span class='type'>{0}</span><br>".
                                        format(rt[i]['fields'][j]['type']
                                               ['name']))
                            # oFType
                            if rt[i]['fields'][j]['type']['ofType'] is not None and \
                                    rt[i]['fields'][j]['type']['ofType']['name'] is not None:
                                # LIST
                                if rt[i]['fields'][j]['type'][
                                        'kind'] is not None and rt[i]['fields'][
                                            j]['type']['kind'] == "LIST":
                                    output_file.write(
                                        "<span class='type'>[{0}]</span><br>".
                                        format(rt[i]['fields'][j]['type']
                                               ['ofType']['name']))
                                # NOT NULL
                                elif rt[i]['fields'][j]['type'][
                                        'kind'] is not None and rt[i]['fields'][
                                            j]['type']['kind'] == "NON_NULL":
                                    output_file.write(
                                        "<span class='type'>!{0}</span><br>".
                                        format(rt[i]['fields'][j]['type']
                                               ['ofType']['name']))
                                # CUSTOM TYPE
                                else:
                                    output_file.write(
                                        "<span class='type'>{0}</span><br>".
                                        format(rt[i]['fields'][j]['type']
                                               ['ofType']['name']))
                            # --------------------
                            x = 0
                            # Prepare a list of ARGS names for queries, mutations and subscriptions
                            # --------------------
                            # My super BOOLEAN IF, used to switch between ENABLED custom types parameter (-c)
                            if ((custom is False and
                                 ((rt[i]['fields'][j]['args'] is not None) and
                                  (rt[i]['name'] not in primitives) and
                                  (rt[i]['kind'] not in advanced_kind) and
                                  ((rt[i]['kind'] == "OBJECT") and
                                   ((rt[i]['name'] == Query) or
                                    (rt[i]['name'] == Mutation) or
                                    (rt[i]['name'] == Subscription))))) or
                                (custom is not False and
                                 ((rt[i]['fields'][j]['args'] is not None) and
                                  (rt[i]['name'] not in primitives) and
                                  (rt[i]['kind'] not in advanced_kind)))):
                                # Printing out queries, mutations and subscriptions ARGS name
                                # Data -> Schema -> Types -> Fields -> Args (defaultValue, name, description)
                                # --------------------
                                for args in rt[i]['fields'][j]['args']:
                                    # Default value if present
                                    if rt[i]['fields'][j]['args'][x][
                                            'defaultValue'] is not None:
                                        output_file.write(
                                            "<span>{0}</span><br>".format(
                                                rt[i]['fields'][j]['args'][x]
                                                ['defaultValue']))
                                    # ARGS name
                                    if rt[i]['fields'][j]['args'][x][
                                            'name'] is not None:
                                        output_file.write(
                                            "<span class='argument'>{0}</span>&nbsp;&nbsp;"
                                            .format(rt[i]['fields'][j]['args']
                                                    [x]['name']))
                                    # ARGS description
                                    if rt[i]['fields'][j]['args'][x]['description'] is not None and \
                                            rt[i]['fields'][j]['args'][x]['description'] != '':
                                        output_file.write(
                                            "<span class='description'>{0}</span><br>"
                                            .format(rt[i]['fields'][j]['args']
                                                    [x]['description']))
                                    # --------------------
                                    # Printing out ARGS types
                                    # Data -> Schema -> Types -> Fields -> Args -> Type (name, ofType, kind)
                                    # TODO half a bug: there are custom objects that have multiple types as the following example
                                    # in this case ![LIST], at the moment this specific case is handled casting the returning value of
                                    # rt[i]['fields'][j]['args'][x]['type']['ofType']['name'] to STRING
                                    # in order to prevent errors (None type concatenated to a string)
                                    # we are missing the custom object but at least the script does not falls apart
                                    """
                                         "description":null,
                                         "isDeprecated":false,
                                         "args":[  ],
                                         "deprecationReason":null,
                                         "type":{  
                                            "kind":"NON_NULL",
                                            "name":null,
                                            "ofType":{  
                                               "kind":"LIST",
                                               "name":null,
                                               "ofType":{  
                                                  "kind":"NON_NULL",
                                                  "name":null,
                                                  "ofType":{  
                                                     "kind":"SCALAR",
                                                     "name":"String",
                                                     "ofType":null
                                                  }
                                               }
                                            }
                                         },
                                         "name":"roles"
                                    """
                                    # --------------------
                                    if rt[i]['fields'][j]['args'][x][
                                            'type'] is not None and (
                                                rt[i]['name'] not in primitives
                                            ) and (rt[i]['kind']
                                                   not in advanced_kind):
                                        # LIST
                                        if rt[i]['fields'][j]['args'][x][
                                                'type']['kind'] == "LIST":
                                            output_file.write(
                                                "<span class='type'>[{0}]</span><br>"
                                                .format(rt[i]['fields'][j]
                                                        ['args'][x]['type']
                                                        ['ofType']['name']))
                                        # NOT NULL
                                        elif rt[i]['fields'][j]['args'][x][
                                                'type']['kind'] == "NON_NULL":
                                            output_file.write(
                                                "<span class='type'>{0}!</span><br>"
                                                .format(rt[i]['fields'][j]
                                                        ['args'][x]['type']
                                                        ['ofType']['name']))
                                        # Holds simple types like float, string, int etc.
                                        else:
                                            if rt[i]['fields'][j]['args'][x][
                                                    'type'][
                                                        'name'] is not None:
                                                output_file.write(
                                                    "<span class='type'>{0}</span><br>"
                                                    .format(rt[i]['fields'][j]
                                                            ['args'][x]['type']
                                                            ['name']))
                                    x += 1
                            j += 1
                    i += 1
        # For None key exceptions use: except KeyError:
        except Exception:
            raise
        # Close documentation
        output_file.write("</body></html>")
        output_file.close()
    green_print("DONE")