From 84f33d6cd55656a61e69a15e5e9621f9765d0cdb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gabrielle=20Guimar=C3=A3es=20de=20Oliveira?= Date: Fri, 21 Jul 2023 15:03:18 -0300 Subject: [PATCH] style: improve docs and dx for builders (#24) * feat: add docs for the main java code * docs: add docs for account * docs: add docs for charge * docs: add docs for customer * docs: add docs for payment * docs: add docs for pix qr code * docs: add docs for refund * docs: add docs for subscription --- src/main/kotlin/Account.kt | 32 +++- src/main/kotlin/Charge.kt | 98 +++++++++++ src/main/kotlin/Customer.kt | 54 ++++++ src/main/kotlin/Payment.kt | 45 +++++ src/main/kotlin/PixQrCode.kt | 45 +++++ src/main/kotlin/Refund.kt | 36 ++++ src/main/kotlin/Subscription.kt | 36 ++++ src/main/kotlin/TaxID.kt | 13 +- src/main/kotlin/WooviError.kt | 10 +- src/main/kotlin/WooviSDK.kt | 280 ++++++++++++++++++++++++++++---- 10 files changed, 606 insertions(+), 43 deletions(-) diff --git a/src/main/kotlin/Account.kt b/src/main/kotlin/Account.kt index 8f2b80e..d0b8f4a 100644 --- a/src/main/kotlin/Account.kt +++ b/src/main/kotlin/Account.kt @@ -8,40 +8,66 @@ import kotlinx.serialization.Serializable @Serializable public data class Balance( + /** Total amount in cents */ public val total: Int, + + /** Blocked amount in cents */ public val blocked: Int, + + /** Available amount in cents */ public val available: Int, ) @Serializable public data class Account( + /** The id of the account */ public val accountId: String, + + /** If the account is a default account */ public val isDefault: Boolean, + + /** The balance of the account */ public val balance: Balance, ) @Serializable public data class WithdrawRequest( + /** Value in cents */ public val value: Int, ) @Serializable -public data class WithdrawResponse( - public val withdraw: Withdraw, -) +public data class WithdrawResponse(public val withdraw: Withdraw) +/** + * The withdrawal response. + * + * @property account The account that the withdrawal was made + * @property transaction The transaction of the withdrawal + */ @Serializable public data class Withdraw( public val account: Account, public val transaction: WithdrawTransaction, ) +/** + * The transaction of the withdrawal. + * + * @property endToEndId The end to end id of the transaction + * @property value The value of the transaction in cents + */ @Serializable public data class WithdrawTransaction( public val endToEndId: String, public val value: Int, ) +/** + * The response of the account list. + * + * @property accounts The list of accounts + */ @Serializable public data class AccountListResponse(public val accounts: List) : List by accounts diff --git a/src/main/kotlin/Charge.kt b/src/main/kotlin/Charge.kt index fc9e6d8..142aac1 100644 --- a/src/main/kotlin/Charge.kt +++ b/src/main/kotlin/Charge.kt @@ -105,6 +105,104 @@ public class ChargeBuilder internal constructor() { public var fines: Fines? by Properties.nullable() public var additionalInfo: List = emptyList() + /** + * The correlation ID is a unique identifier for each request. It is useful for tracking a request through the system. + * + * @param correlationID The correlation ID + */ + public fun correlationID(correlationID: String): ChargeBuilder = apply { + this.correlationID = correlationID + } + + /** + * The value of the charge in cents. + * + * @param value The value of the charge in cents + */ + public fun value(value: Int) { + this.value = value + } + + /** + * The comment of the charge. + * + * @param comment The comment of the charge + */ + public fun comment(comment: String) { + this.comment = comment + } + + /** + * The customer information. + * + * @param customer The customer information + */ + public fun customer(customer: CustomerBuilder): ChargeBuilder = apply { + this.customer = customer.build() + } + + /** + * The expiration date of the charge in days. + * + * @param expiresIn The expiration date of the charge in days + */ + public fun expiresIn(expiresIn: Int): ChargeBuilder = apply { + this.expiresIn = expiresIn + } + + /** + * The days for overdue. + * + * @param daysForOverdue The days for overdue + */ + public fun daysForOverdue(daysForOverdue: Int): ChargeBuilder = apply { + this.daysForOverdue = daysForOverdue + } + + /** + * The days after due date. + * + * @param daysAfterDueDate The days after due date + */ + public fun daysAfterDueDate(daysAfterDueDate: Int): ChargeBuilder = apply { + this.daysAfterDueDate = daysAfterDueDate + } + + /** + * The interests value in cents. + * + * @param interests The interests value in cents + */ + public fun interests(interests: Int): ChargeBuilder = apply { + this.interests = Interests(interests) + } + + /** + * The fines value in cents. + * + * @param fines The fines value in cents + */ + public fun fines(fines: Int): ChargeBuilder = apply { + this.fines = Fines(fines) + } + + /** + * The interests value in cents. + */ + public fun additionalInfo(additionalInfo: List): ChargeBuilder = apply { + this.additionalInfo = additionalInfo + } + + /** + * Adds an info. + * + * @param key The key of the additional info + * @param value The value of the additional info + */ + public fun additionalInfo(key: String, value: String): ChargeBuilder = apply { + additionalInfo = additionalInfo + AdditionalInfo(key, value) + } + @JvmSynthetic internal fun build(): ChargeRequestBody { return ChargeRequestBody( diff --git a/src/main/kotlin/Customer.kt b/src/main/kotlin/Customer.kt index 511373c..721c8a7 100644 --- a/src/main/kotlin/Customer.kt +++ b/src/main/kotlin/Customer.kt @@ -57,6 +57,60 @@ public class CustomerBuilder internal constructor() { public var correlationID: String? by Properties.nullable() public var address: Address? by Properties.nullable() + /** + * The customer's name. + * + * @param name The customer's name. + */ + public fun name(name: String): CustomerBuilder = apply { + this.name = name + } + + /** + * The customer's tax id. + * + * @param taxID The customer's tax id. + */ + public fun taxID(taxID: String): CustomerBuilder = apply { + this.taxID = taxID + } + + /** + * The customer's email. + * + * @param email The customer's email. + */ + public fun email(email: String): CustomerBuilder = apply { + this.email = email + } + + /** + * The customer's phone. + * + * @param phone The customer's phone. + */ + public fun phone(phone: String): CustomerBuilder = apply { + this.phone = phone + } + + /** + * The customer's correlation id. + * + * @param correlationID The customer's correlation id. + */ + public fun correlationID(correlationID: String): CustomerBuilder = apply { + this.correlationID = correlationID + } + + /** + * The customer's address. + * + * @param address The customer's address. + */ + public fun address(address: Address): CustomerBuilder = apply { + this.address = address + } + @JvmSynthetic internal fun build(): CustomerRequest { return CustomerRequest(name, taxID, email, phone, correlationID, address) diff --git a/src/main/kotlin/Payment.kt b/src/main/kotlin/Payment.kt index 49d17ec..87f0ad9 100644 --- a/src/main/kotlin/Payment.kt +++ b/src/main/kotlin/Payment.kt @@ -72,6 +72,51 @@ public class PaymentBuilder internal constructor() { public var comment: String? by Properties.nullable() public var sourceAccountId: String? by Properties.nullable() + /** + * The value of the payment in cents. + * + * @param value The value of the payment in cents. + */ + public fun value(value: Int): PaymentBuilder = apply { + this.value = value + } + + /** + * The destination of the payment. + * + * @param destinationAlias The destination of the payment. + */ + public fun destinationAlias(destinationAlias: String): PaymentBuilder = apply { + this.destinationAlias = destinationAlias + } + + /** + * The source account of the payment. + * + * @param sourceAccountId The source account of the payment. + */ + public fun sourceAccountId(sourceAccountId: String): PaymentBuilder = apply { + this.sourceAccountId = sourceAccountId + } + + /** + * The correlation id of the payment. + * + * @param correlationID The correlation id of the payment. + */ + public fun correlationID(correlationID: String): PaymentBuilder = apply { + this.correlationID = correlationID + } + + /** + * The comment of the payment. + * + * @param comment The comment of the payment. + */ + public fun comment(comment: String): PaymentBuilder = apply { + this.comment = comment + } + @JvmSynthetic internal fun build(): PaymentRequest { return PaymentRequest(value, destinationAlias, correlationID, comment, sourceAccountId) diff --git a/src/main/kotlin/PixQrCode.kt b/src/main/kotlin/PixQrCode.kt index e5c6d86..0d79d65 100644 --- a/src/main/kotlin/PixQrCode.kt +++ b/src/main/kotlin/PixQrCode.kt @@ -47,6 +47,51 @@ public class PixQrCodeBuilder internal constructor() { public var comment: String? by Properties.nullable() public var identifier: String by Properties.required() + /** + * The name of the pix qr code. + * + * @param name The name of the pix qr code. + */ + public fun name(name: String): PixQrCodeBuilder = apply { + this.name = name + } + + /** + * The correlation id of the pix qr code. + * + * @param correlationID The correlation id of the pix qr code. + */ + public fun correlationID(correlationID: String): PixQrCodeBuilder = apply { + this.correlationID = correlationID + } + + /** + * The value of the pix qr code. + * + * @param value The value of the pix qr code. + */ + public fun value(value: Int): PixQrCodeBuilder = apply { + this.value = value + } + + /** + * The comment of the pix qr code. + * + * @param comment The comment of the pix qr code. + */ + public fun comment(comment: String): PixQrCodeBuilder = apply { + this.comment = comment + } + + /** + * The identifier of the pix qr code. + * + * @param identifier The identifier of the pix qr code. + */ + public fun identifier(identifier: String): PixQrCodeBuilder = apply { + this.identifier = identifier + } + @JvmSynthetic internal fun build(): PixQrCodeRequestBody { return PixQrCodeRequestBody(name, correlationID, value, comment, identifier) diff --git a/src/main/kotlin/Refund.kt b/src/main/kotlin/Refund.kt index f8d74c3..3523537 100644 --- a/src/main/kotlin/Refund.kt +++ b/src/main/kotlin/Refund.kt @@ -47,6 +47,42 @@ public class RefundBuilder internal constructor() { public var transactionEndToEndId: String by Properties.required() public var comment: String? by Properties.nullable() + /** + * The correlation id of the refund. + * + * @param correlationID The correlation id of the refund. + */ + public fun correlationID(correlationID: String): RefundBuilder = apply { + this.correlationID = correlationID + } + + /** + * The value of the refund. + * + * @param value The value of the refund. + */ + public fun value(value: Int): RefundBuilder = apply { + this.value = value + } + + /** + * The transaction end to end id of the refund. + * + * @param transactionEndToEndId The transaction end to end id of the refund. + */ + public fun transactionEndToEndId(transactionEndToEndId: String): RefundBuilder = apply { + this.transactionEndToEndId = transactionEndToEndId + } + + /** + * The comment of the refund. + * + * @param comment The comment of the refund. + */ + public fun comment(comment: String): RefundBuilder = apply { + this.comment = comment + } + @JvmSynthetic internal fun build(): RefundRequestBody { return RefundRequestBody(correlationID, value, transactionEndToEndId, comment) diff --git a/src/main/kotlin/Subscription.kt b/src/main/kotlin/Subscription.kt index 5c75536..7b1c184 100644 --- a/src/main/kotlin/Subscription.kt +++ b/src/main/kotlin/Subscription.kt @@ -32,6 +32,42 @@ public class SubscriptionBuilder internal constructor() { public var dayGenerateCharge: Int? by Properties.nullable() public var chargeType: ChargeType? by Properties.nullable() + /** + * The customer of the subscription. + * + * @param customer The customer of the subscription. + */ + public fun customer(customer: CustomerRequest): SubscriptionBuilder = apply { + this.customer = customer + } + + /** + * The value of the subscription. + * + * @param value The value of the subscription. + */ + public fun value(value: Int): SubscriptionBuilder = apply { + this.value = value + } + + /** + * The day generate charge of the subscription. + * + * @param dayGenerateCharge The day generate charge of the subscription. + */ + public fun dayGenerateCharge(dayGenerateCharge: Int): SubscriptionBuilder = apply { + this.dayGenerateCharge = dayGenerateCharge + } + + /** + * The charge type of the subscription. + * + * @param chargeType The charge type of the subscription. + */ + public fun chargeType(chargeType: ChargeType): SubscriptionBuilder = apply { + this.chargeType = chargeType + } + internal fun build(): SubscriptionRequestBody { return SubscriptionRequestBody( customer, diff --git a/src/main/kotlin/TaxID.kt b/src/main/kotlin/TaxID.kt index 5461730..3026bc1 100644 --- a/src/main/kotlin/TaxID.kt +++ b/src/main/kotlin/TaxID.kt @@ -7,13 +7,16 @@ import kotlinx.serialization.ExperimentalSerializationApi import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable +/** + * Represents a taxation identifier, such as CPF or CNPJ in Brazil. + * + * @property text The taxation identifier. + * @property kind The taxation identifier kind. + */ @Serializable public data class TaxID( - @SerialName("taxID") - public val text: String, - - @SerialName("type") - public val kind: TaxIDKind? = null, + @SerialName("taxID") public val text: String, + @SerialName("type") public val kind: TaxIDKind? = null, ) { public fun kind(): TaxIDKind = kind ?: TaxIDKind.CPF } diff --git a/src/main/kotlin/WooviError.kt b/src/main/kotlin/WooviError.kt index 2aa4539..6bcc788 100644 --- a/src/main/kotlin/WooviError.kt +++ b/src/main/kotlin/WooviError.kt @@ -3,8 +3,10 @@ package br.com.openpix.sdk import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable +/** + * [WooviError] is the error response from the Woovi API that can be thrown as an [Throwable]. + * + * @property message The error message. + */ @Serializable -public class WooviError( - @SerialName("error") - override val message: String? = null, -) : Throwable() +public class WooviError(@SerialName("error") override val message: String? = null) : RuntimeException() diff --git a/src/main/kotlin/WooviSDK.kt b/src/main/kotlin/WooviSDK.kt index 88bb9cb..017586b 100644 --- a/src/main/kotlin/WooviSDK.kt +++ b/src/main/kotlin/WooviSDK.kt @@ -1,3 +1,4 @@ +@file:OptIn(ExperimentalSerializationApi::class) @file:JvmName("WooviSDKs") package br.com.openpix.sdk @@ -12,6 +13,7 @@ import io.ktor.client.request.* import io.ktor.http.* import io.ktor.serialization.kotlinx.json.* import java.io.File +import java.util.concurrent.Executor import java.util.concurrent.Executors import java.util.concurrent.Future import kotlin.coroutines.CoroutineContext @@ -27,86 +29,141 @@ public suspend fun main() { println(sdk.allPayments()) } -@OptIn(ExperimentalSerializationApi::class) +/** + * The entrypoint of Woovi SDK for Java, Kotlin and another JVM Languages, it does take an authorization [appId]. + */ public class WooviSDK @JvmOverloads public constructor( + /** The authorization [appId] of your application. */ private val appId: String, + + /** The base URL of Woovi API. */ private val baseUrl: String = "https://api.openpix.com.br/", - override val coroutineContext: CoroutineContext = Executors.newCachedThreadPool().asCoroutineDispatcher(), - private var json: Json = Json { - explicitNulls = true - ignoreUnknownKeys = true - }, - public val client: HttpClient = HttpClient(CIO) { - install(Logging) { - level = LogLevel.HEADERS - } - install(ContentNegotiation) { - json(json) - } + /** The [CoroutineContext] of the SDK. */ + public override val coroutineContext: CoroutineContext = Executors.newCachedThreadPool().asCoroutineDispatcher(), - install(HttpCallValidator) { - validateResponse { response -> - if (response.status.isSuccess()) return@validateResponse - throw response.body() - } - handleResponseExceptionWithRequest { exception, _ -> - val clientException = exception as? ClientRequestException ?: return@handleResponseExceptionWithRequest - throw clientException.response.body() - } - } + /** The [Json] instance of the SDK. */ + private var json: Json = createJson(), - defaultRequest { - url(baseUrl) - header("Content-Type", "application/json") - header("Authorization", appId) - } - }, + /** The [HttpClient] instance of the SDK. */ + public val client: HttpClient = createDefaultHttpClient(appId, baseUrl, json), ) : CoroutineScope { + /** + * Returns a pix qr code. + * + * @param id The pix qr code id. + * @return The pix qr code response. + */ public fun getPixQrCodeAsync(id: String): Future = future { getPixQrCode(id) } + /** + * Returns all pix qr codes. + * + * @return The pix qr code list. + */ public fun allPixQrCodesAsync(): Future = future { allPixQrCodes() } + /** + * Creates a pix qr code. + * + * @param builder The pix qr code builder. + * @return The pix qr code response. + */ public fun createPixQrCodeAsync(builder: PixQrCodeBuilder): Future = future { createPixQrCode(builder) {} } + /** + * Returns an account. + * + * @param id The account id. + * @return The account response. + */ public fun getAccountAsync(id: String): Future = future { getAccount(id) } + /** + * Returns all accounts. + * + * @return The account list response. + */ public fun allAccountsAsync(): Future = future { allAccounts() } + /** + * Withdraws a value [value] from an account with id [id]. + * + * @param id The account id. + * @param value The value to withdraw. + * @return The withdraw response. + */ public fun withdrawAsync(id: String, value: Int): Future = future { withdraw(id, value) } + /** + * Returns a payment. + * + * @param id The payment id. + * @return The payment response. + */ public fun getPaymentAsync(id: String): Future = future { getPayment(id) } + /** + * Returns all payments. + * + * @return The payment list response. + */ public fun allPaymentsAsync(): Future = future { allPayments() } + /** + * Creates a payment. + * + * @param builder The payment builder. + * @return The payment response. + */ public fun createPaymentAsync(builder: PaymentBuilder): Future = future { createPayment(builder) {} } + /** + * Deletes a charge. + * + * @param id The charge id. + * @return The charge delete response. + */ public fun deleteChargeAsync(id: String): Future = future { deleteCharge(id) } + /** + * Returns a charge. + * + * @param id The charge id. + * @return The charge response. + */ public fun getChargeAsync(id: String): Future = future { getCharge(id) } + /** + * Returns all charges. + * + * @param start The start date. + * @param end The end date. + * @param status The charge status. + * @return The charge list response. + */ @JvmOverloads public fun chargesAsync( start: String? = null, @@ -116,73 +173,234 @@ public class WooviSDK @JvmOverloads public constructor( charges(start, end, status) } + /** + * Creates a new charge. + * + * @param builder The charge builder. + * @return The charge response. + */ public fun createChargeAsync(builder: ChargeBuilder): Future = future { createCharge(builder) {} } + /** + * Returns a charge QR code image. + * + * @param id The charge id. + * @param size The image size. + * @return The charge QR code image. + */ public fun chargeQrCodeImageAsync(id: String, size: Int = 768): Future = future { chargeQrCodeImage(id, size) } + /** + * Returns a customer. + * + * @param id The customer id. + * @return The customer response. + */ public fun getCustomerAsync(id: String): Future = future { getCustomer(id) } + /** + * Returns all customers. + * + * @return The customer list response. + */ public fun allCustomersAsync(): Future = future { allCustomers() } + /** + * Creates a new customer. + * + * @param value The customer builder. + * @return The customer response. + */ public fun createCustomerAsync(value: CustomerBuilder): Future = future { createCustomer(value) {} } + /** + * Returns a refund. + * + * @param id The refund id. + * @return The refund response. + */ public fun getRefundAsync(id: String): Future = future { getRefund(id) } + /** + * Returns all refunds. + * + * @return The refund list response. + */ public fun allRefundsAsync(): Future = future { allRefunds() } + /** + * Creates a new refund. + * + * @param value The refund builder. + * @return The refund response. + */ public fun createRefundAsync(value: RefundBuilder): Future = future { createRefund(value) {} } - // Java util functions - + /** + * Removes JSON specification restriction (RFC-4627) and makes parser + * more liberal to the malformed input. In lenient mode quoted boolean literals, + * and unquoted string literals are allowed. + * + * Its relaxations can be expanded in the future, so that lenient parser becomes even more + * permissive to invalid value in the input, replacing them with defaults. + * + * `false` by default. + */ + @JvmOverloads + @OptIn(ExperimentalSerializationApi::class) public fun configureLenientJson(value: Boolean = true): WooviSDK = apply { json = Json(json) { isLenient = value } } + /** + * Enables structured objects to be serialized as map keys by + * changing serialized form of the map from JSON object (key-value pairs) to flat array like `[k1, v1, k2, v2]`. + * `false` by default. + */ + @JvmOverloads + @OptIn(ExperimentalSerializationApi::class) public fun configureAllowStructuredMapKeysJson(value: Boolean = true): WooviSDK = apply { json = Json(json) { allowStructuredMapKeys = value } } + /** + * Specifies whether resulting JSON should be pretty-printed. + * `false` by default. + */ + @JvmOverloads + @OptIn(ExperimentalSerializationApi::class) public fun configurePrettyPrintJson(value: Boolean = true): WooviSDK = apply { json = Json(json) { prettyPrint = value } } + /** + * Specifies whether `null` values should be encoded for nullable properties and must be present in JSON object + * during decoding. + * + * When this flag is disabled properties with `null` values without default are not encoded; + * during decoding, the absence of a field value is treated as `null` for nullable properties without a default value. + * + * `true` by default. + */ + @JvmOverloads + @OptIn(ExperimentalSerializationApi::class) public fun configureExplicitNullsJson(value: Boolean = true): WooviSDK = apply { json = Json(json) { explicitNulls = value } } + /** + * Specifies whether default values of Kotlin properties should be encoded. + * `false` by default. + */ + @JvmOverloads + @OptIn(ExperimentalSerializationApi::class) public fun configureEncodeDefaultsJson(value: Boolean = true): WooviSDK = apply { json = Json(json) { encodeDefaults = value } } + /** + * Specifies whether encounters of unknown properties in the input JSON + * should be ignored instead of throwing [SerializationException]. + * `false` by default. + */ + @JvmOverloads + @OptIn(ExperimentalSerializationApi::class) public fun configureIgnoreUnknownKeysJson(value: Boolean = true): WooviSDK = apply { json = Json(json) { ignoreUnknownKeys = value } } + + public companion object { + /** + * The entrypoint of Woovi SDK for Java, Kotlin and another JVM Languages, it does take an authorization [appId]. + * The overload constructor does take an [Executor] instead of [CoroutineContext] for better Java interoperability. + * + * @param appId The authorization [appId] of your application. + * @param baseUrl The base URL of Woovi API. + * @param executor The [Executor] of the SDK. + * @param json The [Json] instance of the SDK. + * @param client The [HttpClient] instance of the SDK. + */ + @JvmOverloads + @JvmStatic + public fun create( + /** The authorization [appId] of your application. */ + appId: String, + + /** The base URL of Woovi API. */ + baseUrl: String = "https://api.openpix.com.br/", + + /** The [Executor] of the SDK. */ + executor: Executor = Executors.newCachedThreadPool(), + + /** The [Json] instance of the SDK. */ + json: Json = createJson(), + + /** The [HttpClient] instance of the SDK. */ + client: HttpClient = createDefaultHttpClient(appId, baseUrl, json), + ): WooviSDK = WooviSDK(appId, baseUrl, executor.asCoroutineDispatcher(), json, client) + } +} + +@JvmSynthetic +internal fun createJson(): Json = Json { + explicitNulls = true + ignoreUnknownKeys = true +} + +@JvmSynthetic +internal fun createDefaultHttpClient(appId: String, baseUrl: String, json: Json): HttpClient { + return HttpClient(CIO) { + install(Logging) { + level = LogLevel.HEADERS + } + + install(ContentNegotiation) { + json(json) + } + + install(HttpCallValidator) { + validateResponse { response -> + if (response.status.isSuccess()) return@validateResponse + throw response.body() + } + handleResponseExceptionWithRequest { exception, _ -> + val clientException = exception as? ClientRequestException ?: return@handleResponseExceptionWithRequest + throw clientException.response.body() + } + } + + defaultRequest { + url(baseUrl) + header("Content-Type", "application/json") + header("Authorization", appId) + } + } }