All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.github.aoudiamoncef.apollo.plugin.util.SchemaDownloader.kt Maven / Gradle / Ivy

package com.github.aoudiamoncef.apollo.plugin.util

import com.apollographql.apollo.api.internal.json.JsonWriter
import com.apollographql.apollo.compiler.fromJson
import com.apollographql.apollo.compiler.parser.introspection.IntrospectionSchema
import com.apollographql.apollo.compiler.parser.introspection.toSDL
import com.fasterxml.jackson.databind.JsonNode
import com.fasterxml.jackson.databind.ObjectMapper
import okhttp3.MediaType.Companion.toMediaTypeOrNull
import okhttp3.OkHttpClient
import okhttp3.Request
import okhttp3.RequestBody.Companion.toRequestBody
import okhttp3.Response
import okio.buffer
import okio.sink
import java.io.ByteArrayOutputStream
import java.io.File
import java.util.concurrent.TimeUnit

object SchemaDownloader {

    fun newOkHttpClient(
        connectTimeoutSeconds: Long,
        readTimeoutSeconds: Long,
        writeTimeoutSeconds: Long,
        useSelfSignedCertificat: Boolean,
        useGzip: Boolean
    ): OkHttpClient {
        val okhttpClientBuilder = if (useSelfSignedCertificat) {
            UnsafeOkHttpClient.getUnsafeOkHttpClient()
        } else {
            OkHttpClient.Builder()
        }

        if (useGzip) {
            okhttpClientBuilder.addInterceptor(GzipRequestInterceptor())
        }

        return OkHttpClient.Builder()
            .connectTimeout(connectTimeoutSeconds, TimeUnit.SECONDS)
            .readTimeout(readTimeoutSeconds, TimeUnit.SECONDS)
            .writeTimeout(writeTimeoutSeconds, TimeUnit.SECONDS)
            .build()
    }

    private fun executeQuery(
        query: String,
        variables: String? = null,
        url: String,
        headers: Map,
        okHttpClient: OkHttpClient
    ): Response {
        val byteArrayOutputStream = ByteArrayOutputStream()
        JsonWriter.of(byteArrayOutputStream.sink().buffer())
            .apply {
                beginObject()
                name("query")
                value(query)
                if (variables != null) {
                    name("variables")
                    value(variables)
                }
                endObject()
                flush()
            }

        val body = byteArrayOutputStream.toByteArray().toRequestBody("application/json".toMediaTypeOrNull())
        val request = Request.Builder()
            .post(body)
            .apply {
                addHeader("User-Agent", "ApolloMavenPlugin")
                headers.entries.forEach {
                    addHeader(it.key, it.value)
                }
            }
            .header("apollographql-client-name", "apollo-maven-plugin")
            .header("apollographql-client-version", com.apollographql.apollo.compiler.VERSION)
            .url(url)
            .build()

        val response = okHttpClient.newCall(request).execute()

        check(response.isSuccessful) {
            "Cannot get schema from $url: ${response.code}:\n${response.body?.string()}"
        }

        return response
    }

    fun downloadIntrospection(
        endpoint: String,
        schema: File,
        headers: Map,
        prettyPrint: Boolean,
        okHttpClient: OkHttpClient
    ) {

        val response = executeQuery(introspectionQuery, null, endpoint, headers, okHttpClient)

        writeResponse(schema, response, prettyPrint)
    }

    fun downloadRegistry(
        graph: String,
        schema: File,
        key: String,
        variant: String,
        prettyPrint: Boolean,
        okHttpClient: OkHttpClient
    ) {
        val query =
            """
    query DownloadSchema(${'$'}graphID: ID!, ${'$'}variant: String!) {
      service(id: ${'$'}graphID) {
        variant(name: ${'$'}variant) {
          activeSchemaPublish {
            schema {
              document
            }
          }
        }
      }
    }
            """.trimIndent()
        val variables =
            """
      {
        "graphID": "$graph",
        "variant": "$variant"
      }
            """.trimIndent()

        val response = executeQuery(query, variables, "https://graphql.api.apollographql.com/api/graphql", mapOf("x-api-key" to key), okHttpClient)

        val responseString = response.body.use { it?.string() }

        val document = responseString
            ?.fromJson>()
            ?.get("data").cast>()
            ?.get("service").cast>()
            ?.get("variant").cast>()
            ?.get("activeSchemaPublish").cast>()
            ?.get("schema").cast>()
            ?.get("document").cast()

        check(document != null) {
            "Cannot retrieve document from $responseString\nCheck graph id and variant"
        }

        writeResponse(schema, document, prettyPrint)
    }

    inline fun  Any?.cast() = this as? T

    private fun writeResponse(schema: File, response: Response, prettyPrint: Boolean) {
        schema.parentFile?.mkdirs()
        response.body.use { responseBody ->
            if (schema.extension.toLowerCase() == "json") {
                schema.writeText(pretify(responseBody!!.string(), prettyPrint))
            } else {
                IntrospectionSchema(pretify(responseBody!!.string(), prettyPrint).byteInputStream()).toSDL(schema)
            }
        }
    }

    private fun writeResponse(schema: File, document: String?, prettyPrint: Boolean) {
        schema.parentFile?.mkdirs()
        if (schema.extension.toLowerCase() == "json") {
            schema.writeText(pretify(document, prettyPrint))
        } else {
            IntrospectionSchema(pretify(document, prettyPrint).byteInputStream()).toSDL(schema)
        }
    }

    private fun pretify(document: String?, prettyPrint: Boolean): String {
        if (document.isNullOrBlank()) {
            throw IllegalArgumentException("document: is null or blank")
        }
        if (prettyPrint) {
            val mapper = ObjectMapper()
            val json: JsonNode = mapper.readValue(document, JsonNode::class.java)
            return mapper.writerWithDefaultPrettyPrinter().writeValueAsString(json)
        }
        return document.orEmpty()
    }

    private val introspectionQuery =
        """
    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
                  }
                }
              }
            }
          }
        }
      }
    }
        """.trimIndent()
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy