Added CAA records, added multi-page results

This commit is contained in:
Robinson 2021-08-14 23:13:06 -06:00
parent 25750bf1c3
commit 61d86b18be
11 changed files with 168 additions and 102 deletions

29
LICENSE
View File

@ -1,7 +1,7 @@
- KloudflareAPI -
[The Apache Software License, Version 2.0]
https://git.dorkbox.com/dorkbox/KloudflareAPI
Copyright 2020
Copyright 2021
Dorkbox LLC
Cloudflare API v4 for Kotlin
@ -13,3 +13,30 @@
JetBrains s.r.o. and Kotlin Programming Language contributors
Kotlin Compiler, Test Data+Libraries, and Tools repository contain third-party code, to which different licenses may apply
See: https://github.com/JetBrains/kotlin/blob/master/license/README.md
- OkHttp - Squares meticulous HTTP client for the JVM, Android, and GraalVM
[The Apache Software License, Version 2.0]
https://github.com/square/okhttp
Copyright 2021
Square, Inc
- Conscrypt - An open platform for building modern web apps for Java back ends
[The Apache Software License, Version 2.0]
https://github.com/google/conscrypt
Copyright 2021
Google Inc
The Android Open Source Project
The Netty Project
Apache Harmony
- Retrofit - A type-safe HTTP client for Android and the JVM
[The Apache Software License, Version 2.0]
https://github.com/square/retrofit
Copyright 2021
Square, Inc
- Moshi - A modern JSON library for Kotlin and Java
[The Apache Software License, Version 2.0]
https://github.com/square/moshi
Copyright 2021
Square, Inc

View File

