com.gw2tb.apigen.internal.impl.queries.kt Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of api-generator Show documentation
Show all versions of api-generator Show documentation
A library for generating programs that interface with the official Guild Wars 2 API.
The newest version!
/*
* Copyright (c) 2019-2024 Leon Linhart
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
@file:OptIn(LowLevelApiGenApi::class)
package com.gw2tb.apigen.internal.impl
import com.gw2tb.apigen.internal.dsl.*
import com.gw2tb.apigen.ir.*
import com.gw2tb.apigen.ir.model.IRAPIQuery
import com.gw2tb.apigen.ir.model.IRAPIType
import com.gw2tb.apigen.ir.model.IRPathParameter
import com.gw2tb.apigen.ir.model.IRQueryParameter
import com.gw2tb.apigen.model.*
import com.gw2tb.apigen.model.v2.*
import com.gw2tb.apigen.model.Name
import com.gw2tb.apigen.schema.model.APIType
import com.gw2tb.apigen.schema.model.QueryParameter
import kotlin.time.*
internal abstract class QueriesBuilderImplBase : QueriesBuilder {
protected abstract val typeRegistry: ScopedTypeRegistry
protected abstract val path: String
protected abstract val endpoint: APIEndpoint
protected abstract val querySuffix: String?
protected abstract val idTypeKey: String?
protected abstract val summary: String
protected abstract val cache: Duration?
protected abstract val security: Security?
protected abstract val queryTypes: QueryTypes?
protected abstract val apiTypeFactory: (SchemaVersionedDataImpl>, APIType.InterpretationHint?, Boolean) -> T
protected val pathParameters: MutableMap = mutableMapOf()
override fun pathParameter(name: String, type: DeferredPrimitiveType<*>, description: String, key: String, camelCase: String?) {
check(":$key" in (path.split('/')))
check(key !in pathParameters)
pathParameters[key] = IRPathParameter(
key = key,
type = type.getFlat(),
name = Name.derive(camelCase = camelCase, titleCase = name),
description = description
)
}
protected val queryParameters: MutableMap = mutableMapOf()
override fun queryParameter(name: String, type: DeferredPrimitiveType<*>, description: String, key: String, camelCase: String?, isOptional: Boolean) {
check(key !in queryParameters)
queryParameters[key] = IRQueryParameter(
key = key,
type = type.getFlat(),
name = Name.derive(camelCase = camelCase, titleCase = name),
description = description,
isOptional = isOptional
)
}
override fun conditional(
name: Name,
description: String,
disambiguationBy: String,
disambiguationBySideProperty: Boolean,
interpretationInNestedProperty: Boolean,
sharedConfigure: (AbstractSchemaRecordBuilder.() -> Unit)?,
block: SchemaConditionalBuilder.() -> Unit
): DeferredSchemaClass =
SchemaConditionalBuilder(
name, description, disambiguationBy, disambiguationBySideProperty, interpretationInNestedProperty,
sharedConfigure, apiTypeFactory, typeRegistry
).also(block)
override fun enum(
type: DeferredPrimitiveType<*>,
name: Name,
description: String,
block: SchemaEnumBuilder.() -> Unit
): DeferredSchemaClass =
SchemaEnumBuilder(type, name, description, apiTypeFactory, typeRegistry).also(block)
override fun record(
name: Name,
description: String,
block: SchemaRecordBuilder.() -> Unit
): DeferredSchemaClass =
SchemaRecordBuilder(name, description, apiTypeFactory, typeRegistry).also(block)
override fun tuple(
name: Name,
description: String,
block: SchemaTupleBuilder.() -> Unit
): DeferredSchemaClass =
SchemaTupleBuilder(name, description, apiTypeFactory, typeRegistry).also(block)
abstract fun collect(): Collection
}
internal class QueriesBuilderV1Impl(
override val path: String,
override val querySuffix: String?,
override val endpoint: APIEndpoint,
override val idTypeKey: String?,
override val summary: String,
override val cache: Duration?,
override val security: Security?,
override val queryTypes: QueryTypes?,
override val apiTypeFactory: (SchemaVersionedDataImpl>, APIType.InterpretationHint?, Boolean) -> IRAPIType.V1,
override val typeRegistry: ScopedTypeRegistry
) : QueriesBuilderImplBase(), QueriesBuilderV1 {
private lateinit var _schema: IRTypeUse<*>
override fun schema(schema: DeferredType>) {
check(!this::_schema.isInitialized)
_schema = schema.get(typeRegistry, interpretationHint = null, isTopLevel = true).single().data
}
override fun collect(): Collection = listOf(
IRAPIQuery.V1(
path = path,
endpoint = endpoint,
summary = summary,
pathParameters = pathParameters,
queryParameters = queryParameters,
querySuffix = querySuffix,
cache = cache,
type = _schema
)
)
}
internal class QueriesBuilderV2Impl(
override val path: String,
override val querySuffix: String?,
override val endpoint: APIEndpoint,
override val idTypeKey: String?,
override val summary: String,
override val cache: Duration?,
override val security: Security?,
override val queryTypes: QueryTypes?,
private val since: SchemaVersion,
private val until: SchemaVersion?,
override val apiTypeFactory: (SchemaVersionedDataImpl>, APIType.InterpretationHint?, Boolean) -> IRAPIType.V2,
override val typeRegistry: ScopedTypeRegistry
) : QueriesBuilderImplBase(), QueriesBuilderV2 {
private lateinit var _schema: SchemaVersionedDataImpl>
override fun schema(schema: DeferredType>) {
check(!this::_schema.isInitialized)
_schema = schema.get(typeRegistry, interpretationHint = null, isTopLevel = true)
}
override fun schema(vararg schemas: Pair>>) {
check(!this::_schema.isInitialized)
/*
* The method only receives `since` constraints. Thus, we need to ensure that no two constraints are equal
* before we can continue processing.
*/
require(schemas.all { a -> schemas.all { b -> a == b || a.first != b.first } })
require(schemas.none { (version, _) -> version < since })
require(until == null || schemas.none { (version, _) -> version >= until })
val deferredTypeRegistry = ScopedTypeRegistry(declarationCollector = { _, _ -> })
val versions = buildVersionedSchemaData {
schemas.asIterable()
.sortedBy { it.first }
.zipSchemaVersionConstraints()
.forEach { (since, until) ->
since.second.get(deferredTypeRegistry, interpretationHint = null, isTopLevel = true).forEach { (schema, schemaSince, schemaUntil) ->
// Clamp schema versions against bounds implied by the function call
if (since.first >= schemaSince && (until == null || schemaSince < until.first)) {
add(
datum = schema,
since = maxOf(since.first, schemaSince),
until = when {
until == null -> schemaUntil
schemaUntil == null -> until.first
else -> minOf(until.first, schemaUntil)
}
)
}
}
}
}
if (versions.any { it.data is IRTypeReference.Declaration }) {
val apiType = apiTypeFactory(buildVersionedSchemaData {
versions.forEach { (schema, since, until) ->
if (schema is IRTypeReference.Declaration)
add(schema.declaration, since, until)
}
}, null, true)
typeRegistry.registerDeclaration(apiType.name, apiType)
}
_schema = versions
}
private fun buildQuery(
schema: SchemaVersionedDataImpl>,
queryParameters: Map? = null,
queryDetails: IRAPIQuery.Details? = null
) = IRAPIQuery.V2(
path = path,
endpoint = endpoint,
summary = summary,
pathParameters = pathParameters,
queryParameters = queryParameters ?: this.queryParameters,
details = queryDetails,
querySuffix = querySuffix,
cache = cache,
since = since,
until = until,
security = security?.scopes ?: emptySet(),
_type = schema
)
override fun collect(): Collection = buildList {
if (queryTypes != null) {
val idType: IRPrimitiveIdentifierOrAlias = when (val schema = _schema.getOrThrow(SchemaVersion.V2_SCHEMA_CLASSIC).data) {
is IRTypeReference.Declaration -> when (val declaration = schema.declaration) {
is IRConditional -> declaration.sharedProperties.find { it.serialName == idTypeKey }?.type
is IRRecord -> declaration.properties.find { it.serialName == idTypeKey }?.type
else -> error("Cannot extract ID type for key \"$idTypeKey\" from type: ${schema.javaClass}")
}
else -> error("Cannot extract ID type for key \"$idTypeKey\" from type: ${schema.javaClass}")
}.let {
if (it == null) error("Could not find ID member \"$idTypeKey\" for endpoint \"$endpoint\"")
when (it) {
is IRPrimitiveIdentifier -> it
is IRTypeReference.Alias -> it
else -> error("ID type is not a primitive identifier type: ${it.javaClass}")
}
}
queryTypes.values.forEach { queryType ->
val schema: SchemaVersionedDataImpl>
val queryParameters = mutableMapOf()
queryParameters += [email protected]
when (queryType) {
is QueryType.IDs -> {
schema = buildVersionedSchemaData {
add(IRArray(idType as IRTypeUse<*>, false, description = "the available IDs"), since = since, until = until)
}
}
is QueryType.ByID -> {
schema = _schema
IRQueryParameter(queryType.qpKey, idType as IRTypeUse<*>, queryType.qpName, queryType.qpDescription, false)
.also { queryParameters[it.key] = it }
}
is QueryType.ByIDs -> {
schema = _schema.mapData { v -> IRArray(v, false, "") }
IRQueryParameter(queryType.qpKey, IRArray(idType as IRTypeUse<*>, false, null), queryType.qpName, queryType.qpDescription, false)
.also { queryParameters[it.key] = it }
}
is QueryType.ByPage -> {
schema = _schema.mapData { v -> IRArray(v, false, "") }
IRQueryParameter(QueryParameter.PAGE_KEY, IRInteger, Name.derive(titleCase = "Page"), "the index of the requested page", false)
.also { queryParameters[it.key] = it }
IRQueryParameter(QueryParameter.PAGE_SIZE_KEY, IRInteger, Name.derive(titleCase = "PageSize"), "the size of the requested page", true)
.also { queryParameters[it.key] = it }
}
}
add(buildQuery(
schema = schema,
queryParameters = queryParameters,
queryDetails = IRAPIQuery.Details(queryType, idType)
))
}
} else {
add(buildQuery(_schema))
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy