diff --git a/.release-please-manifest.json b/.release-please-manifest.json index d52d2b97..a26ebfc1 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "0.13.0" + ".": "0.14.0" } \ No newline at end of file diff --git a/.stats.yml b/.stats.yml index fcff3ba6..09794c42 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,2 +1,2 @@ configured_endpoints: 97 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/orb%2Forb-02151f7654870aee7820ee1c04659a469e6b67ac4977116334512c6b6e6a2016.yml +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/orb%2Forb-77f4e8cf0fc3b3f18c894408f322af7988ae90606235fe5058442409142a33e1.yml diff --git a/CHANGELOG.md b/CHANGELOG.md index 8e05738e..866c7e38 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,18 @@ # Changelog +## 0.14.0 (2024-12-23) + +Full Changelog: [v0.13.0...v0.14.0](https://github.com/orbcorp/orb-java/compare/v0.13.0...v0.14.0) + +### Features + +* **api:** api update ([#159](https://github.com/orbcorp/orb-java/issues/159)) ([8b90db2](https://github.com/orbcorp/orb-java/commit/8b90db21c09850fa322e51d9e0586caac3d5476d)) + + +### Chores + +* **docs:** update readme ([#157](https://github.com/orbcorp/orb-java/issues/157)) ([4c11716](https://github.com/orbcorp/orb-java/commit/4c1171658c630869fcf256ed76244d8a626a9d16)) + ## 0.13.0 (2024-12-17) Full Changelog: [v0.12.0...v0.13.0](https://github.com/orbcorp/orb-java/compare/v0.12.0...v0.13.0) diff --git a/README.md b/README.md index 393d3359..adfbe079 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ -[![Maven Central](https://img.shields.io/maven-central/v/com.withorb.api/orb-java)](https://central.sonatype.com/artifact/com.withorb.api/orb-java/0.13.0) +[![Maven Central](https://img.shields.io/maven-central/v/com.withorb.api/orb-java)](https://central.sonatype.com/artifact/com.withorb.api/orb-java/0.14.0) @@ -25,7 +25,7 @@ The REST API documentation can be foundĀ on [docs.withorb.com](https://docs.with ```kotlin -implementation("com.withorb.api:orb-java:0.13.0") +implementation("com.withorb.api:orb-java:0.14.0") ``` #### Maven @@ -34,7 +34,7 @@ implementation("com.withorb.api:orb-java:0.13.0") com.withorb.api orb-java - 0.13.0 + 0.14.0 ``` @@ -56,6 +56,9 @@ OrbClient client = OrbOkHttpClient.builder() Alternately, set the environment with `ORB_API_KEY` or `ORB_WEBHOOK_SECRET`, and use `OrbOkHttpClient.fromEnv()` to read from the environment. ```java +import com.withorb.api.client.OrbClient; +import com.withorb.api.client.okhttp.OrbOkHttpClient; + OrbClient client = OrbOkHttpClient.fromEnv(); // Note: you can also call fromEnv() from the client builder, for example if you need to set additional properties @@ -76,8 +79,7 @@ Read the documentation for more configuration options. ### Example: creating a resource -To create a new customer, first use the `CustomerCreateParams` builder to specify attributes, -then pass that to the `create` method of the `customers` service. +To create a new customer, first use the `CustomerCreateParams` builder to specify attributes, then pass that to the `create` method of the `customers` service. ```java import com.withorb.api.models.Customer; @@ -92,12 +94,11 @@ Customer customer = client.customers().create(params); ### Example: listing resources -The Orb API provides a `list` method to get a paginated list of coupons. -You can retrieve the first page by: +The Orb API provides a `list` method to get a paginated list of coupons. You can retrieve the first page by: ```java import com.withorb.api.models.Coupon; -import com.withorb.api.models.Page; +import com.withorb.api.models.CouponListPage; CouponListPage page = client.coupons().list(); for (Coupon coupon : page.data()) { @@ -105,6 +106,30 @@ for (Coupon coupon : page.data()) { } ``` +Use the `CouponListParams` builder to set parameters: + +```java +import com.withorb.api.models.CouponListPage; +import com.withorb.api.models.CouponListParams; + +CouponListParams params = CouponListParams.builder() + .cursor("cursor") + .limit(1L) + .redemptionCode("redemption_code") + .showArchived(true) + .build(); +CouponListPage page1 = client.coupons().list(params); + +// Using the `from` method of the builder you can reuse previous params values: +CouponListPage page2 = client.coupons().list(CouponListParams.builder() + .from(params) + .nextCursor("abc123...") + .build()); + +// Or easily get params for the next page by using the helper `getNextPageParams`: +CouponListPage page3 = client.coupons().list(params.getNextPageParams(page2)); +``` + See [Pagination](#pagination) below for more information on transparently working with lists of objects without worrying about fetching each page. --- @@ -115,14 +140,14 @@ See [Pagination](#pagination) below for more information on transparently workin To make a request to the Orb API, you generally build an instance of the appropriate `Params` class. -In [Example: creating a resource](#example-creating-a-resource) above, we used the `CustomerCreateParams.builder()` to pass to -the `create` method of the `customers` service. +In [Example: creating a resource](#example-creating-a-resource) above, we used the `CustomerCreateParams.builder()` to pass to the `create` method of the `customers` service. -Sometimes, the API may support other properties that are not yet supported in the Java SDK types. In that case, -you can attach them using the `putAdditionalProperty` method. +Sometimes, the API may support other properties that are not yet supported in the Java SDK types. In that case, you can attach them using the `putAdditionalProperty` method. ```java -import com.withorb.api.models.core.JsonValue; +import com.withorb.api.core.JsonValue; +import com.withorb.api.models.CustomerCreateParams; + CustomerCreateParams params = CustomerCreateParams.builder() // ... normal properties .putAdditionalProperty("secret_param", JsonValue.from("4242")) @@ -136,15 +161,19 @@ CustomerCreateParams params = CustomerCreateParams.builder() When receiving a response, the Orb Java SDK will deserialize it into instances of the typed model classes. In rare cases, the API may return a response property that doesn't match the expected Java type. If you directly access the mistaken property, the SDK will throw an unchecked `OrbInvalidDataException` at runtime. If you would prefer to check in advance that that response is completely well-typed, call `.validate()` on the returned model. ```java +import com.withorb.api.models.Customer; + Customer customer = client.customers().create().validate(); ``` ### Response properties as JSON -In rare cases, you may want to access the underlying JSON value for a response property rather than using the typed version provided by -this SDK. Each model property has a corresponding JSON version, with an underscore before the method name, which returns a `JsonField` value. +In rare cases, you may want to access the underlying JSON value for a response property rather than using the typed version provided by this SDK. Each model property has a corresponding JSON version, with an underscore before the method name, which returns a `JsonField` value. ```java +import com.withorb.api.core.JsonField; +import java.util.Optional; + JsonField field = responseObj._field(); if (field.isMissing()) { @@ -166,6 +195,8 @@ if (field.isMissing()) { Sometimes, the server response may include additional properties that are not yet available in this library's types. You can access them using the model's `_additionalProperties` method: ```java +import com.withorb.api.core.JsonValue; + JsonValue secret = amountDiscount._additionalProperties().get("secret_field"); ``` @@ -173,17 +204,18 @@ JsonValue secret = amountDiscount._additionalProperties().get("secret_field"); ## Pagination -For methods that return a paginated list of results, this library provides convenient ways access -the results either one page at a time, or item-by-item across all pages. +For methods that return a paginated list of results, this library provides convenient ways access the results either one page at a time, or item-by-item across all pages. ### Auto-pagination -To iterate through all results across all pages, you can use `autoPager`, -which automatically handles fetching more pages for you: +To iterate through all results across all pages, you can use `autoPager`, which automatically handles fetching more pages for you: ### Synchronous ```java +import com.withorb.api.models.Coupon; +import com.withorb.api.models.CouponListPage; + // As an Iterable: CouponListPage page = client.coupons().list(params); for (Coupon coupon : page.autoPager()) { @@ -206,12 +238,12 @@ asyncClient.coupons().list(params).autoPager() ### Manual pagination -If none of the above helpers meet your needs, you can also manually request pages one-by-one. -A page of results has a `data()` method to fetch the list of objects, as well as top-level -`response` and other methods to fetch top-level data about the page. It also has methods -`hasNextPage`, `getNextPage`, and `getNextPageParams` methods to help with pagination. +If none of the above helpers meet your needs, you can also manually request pages one-by-one. A page of results has a `data()` method to fetch the list of objects, as well as top-level `response` and other methods to fetch top-level data about the page. It also has methods `hasNextPage`, `getNextPage`, and `getNextPageParams` methods to help with pagination. ```java +import com.withorb.api.models.Coupon; +import com.withorb.api.models.CouponListPage; + CouponListPage page = client.coupons().list(params); while (page != null) { for (Coupon coupon : page.data()) { @@ -242,31 +274,33 @@ This library throws exceptions in a single hierarchy for easy handling: - **`OrbException`** - Base exception for all exceptions - - **`OrbServiceException`** - HTTP errors with a well-formed response body we were able to parse. The exception message and the `.debuggingRequestId()` will be set by the server. +- **`OrbServiceException`** - HTTP errors with a well-formed response body we were able to parse. The exception message and the `.debuggingRequestId()` will be set by the server. - | 400 | BadRequestException | - | ------ | ----------------------------- | - | 401 | AuthenticationException | - | 403 | PermissionDeniedException | - | 404 | NotFoundException | - | 422 | UnprocessableEntityException | - | 429 | RateLimitException | - | 5xx | InternalServerException | - | others | UnexpectedStatusCodeException | + | 400 | BadRequestException | + | ------ | ----------------------------- | + | 401 | AuthenticationException | + | 403 | PermissionDeniedException | + | 404 | NotFoundException | + | 422 | UnprocessableEntityException | + | 429 | RateLimitException | + | 5xx | InternalServerException | + | others | UnexpectedStatusCodeException | - - **`OrbIoException`** - I/O networking errors - - **`OrbInvalidDataException`** - any other exceptions on the client side, e.g.: - - We failed to serialize the request body - - We failed to parse the response body (has access to response code and body) +- **`OrbIoException`** - I/O networking errors +- **`OrbInvalidDataException`** - any other exceptions on the client side, e.g.: + - We failed to serialize the request body + - We failed to parse the response body (has access to response code and body) ## Network options ### Retries -Requests that experience certain errors are automatically retried 2 times by default, with a short exponential backoff. Connection errors (for example, due to a network connectivity problem), 408 Request Timeout, 409 Conflict, 429 Rate Limit, and >=500 Internal errors will all be retried by default. -You can provide a `maxRetries` on the client builder to configure this: +Requests that experience certain errors are automatically retried 2 times by default, with a short exponential backoff. Connection errors (for example, due to a network connectivity problem), 408 Request Timeout, 409 Conflict, 429 Rate Limit, and >=500 Internal errors will all be retried by default. You can provide a `maxRetries` on the client builder to configure this: ```java +import com.withorb.api.client.OrbClient; +import com.withorb.api.client.okhttp.OrbOkHttpClient; + OrbClient client = OrbOkHttpClient.builder() .fromEnv() .maxRetries(4) @@ -278,6 +312,10 @@ OrbClient client = OrbOkHttpClient.builder() Requests time out after 1 minute by default. You can configure this on the client builder: ```java +import com.withorb.api.client.OrbClient; +import com.withorb.api.client.okhttp.OrbOkHttpClient; +import java.time.Duration; + OrbClient client = OrbOkHttpClient.builder() .fromEnv() .timeout(Duration.ofSeconds(30)) @@ -289,24 +327,24 @@ OrbClient client = OrbOkHttpClient.builder() Requests can be routed through a proxy. You can configure this on the client builder: ```java +import com.withorb.api.client.OrbClient; +import com.withorb.api.client.okhttp.OrbOkHttpClient; +import java.net.InetSocketAddress; +import java.net.Proxy; + OrbClient client = OrbOkHttpClient.builder() .fromEnv() - .proxy(new Proxy( - Type.HTTP, - new InetSocketAddress("proxy.com", 8080) - )) + .proxy(new Proxy(Proxy.Type.HTTP, new InetSocketAddress("example.com", 8080))) .build(); ``` ## Making custom/undocumented requests -This library is typed for convenient access to the documented API. If you need to access undocumented -params or response properties, the library can still be used. +This library is typed for convenient access to the documented API. If you need to access undocumented params or response properties, the library can still be used. ### Undocumented request params -To make requests using undocumented parameters, you can provide or override parameters on the params object -while building it. +To make requests using undocumented parameters, you can provide or override parameters on the params object while building it. ```kotlin FooCreateParams address = FooCreateParams.builder() @@ -317,10 +355,7 @@ FooCreateParams address = FooCreateParams.builder() ### Undocumented response properties -To access undocumented response properties, you can use `res._additionalProperties()` on a response object to -get a map of untyped fields of type `Map`. You can then access fields like -`._additionalProperties().get("secret_prop").asString()` or use other helpers defined on the `JsonValue` class -to extract it to a desired type. +To access undocumented response properties, you can use `res._additionalProperties()` on a response object to get a map of untyped fields of type `Map`. You can then access fields like `._additionalProperties().get("secret_prop").asString()` or use other helpers defined on the `JsonValue` class to extract it to a desired type. ## Logging diff --git a/build.gradle.kts b/build.gradle.kts index d59bd1c2..bc8fff11 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -4,7 +4,7 @@ plugins { allprojects { group = "com.withorb.api" - version = "0.13.0" // x-release-please-version + version = "0.14.0" // x-release-please-version } diff --git a/orb-java-core/src/main/kotlin/com/withorb/api/models/CustomerCreditListByExternalIdParams.kt b/orb-java-core/src/main/kotlin/com/withorb/api/models/CustomerCreditListByExternalIdParams.kt index 0b4023ff..d94e184c 100644 --- a/orb-java-core/src/main/kotlin/com/withorb/api/models/CustomerCreditListByExternalIdParams.kt +++ b/orb-java-core/src/main/kotlin/com/withorb/api/models/CustomerCreditListByExternalIdParams.kt @@ -98,7 +98,10 @@ constructor( */ fun cursor(cursor: String) = apply { this.cursor = cursor } - /** Include all blocks, not just active ones. */ + /** + * If set to True, all expired and depleted blocks, as well as active block will be + * returned. + */ fun includeAllBlocks(includeAllBlocks: Boolean) = apply { this.includeAllBlocks = includeAllBlocks } diff --git a/orb-java-core/src/main/kotlin/com/withorb/api/models/CustomerCreditListParams.kt b/orb-java-core/src/main/kotlin/com/withorb/api/models/CustomerCreditListParams.kt index e56c59c3..2229acb4 100644 --- a/orb-java-core/src/main/kotlin/com/withorb/api/models/CustomerCreditListParams.kt +++ b/orb-java-core/src/main/kotlin/com/withorb/api/models/CustomerCreditListParams.kt @@ -93,7 +93,10 @@ constructor( */ fun cursor(cursor: String) = apply { this.cursor = cursor } - /** Include all blocks, not just active ones. */ + /** + * If set to True, all expired and depleted blocks, as well as active block will be + * returned. + */ fun includeAllBlocks(includeAllBlocks: Boolean) = apply { this.includeAllBlocks = includeAllBlocks } diff --git a/orb-java-core/src/main/kotlin/com/withorb/api/services/async/customers/CreditServiceAsync.kt b/orb-java-core/src/main/kotlin/com/withorb/api/services/async/customers/CreditServiceAsync.kt index 501aa68a..06b18043 100644 --- a/orb-java-core/src/main/kotlin/com/withorb/api/services/async/customers/CreditServiceAsync.kt +++ b/orb-java-core/src/main/kotlin/com/withorb/api/services/async/customers/CreditServiceAsync.kt @@ -22,6 +22,9 @@ interface CreditServiceAsync { /** * Returns a paginated list of unexpired, non-zero credit blocks for a customer. * + * If `include_all_blocks` is set to `true`, all credit blocks (including expired and depleted + * blocks) will be included in the response. + * * Note that `currency` defaults to credits if not specified. To use a real world currency, set * `currency` to an ISO 4217 string. */ @@ -34,6 +37,9 @@ interface CreditServiceAsync { /** * Returns a paginated list of unexpired, non-zero credit blocks for a customer. * + * If `include_all_blocks` is set to `true`, all credit blocks (including expired and depleted + * blocks) will be included in the response. + * * Note that `currency` defaults to credits if not specified. To use a real world currency, set * `currency` to an ISO 4217 string. */ diff --git a/orb-java-core/src/main/kotlin/com/withorb/api/services/async/customers/CreditServiceAsyncImpl.kt b/orb-java-core/src/main/kotlin/com/withorb/api/services/async/customers/CreditServiceAsyncImpl.kt index a14fbf58..f75b0c68 100644 --- a/orb-java-core/src/main/kotlin/com/withorb/api/services/async/customers/CreditServiceAsyncImpl.kt +++ b/orb-java-core/src/main/kotlin/com/withorb/api/services/async/customers/CreditServiceAsyncImpl.kt @@ -43,6 +43,9 @@ constructor( /** * Returns a paginated list of unexpired, non-zero credit blocks for a customer. * + * If `include_all_blocks` is set to `true`, all credit blocks (including expired and depleted + * blocks) will be included in the response. + * * Note that `currency` defaults to credits if not specified. To use a real world currency, set * `currency` to an ISO 4217 string. */ @@ -79,6 +82,9 @@ constructor( /** * Returns a paginated list of unexpired, non-zero credit blocks for a customer. * + * If `include_all_blocks` is set to `true`, all credit blocks (including expired and depleted + * blocks) will be included in the response. + * * Note that `currency` defaults to credits if not specified. To use a real world currency, set * `currency` to an ISO 4217 string. */ diff --git a/orb-java-core/src/main/kotlin/com/withorb/api/services/blocking/customers/CreditService.kt b/orb-java-core/src/main/kotlin/com/withorb/api/services/blocking/customers/CreditService.kt index 49c8181c..1403480d 100644 --- a/orb-java-core/src/main/kotlin/com/withorb/api/services/blocking/customers/CreditService.kt +++ b/orb-java-core/src/main/kotlin/com/withorb/api/services/blocking/customers/CreditService.kt @@ -21,6 +21,9 @@ interface CreditService { /** * Returns a paginated list of unexpired, non-zero credit blocks for a customer. * + * If `include_all_blocks` is set to `true`, all credit blocks (including expired and depleted + * blocks) will be included in the response. + * * Note that `currency` defaults to credits if not specified. To use a real world currency, set * `currency` to an ISO 4217 string. */ @@ -33,6 +36,9 @@ interface CreditService { /** * Returns a paginated list of unexpired, non-zero credit blocks for a customer. * + * If `include_all_blocks` is set to `true`, all credit blocks (including expired and depleted + * blocks) will be included in the response. + * * Note that `currency` defaults to credits if not specified. To use a real world currency, set * `currency` to an ISO 4217 string. */ diff --git a/orb-java-core/src/main/kotlin/com/withorb/api/services/blocking/customers/CreditServiceImpl.kt b/orb-java-core/src/main/kotlin/com/withorb/api/services/blocking/customers/CreditServiceImpl.kt index 430f95c5..e49aa491 100644 --- a/orb-java-core/src/main/kotlin/com/withorb/api/services/blocking/customers/CreditServiceImpl.kt +++ b/orb-java-core/src/main/kotlin/com/withorb/api/services/blocking/customers/CreditServiceImpl.kt @@ -42,6 +42,9 @@ constructor( /** * Returns a paginated list of unexpired, non-zero credit blocks for a customer. * + * If `include_all_blocks` is set to `true`, all credit blocks (including expired and depleted + * blocks) will be included in the response. + * * Note that `currency` defaults to credits if not specified. To use a real world currency, set * `currency` to an ISO 4217 string. */ @@ -77,6 +80,9 @@ constructor( /** * Returns a paginated list of unexpired, non-zero credit blocks for a customer. * + * If `include_all_blocks` is set to `true`, all credit blocks (including expired and depleted + * blocks) will be included in the response. + * * Note that `currency` defaults to credits if not specified. To use a real world currency, set * `currency` to an ISO 4217 string. */ diff --git a/orb-java-core/src/test/kotlin/com/withorb/api/models/CustomerCreditLedgerCreateEntryByExternalIdParamsTest.kt b/orb-java-core/src/test/kotlin/com/withorb/api/models/CustomerCreditLedgerCreateEntryByExternalIdParamsTest.kt index 6fac02ec..2d1a5ace 100644 --- a/orb-java-core/src/test/kotlin/com/withorb/api/models/CustomerCreditLedgerCreateEntryByExternalIdParamsTest.kt +++ b/orb-java-core/src/test/kotlin/com/withorb/api/models/CustomerCreditLedgerCreateEntryByExternalIdParamsTest.kt @@ -12,7 +12,6 @@ class CustomerCreditLedgerCreateEntryByExternalIdParamsTest { @Test fun createCustomerCreditLedgerCreateEntryByExternalIdParams() { CustomerCreditLedgerCreateEntryByExternalIdParams.builder() - .externalCustomerId("external_customer_id") .forAddIncrementCreditLedgerEntryRequestParams( CustomerCreditLedgerCreateEntryByExternalIdParams .AddIncrementCreditLedgerEntryRequestParams @@ -50,6 +49,7 @@ class CustomerCreditLedgerCreateEntryByExternalIdParamsTest { .perUnitCostBasis("per_unit_cost_basis") .build() ) + .externalCustomerId("external_customer_id") .build() } diff --git a/orb-java-core/src/test/kotlin/com/withorb/api/models/CustomerCreditLedgerCreateEntryParamsTest.kt b/orb-java-core/src/test/kotlin/com/withorb/api/models/CustomerCreditLedgerCreateEntryParamsTest.kt index aec5a315..9f6fe0d7 100644 --- a/orb-java-core/src/test/kotlin/com/withorb/api/models/CustomerCreditLedgerCreateEntryParamsTest.kt +++ b/orb-java-core/src/test/kotlin/com/withorb/api/models/CustomerCreditLedgerCreateEntryParamsTest.kt @@ -12,7 +12,6 @@ class CustomerCreditLedgerCreateEntryParamsTest { @Test fun createCustomerCreditLedgerCreateEntryParams() { CustomerCreditLedgerCreateEntryParams.builder() - .customerId("customer_id") .forAddIncrementCreditLedgerEntryRequestParams( CustomerCreditLedgerCreateEntryParams.AddIncrementCreditLedgerEntryRequestParams .builder() @@ -49,6 +48,7 @@ class CustomerCreditLedgerCreateEntryParamsTest { .perUnitCostBasis("per_unit_cost_basis") .build() ) + .customerId("customer_id") .build() } diff --git a/orb-java-core/src/test/kotlin/com/withorb/api/services/blocking/customers/credits/LedgerServiceTest.kt b/orb-java-core/src/test/kotlin/com/withorb/api/services/blocking/customers/credits/LedgerServiceTest.kt index 86401f9f..a2c7c22f 100644 --- a/orb-java-core/src/test/kotlin/com/withorb/api/services/blocking/customers/credits/LedgerServiceTest.kt +++ b/orb-java-core/src/test/kotlin/com/withorb/api/services/blocking/customers/credits/LedgerServiceTest.kt @@ -43,7 +43,6 @@ class LedgerServiceTest { val customerCreditLedgerCreateEntryResponse = ledgerService.createEntry( CustomerCreditLedgerCreateEntryParams.builder() - .customerId("customer_id") .forAddIncrementCreditLedgerEntryRequestParams( CustomerCreditLedgerCreateEntryParams .AddIncrementCreditLedgerEntryRequestParams @@ -81,6 +80,7 @@ class LedgerServiceTest { .perUnitCostBasis("per_unit_cost_basis") .build() ) + .customerId("customer_id") .build() ) println(customerCreditLedgerCreateEntryResponse) @@ -97,7 +97,6 @@ class LedgerServiceTest { val customerCreditLedgerCreateEntryByExternalIdResponse = ledgerService.createEntryByExternalId( CustomerCreditLedgerCreateEntryByExternalIdParams.builder() - .externalCustomerId("external_customer_id") .forAddIncrementCreditLedgerEntryRequestParams( CustomerCreditLedgerCreateEntryByExternalIdParams .AddIncrementCreditLedgerEntryRequestParams @@ -135,6 +134,7 @@ class LedgerServiceTest { .perUnitCostBasis("per_unit_cost_basis") .build() ) + .externalCustomerId("external_customer_id") .build() ) println(customerCreditLedgerCreateEntryByExternalIdResponse)