@ -60,7 +60,7 @@ Maven Info
<dependency>
<groupId>com.dorkbox</groupId>
<artifactId>KloudflareAPI</artifactId>
<version>1.3</version>
<version>1.4</version>
</dependency>
</dependencies>
```

View File

@ -1,30 +1,45 @@
/*
* Copyright 2021 dorkbox, llc
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import java.time.Instant
///////////////////////////////
////// PUBLISH TO SONATYPE / MAVEN CENTRAL
////// TESTING : (to local maven repo) <'publish and release' - 'publishToMavenLocal'>
////// RELEASE : (to sonatype/maven central), <'publish and release' - 'publishToSonatypeAndRelease'>
///////////////////////////////
gradle.startParameter.showStacktrace = ShowStacktrace.ALWAYS // always show the stacktrace!
gradle.startParameter.warningMode = WarningMode.All
plugins {
java
id("com.dorkbox.GradleUtils") version "2.9"
id("com.dorkbox.Licensing") version "2.9.2"
id("com.dorkbox.VersionUpdate") version "2.4"
id("com.dorkbox.GradlePublish") version "1.11"
id("com.dorkbox.GradleUtils") version "1.12"
id("com.dorkbox.Licensing") version "2.5.2"
id("com.dorkbox.VersionUpdate") version "2.0.5"
id("com.dorkbox.GradlePublish") version "1.8"
kotlin("jvm") version "1.4.21"
kotlin("kapt") version "1.4.21"
kotlin("jvm") version "1.5.21"
kotlin("kapt") version "1.5.21"
}
object Extras {
// set for the project
const val description = "Cloudflare API v4 for Kotlin"
const val group = "com.dorkbox"
const val version = "1.3"
const val version = "1.4"
// set as project.ext
const val name = "KloudflareAPI"
@ -32,6 +47,7 @@ object Extras {
const val vendor = "Dorkbox LLC"
const val vendorUrl = "https://dorkbox.com"
const val url = "https://git.dorkbox.com/dorkbox/KloudflareAPI"
val buildDate = Instant.now().toString()
}
@ -39,8 +55,7 @@ object Extras {
///// assign 'Extras'
///////////////////////////////
GradleUtils.load("$projectDir/../../gradle.properties", Extras)
GradleUtils.fixIntellijPaths()
GradleUtils.defaultResolutionStrategy()
GradleUtils.defaults()
GradleUtils.compileConfiguration(JavaVersion.VERSION_11)
licensing {
@ -71,12 +86,6 @@ sourceSets {
}
}
repositories {
// mavenLocal() // this must be first!
jcenter()
}
tasks.jar.get().apply {
manifest {
// https://docs.oracle.com/javase/tutorial/deployment/jar/packageman.html
@ -98,16 +107,17 @@ dependencies {
implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
implementation("org.jetbrains.kotlin:kotlin-stdlib")
implementation("org.jetbrains.kotlin:kotlin-stdlib-common")
implementation("org.jetbrains.kotlin:kotlin-reflect")
val moshiVer = "1.11.0"
val okHttpVer = "4.9.0"
val moshiVer = "1.12.0"
val okHttpVer = "4.9.1"
val retroVer = "2.9.0"
implementation("com.squareup.okhttp3:okhttp:$okHttpVer")
implementation("com.squareup.okhttp3:logging-interceptor:$okHttpVer") // Log Network Calls
// better SSL library
implementation("org.conscrypt:conscrypt-openjdk-uber:2.5.1")
implementation("org.conscrypt:conscrypt-openjdk-uber:2.5.2")
// For serialization. THESE ARE NOT TRANSITIVE because it screws up the kotlin version
implementation("com.squareup.retrofit2:retrofit:$retroVer")
@ -116,6 +126,8 @@ dependencies {
implementation ("com.squareup.moshi:moshi:$moshiVer")
implementation ("com.squareup.moshi:moshi-kotlin:$moshiVer")
implementation("com.dorkbox:Updates:1.1")
// for AUTOMATIC kotlin reflective serialization of json classes
kapt ("com.squareup.moshi:moshi-kotlin-codegen:$moshiVer")
kaptTest ("com.squareup.moshi:moshi-kotlin-codegen:$moshiVer")

View File

@ -35,31 +35,33 @@ import okhttp3.ResponseBody
import org.conscrypt.Conscrypt
import retrofit2.Call
import retrofit2.Converter
import retrofit2.Response
import retrofit2.Retrofit
import retrofit2.converter.moshi.MoshiConverterFactory
import java.io.IOException
import java.security.Security
import java.util.Collections.emptyMap
class Kloudflare(private val xAuthEmail: String, private val xAuthKey: String) {
companion object {
private const val API_BASE_URL = "https://api.cloudflare.com/client/v4/"
init {
try {
Security.insertProviderAt(Conscrypt.newProvider(), 1);
}
catch (e: Throwable) {
e.printStackTrace();
}
}
/**
* Gets the version number.
*/
const val version = "1.3"
const val version = "1.4"
init {
// Add this project to the updates system, which verifies this class + UUID + version information
dorkbox.updates.Updates.add(Kloudflare::class.java, "16bcc9060ac6483782aafc2a5502e7b3", version)
try {
Security.insertProviderAt(Conscrypt.newProvider(), 1)
}
catch (e: Throwable) {
e.printStackTrace()
}
}
}
private val errorConverter: Converter<ResponseBody, CfErrorResponse>
@ -103,11 +105,10 @@ class Kloudflare(private val xAuthEmail: String, private val xAuthKey: String) {
return response.body?.string()!!
}
@Throws(IOException::class)
private fun <T> wrap(call: Call<CfResponse<T>>): T {
val response = call.execute()
val response: Response<CfResponse<T>> = call.execute()
val body = response.body()
val body: CfResponse<T>? = response.body()
if (response.isSuccessful && body != null && body.success) {
return body.result!!
}
@ -116,6 +117,18 @@ class Kloudflare(private val xAuthEmail: String, private val xAuthKey: String) {
throw IOException("Call failed: " + errorResponse?.errors?.joinToString { error: Error -> "[${error.code} : ${error.message}]" })
}
private fun <T> wrapOptions(call: Call<CfResponse<T>>): Pair<ResultInfo, T> {
val response: Response<CfResponse<T>> = call.execute()
val body: CfResponse<T>? = response.body()
if (response.isSuccessful && body != null && body.success) {
return Pair(body.resultInfo!!, body.result!!)
}
val errorResponse = errorConverter.convert(response.errorBody()!!)
throw IOException("Call failed: " + errorResponse?.errors?.joinToString { error: Error -> "[${error.code} : ${error.message}]" })
}
/**
* Gets the User details
*
@ -149,14 +162,25 @@ class Kloudflare(private val xAuthEmail: String, private val xAuthKey: String) {
* https://api.cloudflare.com/#zone-properties
*/
fun listZones(options: Map<String, String> = emptyMap()): List<Zone> {
val zones = wrap(cloudflare.listZones(xAuthEmail, xAuthKey, options))
zones.forEach { zone ->
// have to assign
zone.kloudflare = this;
val actualOptions = mutableMapOf<String, String>()
actualOptions.putAll(options)
actualOptions.putIfAbsent("per_page", "20") // not too many at once!
val (info, data) = wrapOptions(cloudflare.listZones(xAuthEmail, xAuthKey, actualOptions))
data.forEach { zone ->
// have to assign
zone.kloudflare = this
}
return zones
val zones = mutableListOf<Zone>()
zones.addAll(data)
if (info.totalPages - info.page > 0) {
actualOptions["page"] = "${info.page + 1}"
zones.addAll(listZones(actualOptions))
}
return data
}
/**
@ -182,13 +206,25 @@ class Kloudflare(private val xAuthEmail: String, private val xAuthKey: String) {
*
* https://api.cloudflare.com/#dns-records-for-a-zone-properties
*/
fun listDnsRecords(zone: Zone): List<DnsRecord> {
val wrap =
wrap(cloudflare.listDnsRecords(xAuthEmail, xAuthKey, zone.id))
wrap.forEach {
fun listDnsRecords(zone: Zone, options: Map<String, String> = emptyMap()): List<DnsRecord> {
val actualOptions = mutableMapOf<String, String>()
actualOptions.putAll(options)
actualOptions.putIfAbsent("per_page", "20") // not too many at once!
val (info, data) = wrapOptions(cloudflare.listDnsRecords(xAuthEmail, xAuthKey, zone.id, actualOptions))
data.forEach {
it.zone = zone
}
return wrap
val records = mutableListOf<DnsRecord>()
records.addAll(data)
if (info.totalPages - info.page > 0) {
actualOptions["page"] = "${info.page + 1}"
records.addAll(listDnsRecords(zone, actualOptions))
}
return records
}
/**
@ -197,10 +233,9 @@ class Kloudflare(private val xAuthEmail: String, private val xAuthKey: String) {
* https://api.cloudflare.com/#dns-records-for-a-zone-create-dns-record
*/
fun createDnsRecord(dnsRecord: CreateDnsRecord): DnsRecord {
val wrap =
wrap(cloudflare.createDnsRecord(xAuthEmail, xAuthKey, dnsRecord.zone.id, dnsRecord))
wrap.zone = dnsRecord.zone
return wrap
return wrap(cloudflare.createDnsRecord(xAuthEmail, xAuthKey, dnsRecord.zone.id, dnsRecord)).apply {
zone = dnsRecord.zone
}
}
/**
@ -208,7 +243,7 @@ class Kloudflare(private val xAuthEmail: String, private val xAuthKey: String) {
*
* https://api.cloudflare.com/#dns-records-for-a-zone-update-dns-record
*/
fun updateDnsRecord(updatedDnsRecord: UpdateDnsRecord): Any {
fun updateDnsRecord(updatedDnsRecord: UpdateDnsRecord): DnsRecord {
return wrap(cloudflare.updateDnsRecord(xAuthEmail,
xAuthKey,
updatedDnsRecord.zone.id,

View File

@ -28,15 +28,7 @@ import dorkbox.kloudflareApi.api.zone.RatePlan
import dorkbox.kloudflareApi.api.zone.Zone
import dorkbox.kloudflareApi.api.zone.settings.ZoneSetting
import retrofit2.Call
import retrofit2.http.Body
import retrofit2.http.DELETE
import retrofit2.http.GET
import retrofit2.http.Header
import retrofit2.http.Headers
import retrofit2.http.POST
import retrofit2.http.PUT
import retrofit2.http.Path
import retrofit2.http.QueryMap
import retrofit2.http.*
interface CloudflareActions {
/**
@ -124,7 +116,8 @@ interface CloudflareActions {
fun listDnsRecords(
@Header("X-Auth-Email") email: String,
@Header("X-Auth-Key") key: String,
@Path("zone_identifier") zoneIdentifier: String
@Path("zone_identifier") zoneIdentifier: String,
@QueryMap options: Map<String, String> = emptyMap()
): Call<CfResponse<List<DnsRecord>>>

View File

@ -26,6 +26,9 @@ class ResultInfo {
@field:[Json(name = "per_page")]
var perPage = 20
@field:[Json(name = "total_pages")]
var totalPages = 1
@field:[Json(name = "count")]
var count = 1

View File

@ -67,4 +67,10 @@ open class CreateDnsRecord(@Transient val zone: Zone = Zone()) {
*/
@field:[Json(name = "proxied")]
var proxied = false
/**
* Whether the record is receiving the performance and security benefits of Cloudflare
*/
@field:[Json(name = "data")]
var data = mutableMapOf<String, Any>()
}

View File

@ -1,26 +0,0 @@
/*
* Copyright 2019 dorkbox, llc
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package dorkbox.kloudflareApi.api.dns
import com.squareup.moshi.JsonClass
/**
* https://api.cloudflare.com/#dns-records-for-a-zone-properties
*/
@JsonClass(generateAdapter = true)
class Data {
}

View File

@ -43,7 +43,9 @@ open class DnsRecord {
}
}
override fun toString(): String {
return "DnsRecord(type=$type, name='${name}, content='${content}', ttl='${ttl}')"
}
/**
* DNS record identifier tag
@ -53,7 +55,7 @@ open class DnsRecord {
/**
* Record type
* A, AAAA, CNAME, TXT, SRV, LOC, MX, NS, SPF, CERT, DNSKEY, DS, NAPTR, SMIMEA, SSHFP, TLSA, URI
* A, AAAA, CNAME, TXT, SRV, LOC, MX, NS, SPF, CERT, DNSKEY, DS, NAPTR, SMIMEA, SSHFP, TLSA, URI, CCA
*/
@field:[Json(name = "type")]
var type = RecordType.A
@ -125,5 +127,5 @@ open class DnsRecord {
* Metadata about the record
*/
@field:[Json(name = "data")]
var data: Data = Data()
var data = mutableMapOf<String, Any>()
}

View File

@ -16,5 +16,5 @@
package dorkbox.kloudflareApi.api.dns
enum class RecordType {
A, AAAA, CNAME, TXT, SRV, LOC, MX, NS, SPF, CERT, DNSKEY, DS, NAPTR, SMIMEA, SSHFP, TLSA, URI
A, AAAA, CNAME, TXT, SRV, LOC, MX, NS, SPF, CERT, DNSKEY, DS, NAPTR, SMIMEA, SSHFP, TLSA, URI, CAA
}

View File

@ -53,18 +53,32 @@ object KloudflareTest {
// println(kloudflare.getUserBillingHistory())
val zones = kloudflare.listZones()
zones.forEach {
println(it)
}
// val zones = kloudflare.listZones().filter { it.name == "example.com" }
// zones.forEach {
// println(it)
// }
// println(kloudflare.getZoneRatePlans("123"))
// println(kloudflare.getZoneRatePlans("123"))
// println(kloudflare.getZoneSettings("123"))
// println(kloudflare.listDnsRecords("123"))
println(kloudflare.listAccessRules())
// println(kloudflare.listAccessRules())
val zone = kloudflare.listZones().first { it.name == "example.com" }
zone.dnsRecords.forEach {
println(it)
}
// val newDnsRecord = CreateDnsRecord(zone)
// newDnsRecord.type = RecordType.CAA
// newDnsRecord.name = "example.com"
// newDnsRecord.data["flags"] = 0
// newDnsRecord.data["tag"] = "issue"
// newDnsRecord.data["value"] = "letsencrypt.org"
//
// val newRecord = kloudflare.createDnsRecord(newDnsRecord)
// println("Created: ${newRecord.name} -> ${newRecord.content}")
}
finally {