diff --git a/.release-please-manifest.json b/.release-please-manifest.json
index 52b3e83..a899ac7 100644
--- a/.release-please-manifest.json
+++ b/.release-please-manifest.json
@@ -1,3 +1,3 @@
{
- ".": "0.1.0-alpha.30"
+ ".": "0.1.0-alpha.31"
}
\ No newline at end of file
diff --git a/.stats.yml b/.stats.yml
index d2dca25..aaa0f9f 100644
--- a/.stats.yml
+++ b/.stats.yml
@@ -1,4 +1,4 @@
-configured_endpoints: 16
-openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/brand-dev%2Fbrand.dev-98ef96cef5b06ad7a29dadba48258da7d9ea0a2b3938dc9e714ae06eb9afa1a3.yml
-openapi_spec_hash: 9e957a30999dff7d4ada925e437bd202
-config_hash: c3aaaa9794dba44d524c06591ab17894
+configured_endpoints: 20
+openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/brand-dev%2Fbrand.dev-2cdd67823c6ac9d1ab68032a695c31a098ad285ffb0c073b9dfc00afe5de9b88.yml
+openapi_spec_hash: ac8a965beb9b667b6204a5c573507219
+config_hash: 4cd3173ea1cce7183640aae49cfbb374
diff --git a/CHANGELOG.md b/CHANGELOG.md
index a3f1ad9..fa58760 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,13 @@
# Changelog
+## 0.1.0-alpha.31 (2026-02-23)
+
+Full Changelog: [v0.1.0-alpha.30...v0.1.0-alpha.31](https://github.com/brand-dot-dev/java-sdk/compare/v0.1.0-alpha.30...v0.1.0-alpha.31)
+
+### Features
+
+* **api:** manual updates ([18be8f5](https://github.com/brand-dot-dev/java-sdk/commit/18be8f5ca8d7c94220db6bca788537116da14505))
+
## 0.1.0-alpha.30 (2026-02-22)
Full Changelog: [v0.1.0-alpha.29...v0.1.0-alpha.30](https://github.com/brand-dot-dev/java-sdk/compare/v0.1.0-alpha.29...v0.1.0-alpha.30)
diff --git a/README.md b/README.md
index 60a78e7..4493560 100644
--- a/README.md
+++ b/README.md
@@ -2,8 +2,8 @@
-[](https://central.sonatype.com/artifact/com.branddev.api/brand-dev-java/0.1.0-alpha.30)
-[](https://javadoc.io/doc/com.branddev.api/brand-dev-java/0.1.0-alpha.30)
+[](https://central.sonatype.com/artifact/com.branddev.api/brand-dev-java/0.1.0-alpha.31)
+[](https://javadoc.io/doc/com.branddev.api/brand-dev-java/0.1.0-alpha.31)
@@ -22,7 +22,7 @@ Use the Brand Dev MCP Server to enable AI assistants to interact with this API,
-The REST API documentation can be found on [docs.brand.dev](https://docs.brand.dev/). Javadocs are available on [javadoc.io](https://javadoc.io/doc/com.branddev.api/brand-dev-java/0.1.0-alpha.30).
+The REST API documentation can be found on [docs.brand.dev](https://docs.brand.dev/). Javadocs are available on [javadoc.io](https://javadoc.io/doc/com.branddev.api/brand-dev-java/0.1.0-alpha.31).
@@ -33,7 +33,7 @@ The REST API documentation can be found on [docs.brand.dev](https://docs.brand.d
### Gradle
```kotlin
-implementation("com.branddev.api:brand-dev-java:0.1.0-alpha.30")
+implementation("com.branddev.api:brand-dev-java:0.1.0-alpha.31")
```
### Maven
@@ -42,7 +42,7 @@ implementation("com.branddev.api:brand-dev-java:0.1.0-alpha.30")
com.branddev.api
brand-dev-java
- 0.1.0-alpha.30
+ 0.1.0-alpha.31
```
diff --git a/brand-dev-java-core/src/main/kotlin/com/branddev/api/models/brand/BrandWebScrapeHtmlParams.kt b/brand-dev-java-core/src/main/kotlin/com/branddev/api/models/brand/BrandWebScrapeHtmlParams.kt
new file mode 100644
index 0000000..c142c76
--- /dev/null
+++ b/brand-dev-java-core/src/main/kotlin/com/branddev/api/models/brand/BrandWebScrapeHtmlParams.kt
@@ -0,0 +1,206 @@
+// File generated from our OpenAPI spec by Stainless.
+
+package com.branddev.api.models.brand
+
+import com.branddev.api.core.Params
+import com.branddev.api.core.checkRequired
+import com.branddev.api.core.http.Headers
+import com.branddev.api.core.http.QueryParams
+import java.util.Objects
+
+/**
+ * Scrapes the given URL and returns the raw HTML content of the page. Uses automatic proxy
+ * escalation to handle blocked sites.
+ */
+class BrandWebScrapeHtmlParams
+private constructor(
+ private val url: String,
+ private val additionalHeaders: Headers,
+ private val additionalQueryParams: QueryParams,
+) : Params {
+
+ /** Full URL to scrape (must include http:// or https:// protocol) */
+ fun url(): String = url
+
+ /** Additional headers to send with the request. */
+ fun _additionalHeaders(): Headers = additionalHeaders
+
+ /** Additional query param to send with the request. */
+ fun _additionalQueryParams(): QueryParams = additionalQueryParams
+
+ fun toBuilder() = Builder().from(this)
+
+ companion object {
+
+ /**
+ * Returns a mutable builder for constructing an instance of [BrandWebScrapeHtmlParams].
+ *
+ * The following fields are required:
+ * ```java
+ * .url()
+ * ```
+ */
+ @JvmStatic fun builder() = Builder()
+ }
+
+ /** A builder for [BrandWebScrapeHtmlParams]. */
+ class Builder internal constructor() {
+
+ private var url: String? = null
+ private var additionalHeaders: Headers.Builder = Headers.builder()
+ private var additionalQueryParams: QueryParams.Builder = QueryParams.builder()
+
+ @JvmSynthetic
+ internal fun from(brandWebScrapeHtmlParams: BrandWebScrapeHtmlParams) = apply {
+ url = brandWebScrapeHtmlParams.url
+ additionalHeaders = brandWebScrapeHtmlParams.additionalHeaders.toBuilder()
+ additionalQueryParams = brandWebScrapeHtmlParams.additionalQueryParams.toBuilder()
+ }
+
+ /** Full URL to scrape (must include http:// or https:// protocol) */
+ fun url(url: String) = apply { this.url = url }
+
+ fun additionalHeaders(additionalHeaders: Headers) = apply {
+ this.additionalHeaders.clear()
+ putAllAdditionalHeaders(additionalHeaders)
+ }
+
+ fun additionalHeaders(additionalHeaders: Map>) = apply {
+ this.additionalHeaders.clear()
+ putAllAdditionalHeaders(additionalHeaders)
+ }
+
+ fun putAdditionalHeader(name: String, value: String) = apply {
+ additionalHeaders.put(name, value)
+ }
+
+ fun putAdditionalHeaders(name: String, values: Iterable) = apply {
+ additionalHeaders.put(name, values)
+ }
+
+ fun putAllAdditionalHeaders(additionalHeaders: Headers) = apply {
+ this.additionalHeaders.putAll(additionalHeaders)
+ }
+
+ fun putAllAdditionalHeaders(additionalHeaders: Map>) = apply {
+ this.additionalHeaders.putAll(additionalHeaders)
+ }
+
+ fun replaceAdditionalHeaders(name: String, value: String) = apply {
+ additionalHeaders.replace(name, value)
+ }
+
+ fun replaceAdditionalHeaders(name: String, values: Iterable) = apply {
+ additionalHeaders.replace(name, values)
+ }
+
+ fun replaceAllAdditionalHeaders(additionalHeaders: Headers) = apply {
+ this.additionalHeaders.replaceAll(additionalHeaders)
+ }
+
+ fun replaceAllAdditionalHeaders(additionalHeaders: Map>) = apply {
+ this.additionalHeaders.replaceAll(additionalHeaders)
+ }
+
+ fun removeAdditionalHeaders(name: String) = apply { additionalHeaders.remove(name) }
+
+ fun removeAllAdditionalHeaders(names: Set) = apply {
+ additionalHeaders.removeAll(names)
+ }
+
+ fun additionalQueryParams(additionalQueryParams: QueryParams) = apply {
+ this.additionalQueryParams.clear()
+ putAllAdditionalQueryParams(additionalQueryParams)
+ }
+
+ fun additionalQueryParams(additionalQueryParams: Map>) = apply {
+ this.additionalQueryParams.clear()
+ putAllAdditionalQueryParams(additionalQueryParams)
+ }
+
+ fun putAdditionalQueryParam(key: String, value: String) = apply {
+ additionalQueryParams.put(key, value)
+ }
+
+ fun putAdditionalQueryParams(key: String, values: Iterable) = apply {
+ additionalQueryParams.put(key, values)
+ }
+
+ fun putAllAdditionalQueryParams(additionalQueryParams: QueryParams) = apply {
+ this.additionalQueryParams.putAll(additionalQueryParams)
+ }
+
+ fun putAllAdditionalQueryParams(additionalQueryParams: Map>) =
+ apply {
+ this.additionalQueryParams.putAll(additionalQueryParams)
+ }
+
+ fun replaceAdditionalQueryParams(key: String, value: String) = apply {
+ additionalQueryParams.replace(key, value)
+ }
+
+ fun replaceAdditionalQueryParams(key: String, values: Iterable) = apply {
+ additionalQueryParams.replace(key, values)
+ }
+
+ fun replaceAllAdditionalQueryParams(additionalQueryParams: QueryParams) = apply {
+ this.additionalQueryParams.replaceAll(additionalQueryParams)
+ }
+
+ fun replaceAllAdditionalQueryParams(additionalQueryParams: Map>) =
+ apply {
+ this.additionalQueryParams.replaceAll(additionalQueryParams)
+ }
+
+ fun removeAdditionalQueryParams(key: String) = apply { additionalQueryParams.remove(key) }
+
+ fun removeAllAdditionalQueryParams(keys: Set) = apply {
+ additionalQueryParams.removeAll(keys)
+ }
+
+ /**
+ * Returns an immutable instance of [BrandWebScrapeHtmlParams].
+ *
+ * Further updates to this [Builder] will not mutate the returned instance.
+ *
+ * The following fields are required:
+ * ```java
+ * .url()
+ * ```
+ *
+ * @throws IllegalStateException if any required field is unset.
+ */
+ fun build(): BrandWebScrapeHtmlParams =
+ BrandWebScrapeHtmlParams(
+ checkRequired("url", url),
+ additionalHeaders.build(),
+ additionalQueryParams.build(),
+ )
+ }
+
+ override fun _headers(): Headers = additionalHeaders
+
+ override fun _queryParams(): QueryParams =
+ QueryParams.builder()
+ .apply {
+ put("url", url)
+ putAll(additionalQueryParams)
+ }
+ .build()
+
+ override fun equals(other: Any?): Boolean {
+ if (this === other) {
+ return true
+ }
+
+ return other is BrandWebScrapeHtmlParams &&
+ url == other.url &&
+ additionalHeaders == other.additionalHeaders &&
+ additionalQueryParams == other.additionalQueryParams
+ }
+
+ override fun hashCode(): Int = Objects.hash(url, additionalHeaders, additionalQueryParams)
+
+ override fun toString() =
+ "BrandWebScrapeHtmlParams{url=$url, additionalHeaders=$additionalHeaders, additionalQueryParams=$additionalQueryParams}"
+}
diff --git a/brand-dev-java-core/src/main/kotlin/com/branddev/api/models/brand/BrandWebScrapeHtmlResponse.kt b/brand-dev-java-core/src/main/kotlin/com/branddev/api/models/brand/BrandWebScrapeHtmlResponse.kt
new file mode 100644
index 0000000..8549928
--- /dev/null
+++ b/brand-dev-java-core/src/main/kotlin/com/branddev/api/models/brand/BrandWebScrapeHtmlResponse.kt
@@ -0,0 +1,368 @@
+// File generated from our OpenAPI spec by Stainless.
+
+package com.branddev.api.models.brand
+
+import com.branddev.api.core.Enum
+import com.branddev.api.core.ExcludeMissing
+import com.branddev.api.core.JsonField
+import com.branddev.api.core.JsonMissing
+import com.branddev.api.core.JsonValue
+import com.branddev.api.core.checkRequired
+import com.branddev.api.errors.BrandDevInvalidDataException
+import com.fasterxml.jackson.annotation.JsonAnyGetter
+import com.fasterxml.jackson.annotation.JsonAnySetter
+import com.fasterxml.jackson.annotation.JsonCreator
+import com.fasterxml.jackson.annotation.JsonProperty
+import java.util.Collections
+import java.util.Objects
+import kotlin.jvm.optionals.getOrNull
+
+class BrandWebScrapeHtmlResponse
+@JsonCreator(mode = JsonCreator.Mode.DISABLED)
+private constructor(
+ private val html: JsonField,
+ private val success: JsonField,
+ private val url: JsonField,
+ private val additionalProperties: MutableMap,
+) {
+
+ @JsonCreator
+ private constructor(
+ @JsonProperty("html") @ExcludeMissing html: JsonField = JsonMissing.of(),
+ @JsonProperty("success") @ExcludeMissing success: JsonField = JsonMissing.of(),
+ @JsonProperty("url") @ExcludeMissing url: JsonField = JsonMissing.of(),
+ ) : this(html, success, url, mutableMapOf())
+
+ /**
+ * Raw HTML content of the page
+ *
+ * @throws BrandDevInvalidDataException if the JSON field has an unexpected type or is
+ * unexpectedly missing or null (e.g. if the server responded with an unexpected value).
+ */
+ fun html(): String = html.getRequired("html")
+
+ /**
+ * Indicates success
+ *
+ * @throws BrandDevInvalidDataException if the JSON field has an unexpected type or is
+ * unexpectedly missing or null (e.g. if the server responded with an unexpected value).
+ */
+ fun success(): Success = success.getRequired("success")
+
+ /**
+ * The URL that was scraped
+ *
+ * @throws BrandDevInvalidDataException if the JSON field has an unexpected type or is
+ * unexpectedly missing or null (e.g. if the server responded with an unexpected value).
+ */
+ fun url(): String = url.getRequired("url")
+
+ /**
+ * Returns the raw JSON value of [html].
+ *
+ * Unlike [html], this method doesn't throw if the JSON field has an unexpected type.
+ */
+ @JsonProperty("html") @ExcludeMissing fun _html(): JsonField = html
+
+ /**
+ * Returns the raw JSON value of [success].
+ *
+ * Unlike [success], this method doesn't throw if the JSON field has an unexpected type.
+ */
+ @JsonProperty("success") @ExcludeMissing fun _success(): JsonField = success
+
+ /**
+ * Returns the raw JSON value of [url].
+ *
+ * Unlike [url], this method doesn't throw if the JSON field has an unexpected type.
+ */
+ @JsonProperty("url") @ExcludeMissing fun _url(): JsonField = url
+
+ @JsonAnySetter
+ private fun putAdditionalProperty(key: String, value: JsonValue) {
+ additionalProperties.put(key, value)
+ }
+
+ @JsonAnyGetter
+ @ExcludeMissing
+ fun _additionalProperties(): Map =
+ Collections.unmodifiableMap(additionalProperties)
+
+ fun toBuilder() = Builder().from(this)
+
+ companion object {
+
+ /**
+ * Returns a mutable builder for constructing an instance of [BrandWebScrapeHtmlResponse].
+ *
+ * The following fields are required:
+ * ```java
+ * .html()
+ * .success()
+ * .url()
+ * ```
+ */
+ @JvmStatic fun builder() = Builder()
+ }
+
+ /** A builder for [BrandWebScrapeHtmlResponse]. */
+ class Builder internal constructor() {
+
+ private var html: JsonField? = null
+ private var success: JsonField? = null
+ private var url: JsonField? = null
+ private var additionalProperties: MutableMap = mutableMapOf()
+
+ @JvmSynthetic
+ internal fun from(brandWebScrapeHtmlResponse: BrandWebScrapeHtmlResponse) = apply {
+ html = brandWebScrapeHtmlResponse.html
+ success = brandWebScrapeHtmlResponse.success
+ url = brandWebScrapeHtmlResponse.url
+ additionalProperties = brandWebScrapeHtmlResponse.additionalProperties.toMutableMap()
+ }
+
+ /** Raw HTML content of the page */
+ fun html(html: String) = html(JsonField.of(html))
+
+ /**
+ * Sets [Builder.html] to an arbitrary JSON value.
+ *
+ * You should usually call [Builder.html] with a well-typed [String] value instead. This
+ * method is primarily for setting the field to an undocumented or not yet supported value.
+ */
+ fun html(html: JsonField) = apply { this.html = html }
+
+ /** Indicates success */
+ fun success(success: Success) = success(JsonField.of(success))
+
+ /**
+ * Sets [Builder.success] to an arbitrary JSON value.
+ *
+ * You should usually call [Builder.success] with a well-typed [Success] value instead. This
+ * method is primarily for setting the field to an undocumented or not yet supported value.
+ */
+ fun success(success: JsonField) = apply { this.success = success }
+
+ /** The URL that was scraped */
+ fun url(url: String) = url(JsonField.of(url))
+
+ /**
+ * Sets [Builder.url] to an arbitrary JSON value.
+ *
+ * You should usually call [Builder.url] with a well-typed [String] value instead. This
+ * method is primarily for setting the field to an undocumented or not yet supported value.
+ */
+ fun url(url: JsonField) = apply { this.url = url }
+
+ fun additionalProperties(additionalProperties: Map) = apply {
+ this.additionalProperties.clear()
+ putAllAdditionalProperties(additionalProperties)
+ }
+
+ fun putAdditionalProperty(key: String, value: JsonValue) = apply {
+ additionalProperties.put(key, value)
+ }
+
+ fun putAllAdditionalProperties(additionalProperties: Map) = apply {
+ this.additionalProperties.putAll(additionalProperties)
+ }
+
+ fun removeAdditionalProperty(key: String) = apply { additionalProperties.remove(key) }
+
+ fun removeAllAdditionalProperties(keys: Set) = apply {
+ keys.forEach(::removeAdditionalProperty)
+ }
+
+ /**
+ * Returns an immutable instance of [BrandWebScrapeHtmlResponse].
+ *
+ * Further updates to this [Builder] will not mutate the returned instance.
+ *
+ * The following fields are required:
+ * ```java
+ * .html()
+ * .success()
+ * .url()
+ * ```
+ *
+ * @throws IllegalStateException if any required field is unset.
+ */
+ fun build(): BrandWebScrapeHtmlResponse =
+ BrandWebScrapeHtmlResponse(
+ checkRequired("html", html),
+ checkRequired("success", success),
+ checkRequired("url", url),
+ additionalProperties.toMutableMap(),
+ )
+ }
+
+ private var validated: Boolean = false
+
+ fun validate(): BrandWebScrapeHtmlResponse = apply {
+ if (validated) {
+ return@apply
+ }
+
+ html()
+ success().validate()
+ url()
+ validated = true
+ }
+
+ fun isValid(): Boolean =
+ try {
+ validate()
+ true
+ } catch (e: BrandDevInvalidDataException) {
+ false
+ }
+
+ /**
+ * Returns a score indicating how many valid values are contained in this object recursively.
+ *
+ * Used for best match union deserialization.
+ */
+ @JvmSynthetic
+ internal fun validity(): Int =
+ (if (html.asKnown().isPresent) 1 else 0) +
+ (success.asKnown().getOrNull()?.validity() ?: 0) +
+ (if (url.asKnown().isPresent) 1 else 0)
+
+ /** Indicates success */
+ class Success @JsonCreator private constructor(private val value: JsonField) : Enum {
+
+ /**
+ * Returns this class instance's raw value.
+ *
+ * This is usually only useful if this instance was deserialized from data that doesn't
+ * match any known member, and you want to know that value. For example, if the SDK is on an
+ * older version than the API, then the API may respond with new members that the SDK is
+ * unaware of.
+ */
+ @com.fasterxml.jackson.annotation.JsonValue fun _value(): JsonField = value
+
+ companion object {
+
+ @JvmField val TRUE = of(true)
+
+ @JvmStatic fun of(value: Boolean) = Success(JsonField.of(value))
+ }
+
+ /** An enum containing [Success]'s known values. */
+ enum class Known {
+ TRUE
+ }
+
+ /**
+ * An enum containing [Success]'s known values, as well as an [_UNKNOWN] member.
+ *
+ * An instance of [Success] can contain an unknown value in a couple of cases:
+ * - It was deserialized from data that doesn't match any known member. For example, if the
+ * SDK is on an older version than the API, then the API may respond with new members that
+ * the SDK is unaware of.
+ * - It was constructed with an arbitrary value using the [of] method.
+ */
+ enum class Value {
+ TRUE,
+ /** An enum member indicating that [Success] was instantiated with an unknown value. */
+ _UNKNOWN,
+ }
+
+ /**
+ * Returns an enum member corresponding to this class instance's value, or [Value._UNKNOWN]
+ * if the class was instantiated with an unknown value.
+ *
+ * Use the [known] method instead if you're certain the value is always known or if you want
+ * to throw for the unknown case.
+ */
+ fun value(): Value =
+ when (this) {
+ TRUE -> Value.TRUE
+ else -> Value._UNKNOWN
+ }
+
+ /**
+ * Returns an enum member corresponding to this class instance's value.
+ *
+ * Use the [value] method instead if you're uncertain the value is always known and don't
+ * want to throw for the unknown case.
+ *
+ * @throws BrandDevInvalidDataException if this class instance's value is a not a known
+ * member.
+ */
+ fun known(): Known =
+ when (this) {
+ TRUE -> Known.TRUE
+ else -> throw BrandDevInvalidDataException("Unknown Success: $value")
+ }
+
+ /**
+ * Returns this class instance's primitive wire representation.
+ *
+ * @throws BrandDevInvalidDataException if this class instance's value does not have the
+ * expected primitive type.
+ */
+ fun asBoolean(): Boolean =
+ _value().asBoolean().orElseThrow {
+ BrandDevInvalidDataException("Value is not a Boolean")
+ }
+
+ private var validated: Boolean = false
+
+ fun validate(): Success = apply {
+ if (validated) {
+ return@apply
+ }
+
+ known()
+ validated = true
+ }
+
+ fun isValid(): Boolean =
+ try {
+ validate()
+ true
+ } catch (e: BrandDevInvalidDataException) {
+ false
+ }
+
+ /**
+ * Returns a score indicating how many valid values are contained in this object
+ * recursively.
+ *
+ * Used for best match union deserialization.
+ */
+ @JvmSynthetic internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1
+
+ override fun equals(other: Any?): Boolean {
+ if (this === other) {
+ return true
+ }
+
+ return other is Success && value == other.value
+ }
+
+ override fun hashCode() = value.hashCode()
+
+ override fun toString() = value.toString()
+ }
+
+ override fun equals(other: Any?): Boolean {
+ if (this === other) {
+ return true
+ }
+
+ return other is BrandWebScrapeHtmlResponse &&
+ html == other.html &&
+ success == other.success &&
+ url == other.url &&
+ additionalProperties == other.additionalProperties
+ }
+
+ private val hashCode: Int by lazy { Objects.hash(html, success, url, additionalProperties) }
+
+ override fun hashCode(): Int = hashCode
+
+ override fun toString() =
+ "BrandWebScrapeHtmlResponse{html=$html, success=$success, url=$url, additionalProperties=$additionalProperties}"
+}
diff --git a/brand-dev-java-core/src/main/kotlin/com/branddev/api/models/brand/BrandWebScrapeImagesParams.kt b/brand-dev-java-core/src/main/kotlin/com/branddev/api/models/brand/BrandWebScrapeImagesParams.kt
new file mode 100644
index 0000000..c4ca24f
--- /dev/null
+++ b/brand-dev-java-core/src/main/kotlin/com/branddev/api/models/brand/BrandWebScrapeImagesParams.kt
@@ -0,0 +1,206 @@
+// File generated from our OpenAPI spec by Stainless.
+
+package com.branddev.api.models.brand
+
+import com.branddev.api.core.Params
+import com.branddev.api.core.checkRequired
+import com.branddev.api.core.http.Headers
+import com.branddev.api.core.http.QueryParams
+import java.util.Objects
+
+/**
+ * Scrapes all images from the given URL. Extracts images from img, svg, picture/source, link, and
+ * video elements including inline SVGs, base64 data URIs, and standard URLs.
+ */
+class BrandWebScrapeImagesParams
+private constructor(
+ private val url: String,
+ private val additionalHeaders: Headers,
+ private val additionalQueryParams: QueryParams,
+) : Params {
+
+ /** Full URL to scrape images from (must include http:// or https:// protocol) */
+ fun url(): String = url
+
+ /** Additional headers to send with the request. */
+ fun _additionalHeaders(): Headers = additionalHeaders
+
+ /** Additional query param to send with the request. */
+ fun _additionalQueryParams(): QueryParams = additionalQueryParams
+
+ fun toBuilder() = Builder().from(this)
+
+ companion object {
+
+ /**
+ * Returns a mutable builder for constructing an instance of [BrandWebScrapeImagesParams].
+ *
+ * The following fields are required:
+ * ```java
+ * .url()
+ * ```
+ */
+ @JvmStatic fun builder() = Builder()
+ }
+
+ /** A builder for [BrandWebScrapeImagesParams]. */
+ class Builder internal constructor() {
+
+ private var url: String? = null
+ private var additionalHeaders: Headers.Builder = Headers.builder()
+ private var additionalQueryParams: QueryParams.Builder = QueryParams.builder()
+
+ @JvmSynthetic
+ internal fun from(brandWebScrapeImagesParams: BrandWebScrapeImagesParams) = apply {
+ url = brandWebScrapeImagesParams.url
+ additionalHeaders = brandWebScrapeImagesParams.additionalHeaders.toBuilder()
+ additionalQueryParams = brandWebScrapeImagesParams.additionalQueryParams.toBuilder()
+ }
+
+ /** Full URL to scrape images from (must include http:// or https:// protocol) */
+ fun url(url: String) = apply { this.url = url }
+
+ fun additionalHeaders(additionalHeaders: Headers) = apply {
+ this.additionalHeaders.clear()
+ putAllAdditionalHeaders(additionalHeaders)
+ }
+
+ fun additionalHeaders(additionalHeaders: Map>) = apply {
+ this.additionalHeaders.clear()
+ putAllAdditionalHeaders(additionalHeaders)
+ }
+
+ fun putAdditionalHeader(name: String, value: String) = apply {
+ additionalHeaders.put(name, value)
+ }
+
+ fun putAdditionalHeaders(name: String, values: Iterable) = apply {
+ additionalHeaders.put(name, values)
+ }
+
+ fun putAllAdditionalHeaders(additionalHeaders: Headers) = apply {
+ this.additionalHeaders.putAll(additionalHeaders)
+ }
+
+ fun putAllAdditionalHeaders(additionalHeaders: Map>) = apply {
+ this.additionalHeaders.putAll(additionalHeaders)
+ }
+
+ fun replaceAdditionalHeaders(name: String, value: String) = apply {
+ additionalHeaders.replace(name, value)
+ }
+
+ fun replaceAdditionalHeaders(name: String, values: Iterable) = apply {
+ additionalHeaders.replace(name, values)
+ }
+
+ fun replaceAllAdditionalHeaders(additionalHeaders: Headers) = apply {
+ this.additionalHeaders.replaceAll(additionalHeaders)
+ }
+
+ fun replaceAllAdditionalHeaders(additionalHeaders: Map>) = apply {
+ this.additionalHeaders.replaceAll(additionalHeaders)
+ }
+
+ fun removeAdditionalHeaders(name: String) = apply { additionalHeaders.remove(name) }
+
+ fun removeAllAdditionalHeaders(names: Set) = apply {
+ additionalHeaders.removeAll(names)
+ }
+
+ fun additionalQueryParams(additionalQueryParams: QueryParams) = apply {
+ this.additionalQueryParams.clear()
+ putAllAdditionalQueryParams(additionalQueryParams)
+ }
+
+ fun additionalQueryParams(additionalQueryParams: Map>) = apply {
+ this.additionalQueryParams.clear()
+ putAllAdditionalQueryParams(additionalQueryParams)
+ }
+
+ fun putAdditionalQueryParam(key: String, value: String) = apply {
+ additionalQueryParams.put(key, value)
+ }
+
+ fun putAdditionalQueryParams(key: String, values: Iterable) = apply {
+ additionalQueryParams.put(key, values)
+ }
+
+ fun putAllAdditionalQueryParams(additionalQueryParams: QueryParams) = apply {
+ this.additionalQueryParams.putAll(additionalQueryParams)
+ }
+
+ fun putAllAdditionalQueryParams(additionalQueryParams: Map>) =
+ apply {
+ this.additionalQueryParams.putAll(additionalQueryParams)
+ }
+
+ fun replaceAdditionalQueryParams(key: String, value: String) = apply {
+ additionalQueryParams.replace(key, value)
+ }
+
+ fun replaceAdditionalQueryParams(key: String, values: Iterable) = apply {
+ additionalQueryParams.replace(key, values)
+ }
+
+ fun replaceAllAdditionalQueryParams(additionalQueryParams: QueryParams) = apply {
+ this.additionalQueryParams.replaceAll(additionalQueryParams)
+ }
+
+ fun replaceAllAdditionalQueryParams(additionalQueryParams: Map>) =
+ apply {
+ this.additionalQueryParams.replaceAll(additionalQueryParams)
+ }
+
+ fun removeAdditionalQueryParams(key: String) = apply { additionalQueryParams.remove(key) }
+
+ fun removeAllAdditionalQueryParams(keys: Set) = apply {
+ additionalQueryParams.removeAll(keys)
+ }
+
+ /**
+ * Returns an immutable instance of [BrandWebScrapeImagesParams].
+ *
+ * Further updates to this [Builder] will not mutate the returned instance.
+ *
+ * The following fields are required:
+ * ```java
+ * .url()
+ * ```
+ *
+ * @throws IllegalStateException if any required field is unset.
+ */
+ fun build(): BrandWebScrapeImagesParams =
+ BrandWebScrapeImagesParams(
+ checkRequired("url", url),
+ additionalHeaders.build(),
+ additionalQueryParams.build(),
+ )
+ }
+
+ override fun _headers(): Headers = additionalHeaders
+
+ override fun _queryParams(): QueryParams =
+ QueryParams.builder()
+ .apply {
+ put("url", url)
+ putAll(additionalQueryParams)
+ }
+ .build()
+
+ override fun equals(other: Any?): Boolean {
+ if (this === other) {
+ return true
+ }
+
+ return other is BrandWebScrapeImagesParams &&
+ url == other.url &&
+ additionalHeaders == other.additionalHeaders &&
+ additionalQueryParams == other.additionalQueryParams
+ }
+
+ override fun hashCode(): Int = Objects.hash(url, additionalHeaders, additionalQueryParams)
+
+ override fun toString() =
+ "BrandWebScrapeImagesParams{url=$url, additionalHeaders=$additionalHeaders, additionalQueryParams=$additionalQueryParams}"
+}
diff --git a/brand-dev-java-core/src/main/kotlin/com/branddev/api/models/brand/BrandWebScrapeImagesResponse.kt b/brand-dev-java-core/src/main/kotlin/com/branddev/api/models/brand/BrandWebScrapeImagesResponse.kt
new file mode 100644
index 0000000..32833c2
--- /dev/null
+++ b/brand-dev-java-core/src/main/kotlin/com/branddev/api/models/brand/BrandWebScrapeImagesResponse.kt
@@ -0,0 +1,946 @@
+// File generated from our OpenAPI spec by Stainless.
+
+package com.branddev.api.models.brand
+
+import com.branddev.api.core.Enum
+import com.branddev.api.core.ExcludeMissing
+import com.branddev.api.core.JsonField
+import com.branddev.api.core.JsonMissing
+import com.branddev.api.core.JsonValue
+import com.branddev.api.core.checkKnown
+import com.branddev.api.core.checkRequired
+import com.branddev.api.core.toImmutable
+import com.branddev.api.errors.BrandDevInvalidDataException
+import com.fasterxml.jackson.annotation.JsonAnyGetter
+import com.fasterxml.jackson.annotation.JsonAnySetter
+import com.fasterxml.jackson.annotation.JsonCreator
+import com.fasterxml.jackson.annotation.JsonProperty
+import java.util.Collections
+import java.util.Objects
+import java.util.Optional
+import kotlin.jvm.optionals.getOrNull
+
+class BrandWebScrapeImagesResponse
+@JsonCreator(mode = JsonCreator.Mode.DISABLED)
+private constructor(
+ private val images: JsonField>,
+ private val success: JsonField,
+ private val url: JsonField,
+ private val additionalProperties: MutableMap,
+) {
+
+ @JsonCreator
+ private constructor(
+ @JsonProperty("images") @ExcludeMissing images: JsonField> = JsonMissing.of(),
+ @JsonProperty("success") @ExcludeMissing success: JsonField = JsonMissing.of(),
+ @JsonProperty("url") @ExcludeMissing url: JsonField = JsonMissing.of(),
+ ) : this(images, success, url, mutableMapOf())
+
+ /**
+ * Array of scraped images
+ *
+ * @throws BrandDevInvalidDataException if the JSON field has an unexpected type or is
+ * unexpectedly missing or null (e.g. if the server responded with an unexpected value).
+ */
+ fun images(): List = images.getRequired("images")
+
+ /**
+ * Indicates success
+ *
+ * @throws BrandDevInvalidDataException if the JSON field has an unexpected type or is
+ * unexpectedly missing or null (e.g. if the server responded with an unexpected value).
+ */
+ fun success(): Success = success.getRequired("success")
+
+ /**
+ * The URL that was scraped
+ *
+ * @throws BrandDevInvalidDataException if the JSON field has an unexpected type or is
+ * unexpectedly missing or null (e.g. if the server responded with an unexpected value).
+ */
+ fun url(): String = url.getRequired("url")
+
+ /**
+ * Returns the raw JSON value of [images].
+ *
+ * Unlike [images], this method doesn't throw if the JSON field has an unexpected type.
+ */
+ @JsonProperty("images") @ExcludeMissing fun _images(): JsonField> = images
+
+ /**
+ * Returns the raw JSON value of [success].
+ *
+ * Unlike [success], this method doesn't throw if the JSON field has an unexpected type.
+ */
+ @JsonProperty("success") @ExcludeMissing fun _success(): JsonField = success
+
+ /**
+ * Returns the raw JSON value of [url].
+ *
+ * Unlike [url], this method doesn't throw if the JSON field has an unexpected type.
+ */
+ @JsonProperty("url") @ExcludeMissing fun _url(): JsonField = url
+
+ @JsonAnySetter
+ private fun putAdditionalProperty(key: String, value: JsonValue) {
+ additionalProperties.put(key, value)
+ }
+
+ @JsonAnyGetter
+ @ExcludeMissing
+ fun _additionalProperties(): Map =
+ Collections.unmodifiableMap(additionalProperties)
+
+ fun toBuilder() = Builder().from(this)
+
+ companion object {
+
+ /**
+ * Returns a mutable builder for constructing an instance of [BrandWebScrapeImagesResponse].
+ *
+ * The following fields are required:
+ * ```java
+ * .images()
+ * .success()
+ * .url()
+ * ```
+ */
+ @JvmStatic fun builder() = Builder()
+ }
+
+ /** A builder for [BrandWebScrapeImagesResponse]. */
+ class Builder internal constructor() {
+
+ private var images: JsonField>? = null
+ private var success: JsonField? = null
+ private var url: JsonField? = null
+ private var additionalProperties: MutableMap = mutableMapOf()
+
+ @JvmSynthetic
+ internal fun from(brandWebScrapeImagesResponse: BrandWebScrapeImagesResponse) = apply {
+ images = brandWebScrapeImagesResponse.images.map { it.toMutableList() }
+ success = brandWebScrapeImagesResponse.success
+ url = brandWebScrapeImagesResponse.url
+ additionalProperties = brandWebScrapeImagesResponse.additionalProperties.toMutableMap()
+ }
+
+ /** Array of scraped images */
+ fun images(images: List) = images(JsonField.of(images))
+
+ /**
+ * Sets [Builder.images] to an arbitrary JSON value.
+ *
+ * You should usually call [Builder.images] with a well-typed `List` value instead.
+ * This method is primarily for setting the field to an undocumented or not yet supported
+ * value.
+ */
+ fun images(images: JsonField>) = apply {
+ this.images = images.map { it.toMutableList() }
+ }
+
+ /**
+ * Adds a single [Image] to [images].
+ *
+ * @throws IllegalStateException if the field was previously set to a non-list.
+ */
+ fun addImage(image: Image) = apply {
+ images =
+ (images ?: JsonField.of(mutableListOf())).also {
+ checkKnown("images", it).add(image)
+ }
+ }
+
+ /** Indicates success */
+ fun success(success: Success) = success(JsonField.of(success))
+
+ /**
+ * Sets [Builder.success] to an arbitrary JSON value.
+ *
+ * You should usually call [Builder.success] with a well-typed [Success] value instead. This
+ * method is primarily for setting the field to an undocumented or not yet supported value.
+ */
+ fun success(success: JsonField) = apply { this.success = success }
+
+ /** The URL that was scraped */
+ fun url(url: String) = url(JsonField.of(url))
+
+ /**
+ * Sets [Builder.url] to an arbitrary JSON value.
+ *
+ * You should usually call [Builder.url] with a well-typed [String] value instead. This
+ * method is primarily for setting the field to an undocumented or not yet supported value.
+ */
+ fun url(url: JsonField) = apply { this.url = url }
+
+ fun additionalProperties(additionalProperties: Map) = apply {
+ this.additionalProperties.clear()
+ putAllAdditionalProperties(additionalProperties)
+ }
+
+ fun putAdditionalProperty(key: String, value: JsonValue) = apply {
+ additionalProperties.put(key, value)
+ }
+
+ fun putAllAdditionalProperties(additionalProperties: Map) = apply {
+ this.additionalProperties.putAll(additionalProperties)
+ }
+
+ fun removeAdditionalProperty(key: String) = apply { additionalProperties.remove(key) }
+
+ fun removeAllAdditionalProperties(keys: Set) = apply {
+ keys.forEach(::removeAdditionalProperty)
+ }
+
+ /**
+ * Returns an immutable instance of [BrandWebScrapeImagesResponse].
+ *
+ * Further updates to this [Builder] will not mutate the returned instance.
+ *
+ * The following fields are required:
+ * ```java
+ * .images()
+ * .success()
+ * .url()
+ * ```
+ *
+ * @throws IllegalStateException if any required field is unset.
+ */
+ fun build(): BrandWebScrapeImagesResponse =
+ BrandWebScrapeImagesResponse(
+ checkRequired("images", images).map { it.toImmutable() },
+ checkRequired("success", success),
+ checkRequired("url", url),
+ additionalProperties.toMutableMap(),
+ )
+ }
+
+ private var validated: Boolean = false
+
+ fun validate(): BrandWebScrapeImagesResponse = apply {
+ if (validated) {
+ return@apply
+ }
+
+ images().forEach { it.validate() }
+ success().validate()
+ url()
+ validated = true
+ }
+
+ fun isValid(): Boolean =
+ try {
+ validate()
+ true
+ } catch (e: BrandDevInvalidDataException) {
+ false
+ }
+
+ /**
+ * Returns a score indicating how many valid values are contained in this object recursively.
+ *
+ * Used for best match union deserialization.
+ */
+ @JvmSynthetic
+ internal fun validity(): Int =
+ (images.asKnown().getOrNull()?.sumOf { it.validity().toInt() } ?: 0) +
+ (success.asKnown().getOrNull()?.validity() ?: 0) +
+ (if (url.asKnown().isPresent) 1 else 0)
+
+ class Image
+ @JsonCreator(mode = JsonCreator.Mode.DISABLED)
+ private constructor(
+ private val alt: JsonField,
+ private val element: JsonField,
+ private val src: JsonField,
+ private val type: JsonField,
+ private val additionalProperties: MutableMap,
+ ) {
+
+ @JsonCreator
+ private constructor(
+ @JsonProperty("alt") @ExcludeMissing alt: JsonField = JsonMissing.of(),
+ @JsonProperty("element") @ExcludeMissing element: JsonField = JsonMissing.of(),
+ @JsonProperty("src") @ExcludeMissing src: JsonField = JsonMissing.of(),
+ @JsonProperty("type") @ExcludeMissing type: JsonField = JsonMissing.of(),
+ ) : this(alt, element, src, type, mutableMapOf())
+
+ /**
+ * Alt text of the image, or null if not present
+ *
+ * @throws BrandDevInvalidDataException if the JSON field has an unexpected type (e.g. if
+ * the server responded with an unexpected value).
+ */
+ fun alt(): Optional = alt.getOptional("alt")
+
+ /**
+ * The HTML element the image was found in
+ *
+ * @throws BrandDevInvalidDataException if the JSON field has an unexpected type or is
+ * unexpectedly missing or null (e.g. if the server responded with an unexpected value).
+ */
+ fun element(): Element = element.getRequired("element")
+
+ /**
+ * The image source - can be a URL, inline HTML (for SVGs), or a base64 data URI
+ *
+ * @throws BrandDevInvalidDataException if the JSON field has an unexpected type or is
+ * unexpectedly missing or null (e.g. if the server responded with an unexpected value).
+ */
+ fun src(): String = src.getRequired("src")
+
+ /**
+ * The type/format of the src value
+ *
+ * @throws BrandDevInvalidDataException if the JSON field has an unexpected type or is
+ * unexpectedly missing or null (e.g. if the server responded with an unexpected value).
+ */
+ fun type(): Type = type.getRequired("type")
+
+ /**
+ * Returns the raw JSON value of [alt].
+ *
+ * Unlike [alt], this method doesn't throw if the JSON field has an unexpected type.
+ */
+ @JsonProperty("alt") @ExcludeMissing fun _alt(): JsonField = alt
+
+ /**
+ * Returns the raw JSON value of [element].
+ *
+ * Unlike [element], this method doesn't throw if the JSON field has an unexpected type.
+ */
+ @JsonProperty("element") @ExcludeMissing fun _element(): JsonField = element
+
+ /**
+ * Returns the raw JSON value of [src].
+ *
+ * Unlike [src], this method doesn't throw if the JSON field has an unexpected type.
+ */
+ @JsonProperty("src") @ExcludeMissing fun _src(): JsonField = src
+
+ /**
+ * Returns the raw JSON value of [type].
+ *
+ * Unlike [type], this method doesn't throw if the JSON field has an unexpected type.
+ */
+ @JsonProperty("type") @ExcludeMissing fun _type(): JsonField = type
+
+ @JsonAnySetter
+ private fun putAdditionalProperty(key: String, value: JsonValue) {
+ additionalProperties.put(key, value)
+ }
+
+ @JsonAnyGetter
+ @ExcludeMissing
+ fun _additionalProperties(): Map =
+ Collections.unmodifiableMap(additionalProperties)
+
+ fun toBuilder() = Builder().from(this)
+
+ companion object {
+
+ /**
+ * Returns a mutable builder for constructing an instance of [Image].
+ *
+ * The following fields are required:
+ * ```java
+ * .alt()
+ * .element()
+ * .src()
+ * .type()
+ * ```
+ */
+ @JvmStatic fun builder() = Builder()
+ }
+
+ /** A builder for [Image]. */
+ class Builder internal constructor() {
+
+ private var alt: JsonField? = null
+ private var element: JsonField? = null
+ private var src: JsonField? = null
+ private var type: JsonField? = null
+ private var additionalProperties: MutableMap = mutableMapOf()
+
+ @JvmSynthetic
+ internal fun from(image: Image) = apply {
+ alt = image.alt
+ element = image.element
+ src = image.src
+ type = image.type
+ additionalProperties = image.additionalProperties.toMutableMap()
+ }
+
+ /** Alt text of the image, or null if not present */
+ fun alt(alt: String?) = alt(JsonField.ofNullable(alt))
+
+ /** Alias for calling [Builder.alt] with `alt.orElse(null)`. */
+ fun alt(alt: Optional) = alt(alt.getOrNull())
+
+ /**
+ * Sets [Builder.alt] to an arbitrary JSON value.
+ *
+ * You should usually call [Builder.alt] with a well-typed [String] value instead. This
+ * method is primarily for setting the field to an undocumented or not yet supported
+ * value.
+ */
+ fun alt(alt: JsonField) = apply { this.alt = alt }
+
+ /** The HTML element the image was found in */
+ fun element(element: Element) = element(JsonField.of(element))
+
+ /**
+ * Sets [Builder.element] to an arbitrary JSON value.
+ *
+ * You should usually call [Builder.element] with a well-typed [Element] value instead.
+ * This method is primarily for setting the field to an undocumented or not yet
+ * supported value.
+ */
+ fun element(element: JsonField) = apply { this.element = element }
+
+ /** The image source - can be a URL, inline HTML (for SVGs), or a base64 data URI */
+ fun src(src: String) = src(JsonField.of(src))
+
+ /**
+ * Sets [Builder.src] to an arbitrary JSON value.
+ *
+ * You should usually call [Builder.src] with a well-typed [String] value instead. This
+ * method is primarily for setting the field to an undocumented or not yet supported
+ * value.
+ */
+ fun src(src: JsonField) = apply { this.src = src }
+
+ /** The type/format of the src value */
+ fun type(type: Type) = type(JsonField.of(type))
+
+ /**
+ * Sets [Builder.type] to an arbitrary JSON value.
+ *
+ * You should usually call [Builder.type] with a well-typed [Type] value instead. This
+ * method is primarily for setting the field to an undocumented or not yet supported
+ * value.
+ */
+ fun type(type: JsonField) = apply { this.type = type }
+
+ fun additionalProperties(additionalProperties: Map) = apply {
+ this.additionalProperties.clear()
+ putAllAdditionalProperties(additionalProperties)
+ }
+
+ fun putAdditionalProperty(key: String, value: JsonValue) = apply {
+ additionalProperties.put(key, value)
+ }
+
+ fun putAllAdditionalProperties(additionalProperties: Map) = apply {
+ this.additionalProperties.putAll(additionalProperties)
+ }
+
+ fun removeAdditionalProperty(key: String) = apply { additionalProperties.remove(key) }
+
+ fun removeAllAdditionalProperties(keys: Set) = apply {
+ keys.forEach(::removeAdditionalProperty)
+ }
+
+ /**
+ * Returns an immutable instance of [Image].
+ *
+ * Further updates to this [Builder] will not mutate the returned instance.
+ *
+ * The following fields are required:
+ * ```java
+ * .alt()
+ * .element()
+ * .src()
+ * .type()
+ * ```
+ *
+ * @throws IllegalStateException if any required field is unset.
+ */
+ fun build(): Image =
+ Image(
+ checkRequired("alt", alt),
+ checkRequired("element", element),
+ checkRequired("src", src),
+ checkRequired("type", type),
+ additionalProperties.toMutableMap(),
+ )
+ }
+
+ private var validated: Boolean = false
+
+ fun validate(): Image = apply {
+ if (validated) {
+ return@apply
+ }
+
+ alt()
+ element().validate()
+ src()
+ type().validate()
+ validated = true
+ }
+
+ fun isValid(): Boolean =
+ try {
+ validate()
+ true
+ } catch (e: BrandDevInvalidDataException) {
+ false
+ }
+
+ /**
+ * Returns a score indicating how many valid values are contained in this object
+ * recursively.
+ *
+ * Used for best match union deserialization.
+ */
+ @JvmSynthetic
+ internal fun validity(): Int =
+ (if (alt.asKnown().isPresent) 1 else 0) +
+ (element.asKnown().getOrNull()?.validity() ?: 0) +
+ (if (src.asKnown().isPresent) 1 else 0) +
+ (type.asKnown().getOrNull()?.validity() ?: 0)
+
+ /** The HTML element the image was found in */
+ class Element @JsonCreator private constructor(private val value: JsonField) :
+ Enum {
+
+ /**
+ * Returns this class instance's raw value.
+ *
+ * This is usually only useful if this instance was deserialized from data that doesn't
+ * match any known member, and you want to know that value. For example, if the SDK is
+ * on an older version than the API, then the API may respond with new members that the
+ * SDK is unaware of.
+ */
+ @com.fasterxml.jackson.annotation.JsonValue fun _value(): JsonField = value
+
+ companion object {
+
+ @JvmField val IMG = of("img")
+
+ @JvmField val SVG = of("svg")
+
+ @JvmField val LINK = of("link")
+
+ @JvmField val SOURCE = of("source")
+
+ @JvmField val VIDEO = of("video")
+
+ @JvmStatic fun of(value: String) = Element(JsonField.of(value))
+ }
+
+ /** An enum containing [Element]'s known values. */
+ enum class Known {
+ IMG,
+ SVG,
+ LINK,
+ SOURCE,
+ VIDEO,
+ }
+
+ /**
+ * An enum containing [Element]'s known values, as well as an [_UNKNOWN] member.
+ *
+ * An instance of [Element] can contain an unknown value in a couple of cases:
+ * - It was deserialized from data that doesn't match any known member. For example, if
+ * the SDK is on an older version than the API, then the API may respond with new
+ * members that the SDK is unaware of.
+ * - It was constructed with an arbitrary value using the [of] method.
+ */
+ enum class Value {
+ IMG,
+ SVG,
+ LINK,
+ SOURCE,
+ VIDEO,
+ /**
+ * An enum member indicating that [Element] was instantiated with an unknown value.
+ */
+ _UNKNOWN,
+ }
+
+ /**
+ * Returns an enum member corresponding to this class instance's value, or
+ * [Value._UNKNOWN] if the class was instantiated with an unknown value.
+ *
+ * Use the [known] method instead if you're certain the value is always known or if you
+ * want to throw for the unknown case.
+ */
+ fun value(): Value =
+ when (this) {
+ IMG -> Value.IMG
+ SVG -> Value.SVG
+ LINK -> Value.LINK
+ SOURCE -> Value.SOURCE
+ VIDEO -> Value.VIDEO
+ else -> Value._UNKNOWN
+ }
+
+ /**
+ * Returns an enum member corresponding to this class instance's value.
+ *
+ * Use the [value] method instead if you're uncertain the value is always known and
+ * don't want to throw for the unknown case.
+ *
+ * @throws BrandDevInvalidDataException if this class instance's value is a not a known
+ * member.
+ */
+ fun known(): Known =
+ when (this) {
+ IMG -> Known.IMG
+ SVG -> Known.SVG
+ LINK -> Known.LINK
+ SOURCE -> Known.SOURCE
+ VIDEO -> Known.VIDEO
+ else -> throw BrandDevInvalidDataException("Unknown Element: $value")
+ }
+
+ /**
+ * Returns this class instance's primitive wire representation.
+ *
+ * This differs from the [toString] method because that method is primarily for
+ * debugging and generally doesn't throw.
+ *
+ * @throws BrandDevInvalidDataException if this class instance's value does not have the
+ * expected primitive type.
+ */
+ fun asString(): String =
+ _value().asString().orElseThrow {
+ BrandDevInvalidDataException("Value is not a String")
+ }
+
+ private var validated: Boolean = false
+
+ fun validate(): Element = apply {
+ if (validated) {
+ return@apply
+ }
+
+ known()
+ validated = true
+ }
+
+ fun isValid(): Boolean =
+ try {
+ validate()
+ true
+ } catch (e: BrandDevInvalidDataException) {
+ false
+ }
+
+ /**
+ * Returns a score indicating how many valid values are contained in this object
+ * recursively.
+ *
+ * Used for best match union deserialization.
+ */
+ @JvmSynthetic internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1
+
+ override fun equals(other: Any?): Boolean {
+ if (this === other) {
+ return true
+ }
+
+ return other is Element && value == other.value
+ }
+
+ override fun hashCode() = value.hashCode()
+
+ override fun toString() = value.toString()
+ }
+
+ /** The type/format of the src value */
+ class Type @JsonCreator private constructor(private val value: JsonField) : Enum {
+
+ /**
+ * Returns this class instance's raw value.
+ *
+ * This is usually only useful if this instance was deserialized from data that doesn't
+ * match any known member, and you want to know that value. For example, if the SDK is
+ * on an older version than the API, then the API may respond with new members that the
+ * SDK is unaware of.
+ */
+ @com.fasterxml.jackson.annotation.JsonValue fun _value(): JsonField = value
+
+ companion object {
+
+ @JvmField val URL = of("url")
+
+ @JvmField val HTML = of("html")
+
+ @JvmField val BASE64 = of("base64")
+
+ @JvmStatic fun of(value: String) = Type(JsonField.of(value))
+ }
+
+ /** An enum containing [Type]'s known values. */
+ enum class Known {
+ URL,
+ HTML,
+ BASE64,
+ }
+
+ /**
+ * An enum containing [Type]'s known values, as well as an [_UNKNOWN] member.
+ *
+ * An instance of [Type] can contain an unknown value in a couple of cases:
+ * - It was deserialized from data that doesn't match any known member. For example, if
+ * the SDK is on an older version than the API, then the API may respond with new
+ * members that the SDK is unaware of.
+ * - It was constructed with an arbitrary value using the [of] method.
+ */
+ enum class Value {
+ URL,
+ HTML,
+ BASE64,
+ /** An enum member indicating that [Type] was instantiated with an unknown value. */
+ _UNKNOWN,
+ }
+
+ /**
+ * Returns an enum member corresponding to this class instance's value, or
+ * [Value._UNKNOWN] if the class was instantiated with an unknown value.
+ *
+ * Use the [known] method instead if you're certain the value is always known or if you
+ * want to throw for the unknown case.
+ */
+ fun value(): Value =
+ when (this) {
+ URL -> Value.URL
+ HTML -> Value.HTML
+ BASE64 -> Value.BASE64
+ else -> Value._UNKNOWN
+ }
+
+ /**
+ * Returns an enum member corresponding to this class instance's value.
+ *
+ * Use the [value] method instead if you're uncertain the value is always known and
+ * don't want to throw for the unknown case.
+ *
+ * @throws BrandDevInvalidDataException if this class instance's value is a not a known
+ * member.
+ */
+ fun known(): Known =
+ when (this) {
+ URL -> Known.URL
+ HTML -> Known.HTML
+ BASE64 -> Known.BASE64
+ else -> throw BrandDevInvalidDataException("Unknown Type: $value")
+ }
+
+ /**
+ * Returns this class instance's primitive wire representation.
+ *
+ * This differs from the [toString] method because that method is primarily for
+ * debugging and generally doesn't throw.
+ *
+ * @throws BrandDevInvalidDataException if this class instance's value does not have the
+ * expected primitive type.
+ */
+ fun asString(): String =
+ _value().asString().orElseThrow {
+ BrandDevInvalidDataException("Value is not a String")
+ }
+
+ private var validated: Boolean = false
+
+ fun validate(): Type = apply {
+ if (validated) {
+ return@apply
+ }
+
+ known()
+ validated = true
+ }
+
+ fun isValid(): Boolean =
+ try {
+ validate()
+ true
+ } catch (e: BrandDevInvalidDataException) {
+ false
+ }
+
+ /**
+ * Returns a score indicating how many valid values are contained in this object
+ * recursively.
+ *
+ * Used for best match union deserialization.
+ */
+ @JvmSynthetic internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1
+
+ override fun equals(other: Any?): Boolean {
+ if (this === other) {
+ return true
+ }
+
+ return other is Type && value == other.value
+ }
+
+ override fun hashCode() = value.hashCode()
+
+ override fun toString() = value.toString()
+ }
+
+ override fun equals(other: Any?): Boolean {
+ if (this === other) {
+ return true
+ }
+
+ return other is Image &&
+ alt == other.alt &&
+ element == other.element &&
+ src == other.src &&
+ type == other.type &&
+ additionalProperties == other.additionalProperties
+ }
+
+ private val hashCode: Int by lazy {
+ Objects.hash(alt, element, src, type, additionalProperties)
+ }
+
+ override fun hashCode(): Int = hashCode
+
+ override fun toString() =
+ "Image{alt=$alt, element=$element, src=$src, type=$type, additionalProperties=$additionalProperties}"
+ }
+
+ /** Indicates success */
+ class Success @JsonCreator private constructor(private val value: JsonField) : Enum {
+
+ /**
+ * Returns this class instance's raw value.
+ *
+ * This is usually only useful if this instance was deserialized from data that doesn't
+ * match any known member, and you want to know that value. For example, if the SDK is on an
+ * older version than the API, then the API may respond with new members that the SDK is
+ * unaware of.
+ */
+ @com.fasterxml.jackson.annotation.JsonValue fun _value(): JsonField = value
+
+ companion object {
+
+ @JvmField val TRUE = of(true)
+
+ @JvmStatic fun of(value: Boolean) = Success(JsonField.of(value))
+ }
+
+ /** An enum containing [Success]'s known values. */
+ enum class Known {
+ TRUE
+ }
+
+ /**
+ * An enum containing [Success]'s known values, as well as an [_UNKNOWN] member.
+ *
+ * An instance of [Success] can contain an unknown value in a couple of cases:
+ * - It was deserialized from data that doesn't match any known member. For example, if the
+ * SDK is on an older version than the API, then the API may respond with new members that
+ * the SDK is unaware of.
+ * - It was constructed with an arbitrary value using the [of] method.
+ */
+ enum class Value {
+ TRUE,
+ /** An enum member indicating that [Success] was instantiated with an unknown value. */
+ _UNKNOWN,
+ }
+
+ /**
+ * Returns an enum member corresponding to this class instance's value, or [Value._UNKNOWN]
+ * if the class was instantiated with an unknown value.
+ *
+ * Use the [known] method instead if you're certain the value is always known or if you want
+ * to throw for the unknown case.
+ */
+ fun value(): Value =
+ when (this) {
+ TRUE -> Value.TRUE
+ else -> Value._UNKNOWN
+ }
+
+ /**
+ * Returns an enum member corresponding to this class instance's value.
+ *
+ * Use the [value] method instead if you're uncertain the value is always known and don't
+ * want to throw for the unknown case.
+ *
+ * @throws BrandDevInvalidDataException if this class instance's value is a not a known
+ * member.
+ */
+ fun known(): Known =
+ when (this) {
+ TRUE -> Known.TRUE
+ else -> throw BrandDevInvalidDataException("Unknown Success: $value")
+ }
+
+ /**
+ * Returns this class instance's primitive wire representation.
+ *
+ * @throws BrandDevInvalidDataException if this class instance's value does not have the
+ * expected primitive type.
+ */
+ fun asBoolean(): Boolean =
+ _value().asBoolean().orElseThrow {
+ BrandDevInvalidDataException("Value is not a Boolean")
+ }
+
+ private var validated: Boolean = false
+
+ fun validate(): Success = apply {
+ if (validated) {
+ return@apply
+ }
+
+ known()
+ validated = true
+ }
+
+ fun isValid(): Boolean =
+ try {
+ validate()
+ true
+ } catch (e: BrandDevInvalidDataException) {
+ false
+ }
+
+ /**
+ * Returns a score indicating how many valid values are contained in this object
+ * recursively.
+ *
+ * Used for best match union deserialization.
+ */
+ @JvmSynthetic internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1
+
+ override fun equals(other: Any?): Boolean {
+ if (this === other) {
+ return true
+ }
+
+ return other is Success && value == other.value
+ }
+
+ override fun hashCode() = value.hashCode()
+
+ override fun toString() = value.toString()
+ }
+
+ override fun equals(other: Any?): Boolean {
+ if (this === other) {
+ return true
+ }
+
+ return other is BrandWebScrapeImagesResponse &&
+ images == other.images &&
+ success == other.success &&
+ url == other.url &&
+ additionalProperties == other.additionalProperties
+ }
+
+ private val hashCode: Int by lazy { Objects.hash(images, success, url, additionalProperties) }
+
+ override fun hashCode(): Int = hashCode
+
+ override fun toString() =
+ "BrandWebScrapeImagesResponse{images=$images, success=$success, url=$url, additionalProperties=$additionalProperties}"
+}
diff --git a/brand-dev-java-core/src/main/kotlin/com/branddev/api/models/brand/BrandWebScrapeMdParams.kt b/brand-dev-java-core/src/main/kotlin/com/branddev/api/models/brand/BrandWebScrapeMdParams.kt
new file mode 100644
index 0000000..cd17fca
--- /dev/null
+++ b/brand-dev-java-core/src/main/kotlin/com/branddev/api/models/brand/BrandWebScrapeMdParams.kt
@@ -0,0 +1,256 @@
+// File generated from our OpenAPI spec by Stainless.
+
+package com.branddev.api.models.brand
+
+import com.branddev.api.core.Params
+import com.branddev.api.core.checkRequired
+import com.branddev.api.core.http.Headers
+import com.branddev.api.core.http.QueryParams
+import java.util.Objects
+import java.util.Optional
+import kotlin.jvm.optionals.getOrNull
+
+/**
+ * Scrapes the given URL, converts the HTML content to GitHub Flavored Markdown (GFM), and returns
+ * the result. Uses automatic proxy escalation to handle blocked sites.
+ */
+class BrandWebScrapeMdParams
+private constructor(
+ private val url: String,
+ private val includeImages: Boolean?,
+ private val includeLinks: Boolean?,
+ private val additionalHeaders: Headers,
+ private val additionalQueryParams: QueryParams,
+) : Params {
+
+ /** Full URL to scrape and convert to markdown (must include http:// or https:// protocol) */
+ fun url(): String = url
+
+ /** Include image references in Markdown output */
+ fun includeImages(): Optional = Optional.ofNullable(includeImages)
+
+ /** Preserve hyperlinks in Markdown output */
+ fun includeLinks(): Optional = Optional.ofNullable(includeLinks)
+
+ /** Additional headers to send with the request. */
+ fun _additionalHeaders(): Headers = additionalHeaders
+
+ /** Additional query param to send with the request. */
+ fun _additionalQueryParams(): QueryParams = additionalQueryParams
+
+ fun toBuilder() = Builder().from(this)
+
+ companion object {
+
+ /**
+ * Returns a mutable builder for constructing an instance of [BrandWebScrapeMdParams].
+ *
+ * The following fields are required:
+ * ```java
+ * .url()
+ * ```
+ */
+ @JvmStatic fun builder() = Builder()
+ }
+
+ /** A builder for [BrandWebScrapeMdParams]. */
+ class Builder internal constructor() {
+
+ private var url: String? = null
+ private var includeImages: Boolean? = null
+ private var includeLinks: Boolean? = null
+ private var additionalHeaders: Headers.Builder = Headers.builder()
+ private var additionalQueryParams: QueryParams.Builder = QueryParams.builder()
+
+ @JvmSynthetic
+ internal fun from(brandWebScrapeMdParams: BrandWebScrapeMdParams) = apply {
+ url = brandWebScrapeMdParams.url
+ includeImages = brandWebScrapeMdParams.includeImages
+ includeLinks = brandWebScrapeMdParams.includeLinks
+ additionalHeaders = brandWebScrapeMdParams.additionalHeaders.toBuilder()
+ additionalQueryParams = brandWebScrapeMdParams.additionalQueryParams.toBuilder()
+ }
+
+ /**
+ * Full URL to scrape and convert to markdown (must include http:// or https:// protocol)
+ */
+ fun url(url: String) = apply { this.url = url }
+
+ /** Include image references in Markdown output */
+ fun includeImages(includeImages: Boolean?) = apply { this.includeImages = includeImages }
+
+ /**
+ * Alias for [Builder.includeImages].
+ *
+ * This unboxed primitive overload exists for backwards compatibility.
+ */
+ fun includeImages(includeImages: Boolean) = includeImages(includeImages as Boolean?)
+
+ /** Alias for calling [Builder.includeImages] with `includeImages.orElse(null)`. */
+ fun includeImages(includeImages: Optional) =
+ includeImages(includeImages.getOrNull())
+
+ /** Preserve hyperlinks in Markdown output */
+ fun includeLinks(includeLinks: Boolean?) = apply { this.includeLinks = includeLinks }
+
+ /**
+ * Alias for [Builder.includeLinks].
+ *
+ * This unboxed primitive overload exists for backwards compatibility.
+ */
+ fun includeLinks(includeLinks: Boolean) = includeLinks(includeLinks as Boolean?)
+
+ /** Alias for calling [Builder.includeLinks] with `includeLinks.orElse(null)`. */
+ fun includeLinks(includeLinks: Optional) = includeLinks(includeLinks.getOrNull())
+
+ fun additionalHeaders(additionalHeaders: Headers) = apply {
+ this.additionalHeaders.clear()
+ putAllAdditionalHeaders(additionalHeaders)
+ }
+
+ fun additionalHeaders(additionalHeaders: Map>) = apply {
+ this.additionalHeaders.clear()
+ putAllAdditionalHeaders(additionalHeaders)
+ }
+
+ fun putAdditionalHeader(name: String, value: String) = apply {
+ additionalHeaders.put(name, value)
+ }
+
+ fun putAdditionalHeaders(name: String, values: Iterable) = apply {
+ additionalHeaders.put(name, values)
+ }
+
+ fun putAllAdditionalHeaders(additionalHeaders: Headers) = apply {
+ this.additionalHeaders.putAll(additionalHeaders)
+ }
+
+ fun putAllAdditionalHeaders(additionalHeaders: Map>) = apply {
+ this.additionalHeaders.putAll(additionalHeaders)
+ }
+
+ fun replaceAdditionalHeaders(name: String, value: String) = apply {
+ additionalHeaders.replace(name, value)
+ }
+
+ fun replaceAdditionalHeaders(name: String, values: Iterable) = apply {
+ additionalHeaders.replace(name, values)
+ }
+
+ fun replaceAllAdditionalHeaders(additionalHeaders: Headers) = apply {
+ this.additionalHeaders.replaceAll(additionalHeaders)
+ }
+
+ fun replaceAllAdditionalHeaders(additionalHeaders: Map>) = apply {
+ this.additionalHeaders.replaceAll(additionalHeaders)
+ }
+
+ fun removeAdditionalHeaders(name: String) = apply { additionalHeaders.remove(name) }
+
+ fun removeAllAdditionalHeaders(names: Set) = apply {
+ additionalHeaders.removeAll(names)
+ }
+
+ fun additionalQueryParams(additionalQueryParams: QueryParams) = apply {
+ this.additionalQueryParams.clear()
+ putAllAdditionalQueryParams(additionalQueryParams)
+ }
+
+ fun additionalQueryParams(additionalQueryParams: Map>) = apply {
+ this.additionalQueryParams.clear()
+ putAllAdditionalQueryParams(additionalQueryParams)
+ }
+
+ fun putAdditionalQueryParam(key: String, value: String) = apply {
+ additionalQueryParams.put(key, value)
+ }
+
+ fun putAdditionalQueryParams(key: String, values: Iterable) = apply {
+ additionalQueryParams.put(key, values)
+ }
+
+ fun putAllAdditionalQueryParams(additionalQueryParams: QueryParams) = apply {
+ this.additionalQueryParams.putAll(additionalQueryParams)
+ }
+
+ fun putAllAdditionalQueryParams(additionalQueryParams: Map>) =
+ apply {
+ this.additionalQueryParams.putAll(additionalQueryParams)
+ }
+
+ fun replaceAdditionalQueryParams(key: String, value: String) = apply {
+ additionalQueryParams.replace(key, value)
+ }
+
+ fun replaceAdditionalQueryParams(key: String, values: Iterable) = apply {
+ additionalQueryParams.replace(key, values)
+ }
+
+ fun replaceAllAdditionalQueryParams(additionalQueryParams: QueryParams) = apply {
+ this.additionalQueryParams.replaceAll(additionalQueryParams)
+ }
+
+ fun replaceAllAdditionalQueryParams(additionalQueryParams: Map>) =
+ apply {
+ this.additionalQueryParams.replaceAll(additionalQueryParams)
+ }
+
+ fun removeAdditionalQueryParams(key: String) = apply { additionalQueryParams.remove(key) }
+
+ fun removeAllAdditionalQueryParams(keys: Set) = apply {
+ additionalQueryParams.removeAll(keys)
+ }
+
+ /**
+ * Returns an immutable instance of [BrandWebScrapeMdParams].
+ *
+ * Further updates to this [Builder] will not mutate the returned instance.
+ *
+ * The following fields are required:
+ * ```java
+ * .url()
+ * ```
+ *
+ * @throws IllegalStateException if any required field is unset.
+ */
+ fun build(): BrandWebScrapeMdParams =
+ BrandWebScrapeMdParams(
+ checkRequired("url", url),
+ includeImages,
+ includeLinks,
+ additionalHeaders.build(),
+ additionalQueryParams.build(),
+ )
+ }
+
+ override fun _headers(): Headers = additionalHeaders
+
+ override fun _queryParams(): QueryParams =
+ QueryParams.builder()
+ .apply {
+ put("url", url)
+ includeImages?.let { put("includeImages", it.toString()) }
+ includeLinks?.let { put("includeLinks", it.toString()) }
+ putAll(additionalQueryParams)
+ }
+ .build()
+
+ override fun equals(other: Any?): Boolean {
+ if (this === other) {
+ return true
+ }
+
+ return other is BrandWebScrapeMdParams &&
+ url == other.url &&
+ includeImages == other.includeImages &&
+ includeLinks == other.includeLinks &&
+ additionalHeaders == other.additionalHeaders &&
+ additionalQueryParams == other.additionalQueryParams
+ }
+
+ override fun hashCode(): Int =
+ Objects.hash(url, includeImages, includeLinks, additionalHeaders, additionalQueryParams)
+
+ override fun toString() =
+ "BrandWebScrapeMdParams{url=$url, includeImages=$includeImages, includeLinks=$includeLinks, additionalHeaders=$additionalHeaders, additionalQueryParams=$additionalQueryParams}"
+}
diff --git a/brand-dev-java-core/src/main/kotlin/com/branddev/api/models/brand/BrandWebScrapeMdResponse.kt b/brand-dev-java-core/src/main/kotlin/com/branddev/api/models/brand/BrandWebScrapeMdResponse.kt
new file mode 100644
index 0000000..378b818
--- /dev/null
+++ b/brand-dev-java-core/src/main/kotlin/com/branddev/api/models/brand/BrandWebScrapeMdResponse.kt
@@ -0,0 +1,368 @@
+// File generated from our OpenAPI spec by Stainless.
+
+package com.branddev.api.models.brand
+
+import com.branddev.api.core.Enum
+import com.branddev.api.core.ExcludeMissing
+import com.branddev.api.core.JsonField
+import com.branddev.api.core.JsonMissing
+import com.branddev.api.core.JsonValue
+import com.branddev.api.core.checkRequired
+import com.branddev.api.errors.BrandDevInvalidDataException
+import com.fasterxml.jackson.annotation.JsonAnyGetter
+import com.fasterxml.jackson.annotation.JsonAnySetter
+import com.fasterxml.jackson.annotation.JsonCreator
+import com.fasterxml.jackson.annotation.JsonProperty
+import java.util.Collections
+import java.util.Objects
+import kotlin.jvm.optionals.getOrNull
+
+class BrandWebScrapeMdResponse
+@JsonCreator(mode = JsonCreator.Mode.DISABLED)
+private constructor(
+ private val markdown: JsonField,
+ private val success: JsonField,
+ private val url: JsonField,
+ private val additionalProperties: MutableMap,
+) {
+
+ @JsonCreator
+ private constructor(
+ @JsonProperty("markdown") @ExcludeMissing markdown: JsonField = JsonMissing.of(),
+ @JsonProperty("success") @ExcludeMissing success: JsonField = JsonMissing.of(),
+ @JsonProperty("url") @ExcludeMissing url: JsonField = JsonMissing.of(),
+ ) : this(markdown, success, url, mutableMapOf())
+
+ /**
+ * Page content converted to GitHub Flavored Markdown
+ *
+ * @throws BrandDevInvalidDataException if the JSON field has an unexpected type or is
+ * unexpectedly missing or null (e.g. if the server responded with an unexpected value).
+ */
+ fun markdown(): String = markdown.getRequired("markdown")
+
+ /**
+ * Indicates success
+ *
+ * @throws BrandDevInvalidDataException if the JSON field has an unexpected type or is
+ * unexpectedly missing or null (e.g. if the server responded with an unexpected value).
+ */
+ fun success(): Success = success.getRequired("success")
+
+ /**
+ * The URL that was scraped
+ *
+ * @throws BrandDevInvalidDataException if the JSON field has an unexpected type or is
+ * unexpectedly missing or null (e.g. if the server responded with an unexpected value).
+ */
+ fun url(): String = url.getRequired("url")
+
+ /**
+ * Returns the raw JSON value of [markdown].
+ *
+ * Unlike [markdown], this method doesn't throw if the JSON field has an unexpected type.
+ */
+ @JsonProperty("markdown") @ExcludeMissing fun _markdown(): JsonField = markdown
+
+ /**
+ * Returns the raw JSON value of [success].
+ *
+ * Unlike [success], this method doesn't throw if the JSON field has an unexpected type.
+ */
+ @JsonProperty("success") @ExcludeMissing fun _success(): JsonField = success
+
+ /**
+ * Returns the raw JSON value of [url].
+ *
+ * Unlike [url], this method doesn't throw if the JSON field has an unexpected type.
+ */
+ @JsonProperty("url") @ExcludeMissing fun _url(): JsonField = url
+
+ @JsonAnySetter
+ private fun putAdditionalProperty(key: String, value: JsonValue) {
+ additionalProperties.put(key, value)
+ }
+
+ @JsonAnyGetter
+ @ExcludeMissing
+ fun _additionalProperties(): Map =
+ Collections.unmodifiableMap(additionalProperties)
+
+ fun toBuilder() = Builder().from(this)
+
+ companion object {
+
+ /**
+ * Returns a mutable builder for constructing an instance of [BrandWebScrapeMdResponse].
+ *
+ * The following fields are required:
+ * ```java
+ * .markdown()
+ * .success()
+ * .url()
+ * ```
+ */
+ @JvmStatic fun builder() = Builder()
+ }
+
+ /** A builder for [BrandWebScrapeMdResponse]. */
+ class Builder internal constructor() {
+
+ private var markdown: JsonField? = null
+ private var success: JsonField? = null
+ private var url: JsonField? = null
+ private var additionalProperties: MutableMap = mutableMapOf()
+
+ @JvmSynthetic
+ internal fun from(brandWebScrapeMdResponse: BrandWebScrapeMdResponse) = apply {
+ markdown = brandWebScrapeMdResponse.markdown
+ success = brandWebScrapeMdResponse.success
+ url = brandWebScrapeMdResponse.url
+ additionalProperties = brandWebScrapeMdResponse.additionalProperties.toMutableMap()
+ }
+
+ /** Page content converted to GitHub Flavored Markdown */
+ fun markdown(markdown: String) = markdown(JsonField.of(markdown))
+
+ /**
+ * Sets [Builder.markdown] to an arbitrary JSON value.
+ *
+ * You should usually call [Builder.markdown] with a well-typed [String] value instead. This
+ * method is primarily for setting the field to an undocumented or not yet supported value.
+ */
+ fun markdown(markdown: JsonField) = apply { this.markdown = markdown }
+
+ /** Indicates success */
+ fun success(success: Success) = success(JsonField.of(success))
+
+ /**
+ * Sets [Builder.success] to an arbitrary JSON value.
+ *
+ * You should usually call [Builder.success] with a well-typed [Success] value instead. This
+ * method is primarily for setting the field to an undocumented or not yet supported value.
+ */
+ fun success(success: JsonField) = apply { this.success = success }
+
+ /** The URL that was scraped */
+ fun url(url: String) = url(JsonField.of(url))
+
+ /**
+ * Sets [Builder.url] to an arbitrary JSON value.
+ *
+ * You should usually call [Builder.url] with a well-typed [String] value instead. This
+ * method is primarily for setting the field to an undocumented or not yet supported value.
+ */
+ fun url(url: JsonField) = apply { this.url = url }
+
+ fun additionalProperties(additionalProperties: Map) = apply {
+ this.additionalProperties.clear()
+ putAllAdditionalProperties(additionalProperties)
+ }
+
+ fun putAdditionalProperty(key: String, value: JsonValue) = apply {
+ additionalProperties.put(key, value)
+ }
+
+ fun putAllAdditionalProperties(additionalProperties: Map) = apply {
+ this.additionalProperties.putAll(additionalProperties)
+ }
+
+ fun removeAdditionalProperty(key: String) = apply { additionalProperties.remove(key) }
+
+ fun removeAllAdditionalProperties(keys: Set) = apply {
+ keys.forEach(::removeAdditionalProperty)
+ }
+
+ /**
+ * Returns an immutable instance of [BrandWebScrapeMdResponse].
+ *
+ * Further updates to this [Builder] will not mutate the returned instance.
+ *
+ * The following fields are required:
+ * ```java
+ * .markdown()
+ * .success()
+ * .url()
+ * ```
+ *
+ * @throws IllegalStateException if any required field is unset.
+ */
+ fun build(): BrandWebScrapeMdResponse =
+ BrandWebScrapeMdResponse(
+ checkRequired("markdown", markdown),
+ checkRequired("success", success),
+ checkRequired("url", url),
+ additionalProperties.toMutableMap(),
+ )
+ }
+
+ private var validated: Boolean = false
+
+ fun validate(): BrandWebScrapeMdResponse = apply {
+ if (validated) {
+ return@apply
+ }
+
+ markdown()
+ success().validate()
+ url()
+ validated = true
+ }
+
+ fun isValid(): Boolean =
+ try {
+ validate()
+ true
+ } catch (e: BrandDevInvalidDataException) {
+ false
+ }
+
+ /**
+ * Returns a score indicating how many valid values are contained in this object recursively.
+ *
+ * Used for best match union deserialization.
+ */
+ @JvmSynthetic
+ internal fun validity(): Int =
+ (if (markdown.asKnown().isPresent) 1 else 0) +
+ (success.asKnown().getOrNull()?.validity() ?: 0) +
+ (if (url.asKnown().isPresent) 1 else 0)
+
+ /** Indicates success */
+ class Success @JsonCreator private constructor(private val value: JsonField) : Enum {
+
+ /**
+ * Returns this class instance's raw value.
+ *
+ * This is usually only useful if this instance was deserialized from data that doesn't
+ * match any known member, and you want to know that value. For example, if the SDK is on an
+ * older version than the API, then the API may respond with new members that the SDK is
+ * unaware of.
+ */
+ @com.fasterxml.jackson.annotation.JsonValue fun _value(): JsonField = value
+
+ companion object {
+
+ @JvmField val TRUE = of(true)
+
+ @JvmStatic fun of(value: Boolean) = Success(JsonField.of(value))
+ }
+
+ /** An enum containing [Success]'s known values. */
+ enum class Known {
+ TRUE
+ }
+
+ /**
+ * An enum containing [Success]'s known values, as well as an [_UNKNOWN] member.
+ *
+ * An instance of [Success] can contain an unknown value in a couple of cases:
+ * - It was deserialized from data that doesn't match any known member. For example, if the
+ * SDK is on an older version than the API, then the API may respond with new members that
+ * the SDK is unaware of.
+ * - It was constructed with an arbitrary value using the [of] method.
+ */
+ enum class Value {
+ TRUE,
+ /** An enum member indicating that [Success] was instantiated with an unknown value. */
+ _UNKNOWN,
+ }
+
+ /**
+ * Returns an enum member corresponding to this class instance's value, or [Value._UNKNOWN]
+ * if the class was instantiated with an unknown value.
+ *
+ * Use the [known] method instead if you're certain the value is always known or if you want
+ * to throw for the unknown case.
+ */
+ fun value(): Value =
+ when (this) {
+ TRUE -> Value.TRUE
+ else -> Value._UNKNOWN
+ }
+
+ /**
+ * Returns an enum member corresponding to this class instance's value.
+ *
+ * Use the [value] method instead if you're uncertain the value is always known and don't
+ * want to throw for the unknown case.
+ *
+ * @throws BrandDevInvalidDataException if this class instance's value is a not a known
+ * member.
+ */
+ fun known(): Known =
+ when (this) {
+ TRUE -> Known.TRUE
+ else -> throw BrandDevInvalidDataException("Unknown Success: $value")
+ }
+
+ /**
+ * Returns this class instance's primitive wire representation.
+ *
+ * @throws BrandDevInvalidDataException if this class instance's value does not have the
+ * expected primitive type.
+ */
+ fun asBoolean(): Boolean =
+ _value().asBoolean().orElseThrow {
+ BrandDevInvalidDataException("Value is not a Boolean")
+ }
+
+ private var validated: Boolean = false
+
+ fun validate(): Success = apply {
+ if (validated) {
+ return@apply
+ }
+
+ known()
+ validated = true
+ }
+
+ fun isValid(): Boolean =
+ try {
+ validate()
+ true
+ } catch (e: BrandDevInvalidDataException) {
+ false
+ }
+
+ /**
+ * Returns a score indicating how many valid values are contained in this object
+ * recursively.
+ *
+ * Used for best match union deserialization.
+ */
+ @JvmSynthetic internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1
+
+ override fun equals(other: Any?): Boolean {
+ if (this === other) {
+ return true
+ }
+
+ return other is Success && value == other.value
+ }
+
+ override fun hashCode() = value.hashCode()
+
+ override fun toString() = value.toString()
+ }
+
+ override fun equals(other: Any?): Boolean {
+ if (this === other) {
+ return true
+ }
+
+ return other is BrandWebScrapeMdResponse &&
+ markdown == other.markdown &&
+ success == other.success &&
+ url == other.url &&
+ additionalProperties == other.additionalProperties
+ }
+
+ private val hashCode: Int by lazy { Objects.hash(markdown, success, url, additionalProperties) }
+
+ override fun hashCode(): Int = hashCode
+
+ override fun toString() =
+ "BrandWebScrapeMdResponse{markdown=$markdown, success=$success, url=$url, additionalProperties=$additionalProperties}"
+}
diff --git a/brand-dev-java-core/src/main/kotlin/com/branddev/api/models/brand/BrandWebScrapeSitemapParams.kt b/brand-dev-java-core/src/main/kotlin/com/branddev/api/models/brand/BrandWebScrapeSitemapParams.kt
new file mode 100644
index 0000000..7ee57a1
--- /dev/null
+++ b/brand-dev-java-core/src/main/kotlin/com/branddev/api/models/brand/BrandWebScrapeSitemapParams.kt
@@ -0,0 +1,213 @@
+// File generated from our OpenAPI spec by Stainless.
+
+package com.branddev.api.models.brand
+
+import com.branddev.api.core.Params
+import com.branddev.api.core.checkRequired
+import com.branddev.api.core.http.Headers
+import com.branddev.api.core.http.QueryParams
+import java.util.Objects
+
+/**
+ * Crawls the sitemap of the given domain and returns all discovered page URLs. Supports sitemap
+ * index files (recursive), parallel fetching with concurrency control, deduplication, and filters
+ * out non-page resources (images, PDFs, etc.).
+ */
+class BrandWebScrapeSitemapParams
+private constructor(
+ private val domain: String,
+ private val additionalHeaders: Headers,
+ private val additionalQueryParams: QueryParams,
+) : Params {
+
+ /**
+ * Domain name to crawl sitemaps for (e.g., 'example.com'). The domain will be automatically
+ * normalized and validated.
+ */
+ fun domain(): String = domain
+
+ /** Additional headers to send with the request. */
+ fun _additionalHeaders(): Headers = additionalHeaders
+
+ /** Additional query param to send with the request. */
+ fun _additionalQueryParams(): QueryParams = additionalQueryParams
+
+ fun toBuilder() = Builder().from(this)
+
+ companion object {
+
+ /**
+ * Returns a mutable builder for constructing an instance of [BrandWebScrapeSitemapParams].
+ *
+ * The following fields are required:
+ * ```java
+ * .domain()
+ * ```
+ */
+ @JvmStatic fun builder() = Builder()
+ }
+
+ /** A builder for [BrandWebScrapeSitemapParams]. */
+ class Builder internal constructor() {
+
+ private var domain: String? = null
+ private var additionalHeaders: Headers.Builder = Headers.builder()
+ private var additionalQueryParams: QueryParams.Builder = QueryParams.builder()
+
+ @JvmSynthetic
+ internal fun from(brandWebScrapeSitemapParams: BrandWebScrapeSitemapParams) = apply {
+ domain = brandWebScrapeSitemapParams.domain
+ additionalHeaders = brandWebScrapeSitemapParams.additionalHeaders.toBuilder()
+ additionalQueryParams = brandWebScrapeSitemapParams.additionalQueryParams.toBuilder()
+ }
+
+ /**
+ * Domain name to crawl sitemaps for (e.g., 'example.com'). The domain will be automatically
+ * normalized and validated.
+ */
+ fun domain(domain: String) = apply { this.domain = domain }
+
+ fun additionalHeaders(additionalHeaders: Headers) = apply {
+ this.additionalHeaders.clear()
+ putAllAdditionalHeaders(additionalHeaders)
+ }
+
+ fun additionalHeaders(additionalHeaders: Map>) = apply {
+ this.additionalHeaders.clear()
+ putAllAdditionalHeaders(additionalHeaders)
+ }
+
+ fun putAdditionalHeader(name: String, value: String) = apply {
+ additionalHeaders.put(name, value)
+ }
+
+ fun putAdditionalHeaders(name: String, values: Iterable) = apply {
+ additionalHeaders.put(name, values)
+ }
+
+ fun putAllAdditionalHeaders(additionalHeaders: Headers) = apply {
+ this.additionalHeaders.putAll(additionalHeaders)
+ }
+
+ fun putAllAdditionalHeaders(additionalHeaders: Map>) = apply {
+ this.additionalHeaders.putAll(additionalHeaders)
+ }
+
+ fun replaceAdditionalHeaders(name: String, value: String) = apply {
+ additionalHeaders.replace(name, value)
+ }
+
+ fun replaceAdditionalHeaders(name: String, values: Iterable) = apply {
+ additionalHeaders.replace(name, values)
+ }
+
+ fun replaceAllAdditionalHeaders(additionalHeaders: Headers) = apply {
+ this.additionalHeaders.replaceAll(additionalHeaders)
+ }
+
+ fun replaceAllAdditionalHeaders(additionalHeaders: Map>) = apply {
+ this.additionalHeaders.replaceAll(additionalHeaders)
+ }
+
+ fun removeAdditionalHeaders(name: String) = apply { additionalHeaders.remove(name) }
+
+ fun removeAllAdditionalHeaders(names: Set) = apply {
+ additionalHeaders.removeAll(names)
+ }
+
+ fun additionalQueryParams(additionalQueryParams: QueryParams) = apply {
+ this.additionalQueryParams.clear()
+ putAllAdditionalQueryParams(additionalQueryParams)
+ }
+
+ fun additionalQueryParams(additionalQueryParams: Map>) = apply {
+ this.additionalQueryParams.clear()
+ putAllAdditionalQueryParams(additionalQueryParams)
+ }
+
+ fun putAdditionalQueryParam(key: String, value: String) = apply {
+ additionalQueryParams.put(key, value)
+ }
+
+ fun putAdditionalQueryParams(key: String, values: Iterable) = apply {
+ additionalQueryParams.put(key, values)
+ }
+
+ fun putAllAdditionalQueryParams(additionalQueryParams: QueryParams) = apply {
+ this.additionalQueryParams.putAll(additionalQueryParams)
+ }
+
+ fun putAllAdditionalQueryParams(additionalQueryParams: Map>) =
+ apply {
+ this.additionalQueryParams.putAll(additionalQueryParams)
+ }
+
+ fun replaceAdditionalQueryParams(key: String, value: String) = apply {
+ additionalQueryParams.replace(key, value)
+ }
+
+ fun replaceAdditionalQueryParams(key: String, values: Iterable) = apply {
+ additionalQueryParams.replace(key, values)
+ }
+
+ fun replaceAllAdditionalQueryParams(additionalQueryParams: QueryParams) = apply {
+ this.additionalQueryParams.replaceAll(additionalQueryParams)
+ }
+
+ fun replaceAllAdditionalQueryParams(additionalQueryParams: Map>) =
+ apply {
+ this.additionalQueryParams.replaceAll(additionalQueryParams)
+ }
+
+ fun removeAdditionalQueryParams(key: String) = apply { additionalQueryParams.remove(key) }
+
+ fun removeAllAdditionalQueryParams(keys: Set) = apply {
+ additionalQueryParams.removeAll(keys)
+ }
+
+ /**
+ * Returns an immutable instance of [BrandWebScrapeSitemapParams].
+ *
+ * Further updates to this [Builder] will not mutate the returned instance.
+ *
+ * The following fields are required:
+ * ```java
+ * .domain()
+ * ```
+ *
+ * @throws IllegalStateException if any required field is unset.
+ */
+ fun build(): BrandWebScrapeSitemapParams =
+ BrandWebScrapeSitemapParams(
+ checkRequired("domain", domain),
+ additionalHeaders.build(),
+ additionalQueryParams.build(),
+ )
+ }
+
+ override fun _headers(): Headers = additionalHeaders
+
+ override fun _queryParams(): QueryParams =
+ QueryParams.builder()
+ .apply {
+ put("domain", domain)
+ putAll(additionalQueryParams)
+ }
+ .build()
+
+ override fun equals(other: Any?): Boolean {
+ if (this === other) {
+ return true
+ }
+
+ return other is BrandWebScrapeSitemapParams &&
+ domain == other.domain &&
+ additionalHeaders == other.additionalHeaders &&
+ additionalQueryParams == other.additionalQueryParams
+ }
+
+ override fun hashCode(): Int = Objects.hash(domain, additionalHeaders, additionalQueryParams)
+
+ override fun toString() =
+ "BrandWebScrapeSitemapParams{domain=$domain, additionalHeaders=$additionalHeaders, additionalQueryParams=$additionalQueryParams}"
+}
diff --git a/brand-dev-java-core/src/main/kotlin/com/branddev/api/models/brand/BrandWebScrapeSitemapResponse.kt b/brand-dev-java-core/src/main/kotlin/com/branddev/api/models/brand/BrandWebScrapeSitemapResponse.kt
new file mode 100644
index 0000000..bca261f
--- /dev/null
+++ b/brand-dev-java-core/src/main/kotlin/com/branddev/api/models/brand/BrandWebScrapeSitemapResponse.kt
@@ -0,0 +1,726 @@
+// File generated from our OpenAPI spec by Stainless.
+
+package com.branddev.api.models.brand
+
+import com.branddev.api.core.Enum
+import com.branddev.api.core.ExcludeMissing
+import com.branddev.api.core.JsonField
+import com.branddev.api.core.JsonMissing
+import com.branddev.api.core.JsonValue
+import com.branddev.api.core.checkKnown
+import com.branddev.api.core.checkRequired
+import com.branddev.api.core.toImmutable
+import com.branddev.api.errors.BrandDevInvalidDataException
+import com.fasterxml.jackson.annotation.JsonAnyGetter
+import com.fasterxml.jackson.annotation.JsonAnySetter
+import com.fasterxml.jackson.annotation.JsonCreator
+import com.fasterxml.jackson.annotation.JsonProperty
+import java.util.Collections
+import java.util.Objects
+import kotlin.jvm.optionals.getOrNull
+
+class BrandWebScrapeSitemapResponse
+@JsonCreator(mode = JsonCreator.Mode.DISABLED)
+private constructor(
+ private val domain: JsonField,
+ private val meta: JsonField,
+ private val success: JsonField,
+ private val urls: JsonField>,
+ private val additionalProperties: MutableMap,
+) {
+
+ @JsonCreator
+ private constructor(
+ @JsonProperty("domain") @ExcludeMissing domain: JsonField = JsonMissing.of(),
+ @JsonProperty("meta") @ExcludeMissing meta: JsonField = JsonMissing.of(),
+ @JsonProperty("success") @ExcludeMissing success: JsonField = JsonMissing.of(),
+ @JsonProperty("urls") @ExcludeMissing urls: JsonField> = JsonMissing.of(),
+ ) : this(domain, meta, success, urls, mutableMapOf())
+
+ /**
+ * The normalized domain that was crawled
+ *
+ * @throws BrandDevInvalidDataException if the JSON field has an unexpected type or is
+ * unexpectedly missing or null (e.g. if the server responded with an unexpected value).
+ */
+ fun domain(): String = domain.getRequired("domain")
+
+ /**
+ * Metadata about the sitemap crawl operation
+ *
+ * @throws BrandDevInvalidDataException if the JSON field has an unexpected type or is
+ * unexpectedly missing or null (e.g. if the server responded with an unexpected value).
+ */
+ fun meta(): Meta = meta.getRequired("meta")
+
+ /**
+ * Indicates success
+ *
+ * @throws BrandDevInvalidDataException if the JSON field has an unexpected type or is
+ * unexpectedly missing or null (e.g. if the server responded with an unexpected value).
+ */
+ fun success(): Success = success.getRequired("success")
+
+ /**
+ * Array of discovered page URLs from the sitemap (max 500)
+ *
+ * @throws BrandDevInvalidDataException if the JSON field has an unexpected type or is
+ * unexpectedly missing or null (e.g. if the server responded with an unexpected value).
+ */
+ fun urls(): List = urls.getRequired("urls")
+
+ /**
+ * Returns the raw JSON value of [domain].
+ *
+ * Unlike [domain], this method doesn't throw if the JSON field has an unexpected type.
+ */
+ @JsonProperty("domain") @ExcludeMissing fun _domain(): JsonField = domain
+
+ /**
+ * Returns the raw JSON value of [meta].
+ *
+ * Unlike [meta], this method doesn't throw if the JSON field has an unexpected type.
+ */
+ @JsonProperty("meta") @ExcludeMissing fun _meta(): JsonField = meta
+
+ /**
+ * Returns the raw JSON value of [success].
+ *
+ * Unlike [success], this method doesn't throw if the JSON field has an unexpected type.
+ */
+ @JsonProperty("success") @ExcludeMissing fun _success(): JsonField = success
+
+ /**
+ * Returns the raw JSON value of [urls].
+ *
+ * Unlike [urls], this method doesn't throw if the JSON field has an unexpected type.
+ */
+ @JsonProperty("urls") @ExcludeMissing fun _urls(): JsonField> = urls
+
+ @JsonAnySetter
+ private fun putAdditionalProperty(key: String, value: JsonValue) {
+ additionalProperties.put(key, value)
+ }
+
+ @JsonAnyGetter
+ @ExcludeMissing
+ fun _additionalProperties(): Map =
+ Collections.unmodifiableMap(additionalProperties)
+
+ fun toBuilder() = Builder().from(this)
+
+ companion object {
+
+ /**
+ * Returns a mutable builder for constructing an instance of
+ * [BrandWebScrapeSitemapResponse].
+ *
+ * The following fields are required:
+ * ```java
+ * .domain()
+ * .meta()
+ * .success()
+ * .urls()
+ * ```
+ */
+ @JvmStatic fun builder() = Builder()
+ }
+
+ /** A builder for [BrandWebScrapeSitemapResponse]. */
+ class Builder internal constructor() {
+
+ private var domain: JsonField? = null
+ private var meta: JsonField? = null
+ private var success: JsonField? = null
+ private var urls: JsonField>? = null
+ private var additionalProperties: MutableMap = mutableMapOf()
+
+ @JvmSynthetic
+ internal fun from(brandWebScrapeSitemapResponse: BrandWebScrapeSitemapResponse) = apply {
+ domain = brandWebScrapeSitemapResponse.domain
+ meta = brandWebScrapeSitemapResponse.meta
+ success = brandWebScrapeSitemapResponse.success
+ urls = brandWebScrapeSitemapResponse.urls.map { it.toMutableList() }
+ additionalProperties = brandWebScrapeSitemapResponse.additionalProperties.toMutableMap()
+ }
+
+ /** The normalized domain that was crawled */
+ fun domain(domain: String) = domain(JsonField.of(domain))
+
+ /**
+ * Sets [Builder.domain] to an arbitrary JSON value.
+ *
+ * You should usually call [Builder.domain] with a well-typed [String] value instead. This
+ * method is primarily for setting the field to an undocumented or not yet supported value.
+ */
+ fun domain(domain: JsonField) = apply { this.domain = domain }
+
+ /** Metadata about the sitemap crawl operation */
+ fun meta(meta: Meta) = meta(JsonField.of(meta))
+
+ /**
+ * Sets [Builder.meta] to an arbitrary JSON value.
+ *
+ * You should usually call [Builder.meta] with a well-typed [Meta] value instead. This
+ * method is primarily for setting the field to an undocumented or not yet supported value.
+ */
+ fun meta(meta: JsonField) = apply { this.meta = meta }
+
+ /** Indicates success */
+ fun success(success: Success) = success(JsonField.of(success))
+
+ /**
+ * Sets [Builder.success] to an arbitrary JSON value.
+ *
+ * You should usually call [Builder.success] with a well-typed [Success] value instead. This
+ * method is primarily for setting the field to an undocumented or not yet supported value.
+ */
+ fun success(success: JsonField) = apply { this.success = success }
+
+ /** Array of discovered page URLs from the sitemap (max 500) */
+ fun urls(urls: List) = urls(JsonField.of(urls))
+
+ /**
+ * Sets [Builder.urls] to an arbitrary JSON value.
+ *
+ * You should usually call [Builder.urls] with a well-typed `List` value instead.
+ * This method is primarily for setting the field to an undocumented or not yet supported
+ * value.
+ */
+ fun urls(urls: JsonField>) = apply {
+ this.urls = urls.map { it.toMutableList() }
+ }
+
+ /**
+ * Adds a single [String] to [urls].
+ *
+ * @throws IllegalStateException if the field was previously set to a non-list.
+ */
+ fun addUrl(url: String) = apply {
+ urls = (urls ?: JsonField.of(mutableListOf())).also { checkKnown("urls", it).add(url) }
+ }
+
+ fun additionalProperties(additionalProperties: Map) = apply {
+ this.additionalProperties.clear()
+ putAllAdditionalProperties(additionalProperties)
+ }
+
+ fun putAdditionalProperty(key: String, value: JsonValue) = apply {
+ additionalProperties.put(key, value)
+ }
+
+ fun putAllAdditionalProperties(additionalProperties: Map) = apply {
+ this.additionalProperties.putAll(additionalProperties)
+ }
+
+ fun removeAdditionalProperty(key: String) = apply { additionalProperties.remove(key) }
+
+ fun removeAllAdditionalProperties(keys: Set) = apply {
+ keys.forEach(::removeAdditionalProperty)
+ }
+
+ /**
+ * Returns an immutable instance of [BrandWebScrapeSitemapResponse].
+ *
+ * Further updates to this [Builder] will not mutate the returned instance.
+ *
+ * The following fields are required:
+ * ```java
+ * .domain()
+ * .meta()
+ * .success()
+ * .urls()
+ * ```
+ *
+ * @throws IllegalStateException if any required field is unset.
+ */
+ fun build(): BrandWebScrapeSitemapResponse =
+ BrandWebScrapeSitemapResponse(
+ checkRequired("domain", domain),
+ checkRequired("meta", meta),
+ checkRequired("success", success),
+ checkRequired("urls", urls).map { it.toImmutable() },
+ additionalProperties.toMutableMap(),
+ )
+ }
+
+ private var validated: Boolean = false
+
+ fun validate(): BrandWebScrapeSitemapResponse = apply {
+ if (validated) {
+ return@apply
+ }
+
+ domain()
+ meta().validate()
+ success().validate()
+ urls()
+ validated = true
+ }
+
+ fun isValid(): Boolean =
+ try {
+ validate()
+ true
+ } catch (e: BrandDevInvalidDataException) {
+ false
+ }
+
+ /**
+ * Returns a score indicating how many valid values are contained in this object recursively.
+ *
+ * Used for best match union deserialization.
+ */
+ @JvmSynthetic
+ internal fun validity(): Int =
+ (if (domain.asKnown().isPresent) 1 else 0) +
+ (meta.asKnown().getOrNull()?.validity() ?: 0) +
+ (success.asKnown().getOrNull()?.validity() ?: 0) +
+ (urls.asKnown().getOrNull()?.size ?: 0)
+
+ /** Metadata about the sitemap crawl operation */
+ class Meta
+ @JsonCreator(mode = JsonCreator.Mode.DISABLED)
+ private constructor(
+ private val errors: JsonField,
+ private val sitemapsDiscovered: JsonField,
+ private val sitemapsFetched: JsonField,
+ private val sitemapsSkipped: JsonField,
+ private val additionalProperties: MutableMap,
+ ) {
+
+ @JsonCreator
+ private constructor(
+ @JsonProperty("errors") @ExcludeMissing errors: JsonField = JsonMissing.of(),
+ @JsonProperty("sitemapsDiscovered")
+ @ExcludeMissing
+ sitemapsDiscovered: JsonField = JsonMissing.of(),
+ @JsonProperty("sitemapsFetched")
+ @ExcludeMissing
+ sitemapsFetched: JsonField = JsonMissing.of(),
+ @JsonProperty("sitemapsSkipped")
+ @ExcludeMissing
+ sitemapsSkipped: JsonField = JsonMissing.of(),
+ ) : this(errors, sitemapsDiscovered, sitemapsFetched, sitemapsSkipped, mutableMapOf())
+
+ /**
+ * Number of errors encountered during crawling
+ *
+ * @throws BrandDevInvalidDataException if the JSON field has an unexpected type or is
+ * unexpectedly missing or null (e.g. if the server responded with an unexpected value).
+ */
+ fun errors(): Long = errors.getRequired("errors")
+
+ /**
+ * Total number of sitemap files discovered
+ *
+ * @throws BrandDevInvalidDataException if the JSON field has an unexpected type or is
+ * unexpectedly missing or null (e.g. if the server responded with an unexpected value).
+ */
+ fun sitemapsDiscovered(): Long = sitemapsDiscovered.getRequired("sitemapsDiscovered")
+
+ /**
+ * Number of sitemap files successfully fetched and parsed
+ *
+ * @throws BrandDevInvalidDataException if the JSON field has an unexpected type or is
+ * unexpectedly missing or null (e.g. if the server responded with an unexpected value).
+ */
+ fun sitemapsFetched(): Long = sitemapsFetched.getRequired("sitemapsFetched")
+
+ /**
+ * Number of sitemap files skipped (due to errors, timeouts, or limits)
+ *
+ * @throws BrandDevInvalidDataException if the JSON field has an unexpected type or is
+ * unexpectedly missing or null (e.g. if the server responded with an unexpected value).
+ */
+ fun sitemapsSkipped(): Long = sitemapsSkipped.getRequired("sitemapsSkipped")
+
+ /**
+ * Returns the raw JSON value of [errors].
+ *
+ * Unlike [errors], this method doesn't throw if the JSON field has an unexpected type.
+ */
+ @JsonProperty("errors") @ExcludeMissing fun _errors(): JsonField = errors
+
+ /**
+ * Returns the raw JSON value of [sitemapsDiscovered].
+ *
+ * Unlike [sitemapsDiscovered], this method doesn't throw if the JSON field has an
+ * unexpected type.
+ */
+ @JsonProperty("sitemapsDiscovered")
+ @ExcludeMissing
+ fun _sitemapsDiscovered(): JsonField = sitemapsDiscovered
+
+ /**
+ * Returns the raw JSON value of [sitemapsFetched].
+ *
+ * Unlike [sitemapsFetched], this method doesn't throw if the JSON field has an unexpected
+ * type.
+ */
+ @JsonProperty("sitemapsFetched")
+ @ExcludeMissing
+ fun _sitemapsFetched(): JsonField = sitemapsFetched
+
+ /**
+ * Returns the raw JSON value of [sitemapsSkipped].
+ *
+ * Unlike [sitemapsSkipped], this method doesn't throw if the JSON field has an unexpected
+ * type.
+ */
+ @JsonProperty("sitemapsSkipped")
+ @ExcludeMissing
+ fun _sitemapsSkipped(): JsonField = sitemapsSkipped
+
+ @JsonAnySetter
+ private fun putAdditionalProperty(key: String, value: JsonValue) {
+ additionalProperties.put(key, value)
+ }
+
+ @JsonAnyGetter
+ @ExcludeMissing
+ fun _additionalProperties(): Map =
+ Collections.unmodifiableMap(additionalProperties)
+
+ fun toBuilder() = Builder().from(this)
+
+ companion object {
+
+ /**
+ * Returns a mutable builder for constructing an instance of [Meta].
+ *
+ * The following fields are required:
+ * ```java
+ * .errors()
+ * .sitemapsDiscovered()
+ * .sitemapsFetched()
+ * .sitemapsSkipped()
+ * ```
+ */
+ @JvmStatic fun builder() = Builder()
+ }
+
+ /** A builder for [Meta]. */
+ class Builder internal constructor() {
+
+ private var errors: JsonField? = null
+ private var sitemapsDiscovered: JsonField? = null
+ private var sitemapsFetched: JsonField? = null
+ private var sitemapsSkipped: JsonField? = null
+ private var additionalProperties: MutableMap = mutableMapOf()
+
+ @JvmSynthetic
+ internal fun from(meta: Meta) = apply {
+ errors = meta.errors
+ sitemapsDiscovered = meta.sitemapsDiscovered
+ sitemapsFetched = meta.sitemapsFetched
+ sitemapsSkipped = meta.sitemapsSkipped
+ additionalProperties = meta.additionalProperties.toMutableMap()
+ }
+
+ /** Number of errors encountered during crawling */
+ fun errors(errors: Long) = errors(JsonField.of(errors))
+
+ /**
+ * Sets [Builder.errors] to an arbitrary JSON value.
+ *
+ * You should usually call [Builder.errors] with a well-typed [Long] value instead. This
+ * method is primarily for setting the field to an undocumented or not yet supported
+ * value.
+ */
+ fun errors(errors: JsonField) = apply { this.errors = errors }
+
+ /** Total number of sitemap files discovered */
+ fun sitemapsDiscovered(sitemapsDiscovered: Long) =
+ sitemapsDiscovered(JsonField.of(sitemapsDiscovered))
+
+ /**
+ * Sets [Builder.sitemapsDiscovered] to an arbitrary JSON value.
+ *
+ * You should usually call [Builder.sitemapsDiscovered] with a well-typed [Long] value
+ * instead. This method is primarily for setting the field to an undocumented or not yet
+ * supported value.
+ */
+ fun sitemapsDiscovered(sitemapsDiscovered: JsonField) = apply {
+ this.sitemapsDiscovered = sitemapsDiscovered
+ }
+
+ /** Number of sitemap files successfully fetched and parsed */
+ fun sitemapsFetched(sitemapsFetched: Long) =
+ sitemapsFetched(JsonField.of(sitemapsFetched))
+
+ /**
+ * Sets [Builder.sitemapsFetched] to an arbitrary JSON value.
+ *
+ * You should usually call [Builder.sitemapsFetched] with a well-typed [Long] value
+ * instead. This method is primarily for setting the field to an undocumented or not yet
+ * supported value.
+ */
+ fun sitemapsFetched(sitemapsFetched: JsonField) = apply {
+ this.sitemapsFetched = sitemapsFetched
+ }
+
+ /** Number of sitemap files skipped (due to errors, timeouts, or limits) */
+ fun sitemapsSkipped(sitemapsSkipped: Long) =
+ sitemapsSkipped(JsonField.of(sitemapsSkipped))
+
+ /**
+ * Sets [Builder.sitemapsSkipped] to an arbitrary JSON value.
+ *
+ * You should usually call [Builder.sitemapsSkipped] with a well-typed [Long] value
+ * instead. This method is primarily for setting the field to an undocumented or not yet
+ * supported value.
+ */
+ fun sitemapsSkipped(sitemapsSkipped: JsonField) = apply {
+ this.sitemapsSkipped = sitemapsSkipped
+ }
+
+ fun additionalProperties(additionalProperties: Map) = apply {
+ this.additionalProperties.clear()
+ putAllAdditionalProperties(additionalProperties)
+ }
+
+ fun putAdditionalProperty(key: String, value: JsonValue) = apply {
+ additionalProperties.put(key, value)
+ }
+
+ fun putAllAdditionalProperties(additionalProperties: Map) = apply {
+ this.additionalProperties.putAll(additionalProperties)
+ }
+
+ fun removeAdditionalProperty(key: String) = apply { additionalProperties.remove(key) }
+
+ fun removeAllAdditionalProperties(keys: Set) = apply {
+ keys.forEach(::removeAdditionalProperty)
+ }
+
+ /**
+ * Returns an immutable instance of [Meta].
+ *
+ * Further updates to this [Builder] will not mutate the returned instance.
+ *
+ * The following fields are required:
+ * ```java
+ * .errors()
+ * .sitemapsDiscovered()
+ * .sitemapsFetched()
+ * .sitemapsSkipped()
+ * ```
+ *
+ * @throws IllegalStateException if any required field is unset.
+ */
+ fun build(): Meta =
+ Meta(
+ checkRequired("errors", errors),
+ checkRequired("sitemapsDiscovered", sitemapsDiscovered),
+ checkRequired("sitemapsFetched", sitemapsFetched),
+ checkRequired("sitemapsSkipped", sitemapsSkipped),
+ additionalProperties.toMutableMap(),
+ )
+ }
+
+ private var validated: Boolean = false
+
+ fun validate(): Meta = apply {
+ if (validated) {
+ return@apply
+ }
+
+ errors()
+ sitemapsDiscovered()
+ sitemapsFetched()
+ sitemapsSkipped()
+ validated = true
+ }
+
+ fun isValid(): Boolean =
+ try {
+ validate()
+ true
+ } catch (e: BrandDevInvalidDataException) {
+ false
+ }
+
+ /**
+ * Returns a score indicating how many valid values are contained in this object
+ * recursively.
+ *
+ * Used for best match union deserialization.
+ */
+ @JvmSynthetic
+ internal fun validity(): Int =
+ (if (errors.asKnown().isPresent) 1 else 0) +
+ (if (sitemapsDiscovered.asKnown().isPresent) 1 else 0) +
+ (if (sitemapsFetched.asKnown().isPresent) 1 else 0) +
+ (if (sitemapsSkipped.asKnown().isPresent) 1 else 0)
+
+ override fun equals(other: Any?): Boolean {
+ if (this === other) {
+ return true
+ }
+
+ return other is Meta &&
+ errors == other.errors &&
+ sitemapsDiscovered == other.sitemapsDiscovered &&
+ sitemapsFetched == other.sitemapsFetched &&
+ sitemapsSkipped == other.sitemapsSkipped &&
+ additionalProperties == other.additionalProperties
+ }
+
+ private val hashCode: Int by lazy {
+ Objects.hash(
+ errors,
+ sitemapsDiscovered,
+ sitemapsFetched,
+ sitemapsSkipped,
+ additionalProperties,
+ )
+ }
+
+ override fun hashCode(): Int = hashCode
+
+ override fun toString() =
+ "Meta{errors=$errors, sitemapsDiscovered=$sitemapsDiscovered, sitemapsFetched=$sitemapsFetched, sitemapsSkipped=$sitemapsSkipped, additionalProperties=$additionalProperties}"
+ }
+
+ /** Indicates success */
+ class Success @JsonCreator private constructor(private val value: JsonField) : Enum {
+
+ /**
+ * Returns this class instance's raw value.
+ *
+ * This is usually only useful if this instance was deserialized from data that doesn't
+ * match any known member, and you want to know that value. For example, if the SDK is on an
+ * older version than the API, then the API may respond with new members that the SDK is
+ * unaware of.
+ */
+ @com.fasterxml.jackson.annotation.JsonValue fun _value(): JsonField = value
+
+ companion object {
+
+ @JvmField val TRUE = of(true)
+
+ @JvmStatic fun of(value: Boolean) = Success(JsonField.of(value))
+ }
+
+ /** An enum containing [Success]'s known values. */
+ enum class Known {
+ TRUE
+ }
+
+ /**
+ * An enum containing [Success]'s known values, as well as an [_UNKNOWN] member.
+ *
+ * An instance of [Success] can contain an unknown value in a couple of cases:
+ * - It was deserialized from data that doesn't match any known member. For example, if the
+ * SDK is on an older version than the API, then the API may respond with new members that
+ * the SDK is unaware of.
+ * - It was constructed with an arbitrary value using the [of] method.
+ */
+ enum class Value {
+ TRUE,
+ /** An enum member indicating that [Success] was instantiated with an unknown value. */
+ _UNKNOWN,
+ }
+
+ /**
+ * Returns an enum member corresponding to this class instance's value, or [Value._UNKNOWN]
+ * if the class was instantiated with an unknown value.
+ *
+ * Use the [known] method instead if you're certain the value is always known or if you want
+ * to throw for the unknown case.
+ */
+ fun value(): Value =
+ when (this) {
+ TRUE -> Value.TRUE
+ else -> Value._UNKNOWN
+ }
+
+ /**
+ * Returns an enum member corresponding to this class instance's value.
+ *
+ * Use the [value] method instead if you're uncertain the value is always known and don't
+ * want to throw for the unknown case.
+ *
+ * @throws BrandDevInvalidDataException if this class instance's value is a not a known
+ * member.
+ */
+ fun known(): Known =
+ when (this) {
+ TRUE -> Known.TRUE
+ else -> throw BrandDevInvalidDataException("Unknown Success: $value")
+ }
+
+ /**
+ * Returns this class instance's primitive wire representation.
+ *
+ * @throws BrandDevInvalidDataException if this class instance's value does not have the
+ * expected primitive type.
+ */
+ fun asBoolean(): Boolean =
+ _value().asBoolean().orElseThrow {
+ BrandDevInvalidDataException("Value is not a Boolean")
+ }
+
+ private var validated: Boolean = false
+
+ fun validate(): Success = apply {
+ if (validated) {
+ return@apply
+ }
+
+ known()
+ validated = true
+ }
+
+ fun isValid(): Boolean =
+ try {
+ validate()
+ true
+ } catch (e: BrandDevInvalidDataException) {
+ false
+ }
+
+ /**
+ * Returns a score indicating how many valid values are contained in this object
+ * recursively.
+ *
+ * Used for best match union deserialization.
+ */
+ @JvmSynthetic internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1
+
+ override fun equals(other: Any?): Boolean {
+ if (this === other) {
+ return true
+ }
+
+ return other is Success && value == other.value
+ }
+
+ override fun hashCode() = value.hashCode()
+
+ override fun toString() = value.toString()
+ }
+
+ override fun equals(other: Any?): Boolean {
+ if (this === other) {
+ return true
+ }
+
+ return other is BrandWebScrapeSitemapResponse &&
+ domain == other.domain &&
+ meta == other.meta &&
+ success == other.success &&
+ urls == other.urls &&
+ additionalProperties == other.additionalProperties
+ }
+
+ private val hashCode: Int by lazy {
+ Objects.hash(domain, meta, success, urls, additionalProperties)
+ }
+
+ override fun hashCode(): Int = hashCode
+
+ override fun toString() =
+ "BrandWebScrapeSitemapResponse{domain=$domain, meta=$meta, success=$success, urls=$urls, additionalProperties=$additionalProperties}"
+}
diff --git a/brand-dev-java-core/src/main/kotlin/com/branddev/api/services/async/BrandServiceAsync.kt b/brand-dev-java-core/src/main/kotlin/com/branddev/api/services/async/BrandServiceAsync.kt
index 1846ad9..70b9804 100644
--- a/brand-dev-java-core/src/main/kotlin/com/branddev/api/services/async/BrandServiceAsync.kt
+++ b/brand-dev-java-core/src/main/kotlin/com/branddev/api/services/async/BrandServiceAsync.kt
@@ -37,6 +37,14 @@ import com.branddev.api.models.brand.BrandScreenshotParams
import com.branddev.api.models.brand.BrandScreenshotResponse
import com.branddev.api.models.brand.BrandStyleguideParams
import com.branddev.api.models.brand.BrandStyleguideResponse
+import com.branddev.api.models.brand.BrandWebScrapeHtmlParams
+import com.branddev.api.models.brand.BrandWebScrapeHtmlResponse
+import com.branddev.api.models.brand.BrandWebScrapeImagesParams
+import com.branddev.api.models.brand.BrandWebScrapeImagesResponse
+import com.branddev.api.models.brand.BrandWebScrapeMdParams
+import com.branddev.api.models.brand.BrandWebScrapeMdResponse
+import com.branddev.api.models.brand.BrandWebScrapeSitemapParams
+import com.branddev.api.models.brand.BrandWebScrapeSitemapResponse
import java.util.concurrent.CompletableFuture
import java.util.function.Consumer
@@ -317,6 +325,64 @@ interface BrandServiceAsync {
requestOptions: RequestOptions = RequestOptions.none(),
): CompletableFuture
+ /**
+ * Scrapes the given URL and returns the raw HTML content of the page. Uses automatic proxy
+ * escalation to handle blocked sites.
+ */
+ fun webScrapeHtml(
+ params: BrandWebScrapeHtmlParams
+ ): CompletableFuture = webScrapeHtml(params, RequestOptions.none())
+
+ /** @see webScrapeHtml */
+ fun webScrapeHtml(
+ params: BrandWebScrapeHtmlParams,
+ requestOptions: RequestOptions = RequestOptions.none(),
+ ): CompletableFuture
+
+ /**
+ * Scrapes all images from the given URL. Extracts images from img, svg, picture/source, link,
+ * and video elements including inline SVGs, base64 data URIs, and standard URLs.
+ */
+ fun webScrapeImages(
+ params: BrandWebScrapeImagesParams
+ ): CompletableFuture =
+ webScrapeImages(params, RequestOptions.none())
+
+ /** @see webScrapeImages */
+ fun webScrapeImages(
+ params: BrandWebScrapeImagesParams,
+ requestOptions: RequestOptions = RequestOptions.none(),
+ ): CompletableFuture
+
+ /**
+ * Scrapes the given URL, converts the HTML content to GitHub Flavored Markdown (GFM), and
+ * returns the result. Uses automatic proxy escalation to handle blocked sites.
+ */
+ fun webScrapeMd(params: BrandWebScrapeMdParams): CompletableFuture =
+ webScrapeMd(params, RequestOptions.none())
+
+ /** @see webScrapeMd */
+ fun webScrapeMd(
+ params: BrandWebScrapeMdParams,
+ requestOptions: RequestOptions = RequestOptions.none(),
+ ): CompletableFuture
+
+ /**
+ * Crawls the sitemap of the given domain and returns all discovered page URLs. Supports sitemap
+ * index files (recursive), parallel fetching with concurrency control, deduplication, and
+ * filters out non-page resources (images, PDFs, etc.).
+ */
+ fun webScrapeSitemap(
+ params: BrandWebScrapeSitemapParams
+ ): CompletableFuture =
+ webScrapeSitemap(params, RequestOptions.none())
+
+ /** @see webScrapeSitemap */
+ fun webScrapeSitemap(
+ params: BrandWebScrapeSitemapParams,
+ requestOptions: RequestOptions = RequestOptions.none(),
+ ): CompletableFuture
+
/** A view of [BrandServiceAsync] that provides access to raw HTTP responses for each method. */
interface WithRawResponse {
@@ -607,5 +673,65 @@ interface BrandServiceAsync {
params: BrandStyleguideParams,
requestOptions: RequestOptions = RequestOptions.none(),
): CompletableFuture>
+
+ /**
+ * Returns a raw HTTP response for `get /web/scrape/html`, but is otherwise the same as
+ * [BrandServiceAsync.webScrapeHtml].
+ */
+ fun webScrapeHtml(
+ params: BrandWebScrapeHtmlParams
+ ): CompletableFuture> =
+ webScrapeHtml(params, RequestOptions.none())
+
+ /** @see webScrapeHtml */
+ fun webScrapeHtml(
+ params: BrandWebScrapeHtmlParams,
+ requestOptions: RequestOptions = RequestOptions.none(),
+ ): CompletableFuture>
+
+ /**
+ * Returns a raw HTTP response for `get /web/scrape/images`, but is otherwise the same as
+ * [BrandServiceAsync.webScrapeImages].
+ */
+ fun webScrapeImages(
+ params: BrandWebScrapeImagesParams
+ ): CompletableFuture> =
+ webScrapeImages(params, RequestOptions.none())
+
+ /** @see webScrapeImages */
+ fun webScrapeImages(
+ params: BrandWebScrapeImagesParams,
+ requestOptions: RequestOptions = RequestOptions.none(),
+ ): CompletableFuture>
+
+ /**
+ * Returns a raw HTTP response for `get /web/scrape/markdown`, but is otherwise the same as
+ * [BrandServiceAsync.webScrapeMd].
+ */
+ fun webScrapeMd(
+ params: BrandWebScrapeMdParams
+ ): CompletableFuture> =
+ webScrapeMd(params, RequestOptions.none())
+
+ /** @see webScrapeMd */
+ fun webScrapeMd(
+ params: BrandWebScrapeMdParams,
+ requestOptions: RequestOptions = RequestOptions.none(),
+ ): CompletableFuture>
+
+ /**
+ * Returns a raw HTTP response for `get /web/scrape/sitemap`, but is otherwise the same as
+ * [BrandServiceAsync.webScrapeSitemap].
+ */
+ fun webScrapeSitemap(
+ params: BrandWebScrapeSitemapParams
+ ): CompletableFuture> =
+ webScrapeSitemap(params, RequestOptions.none())
+
+ /** @see webScrapeSitemap */
+ fun webScrapeSitemap(
+ params: BrandWebScrapeSitemapParams,
+ requestOptions: RequestOptions = RequestOptions.none(),
+ ): CompletableFuture>
}
}
diff --git a/brand-dev-java-core/src/main/kotlin/com/branddev/api/services/async/BrandServiceAsyncImpl.kt b/brand-dev-java-core/src/main/kotlin/com/branddev/api/services/async/BrandServiceAsyncImpl.kt
index 4592ca1..393e4e3 100644
--- a/brand-dev-java-core/src/main/kotlin/com/branddev/api/services/async/BrandServiceAsyncImpl.kt
+++ b/brand-dev-java-core/src/main/kotlin/com/branddev/api/services/async/BrandServiceAsyncImpl.kt
@@ -47,6 +47,14 @@ import com.branddev.api.models.brand.BrandScreenshotParams
import com.branddev.api.models.brand.BrandScreenshotResponse
import com.branddev.api.models.brand.BrandStyleguideParams
import com.branddev.api.models.brand.BrandStyleguideResponse
+import com.branddev.api.models.brand.BrandWebScrapeHtmlParams
+import com.branddev.api.models.brand.BrandWebScrapeHtmlResponse
+import com.branddev.api.models.brand.BrandWebScrapeImagesParams
+import com.branddev.api.models.brand.BrandWebScrapeImagesResponse
+import com.branddev.api.models.brand.BrandWebScrapeMdParams
+import com.branddev.api.models.brand.BrandWebScrapeMdResponse
+import com.branddev.api.models.brand.BrandWebScrapeSitemapParams
+import com.branddev.api.models.brand.BrandWebScrapeSitemapResponse
import java.util.concurrent.CompletableFuture
import java.util.function.Consumer
@@ -174,6 +182,34 @@ class BrandServiceAsyncImpl internal constructor(private val clientOptions: Clie
// get /brand/styleguide
withRawResponse().styleguide(params, requestOptions).thenApply { it.parse() }
+ override fun webScrapeHtml(
+ params: BrandWebScrapeHtmlParams,
+ requestOptions: RequestOptions,
+ ): CompletableFuture =
+ // get /web/scrape/html
+ withRawResponse().webScrapeHtml(params, requestOptions).thenApply { it.parse() }
+
+ override fun webScrapeImages(
+ params: BrandWebScrapeImagesParams,
+ requestOptions: RequestOptions,
+ ): CompletableFuture =
+ // get /web/scrape/images
+ withRawResponse().webScrapeImages(params, requestOptions).thenApply { it.parse() }
+
+ override fun webScrapeMd(
+ params: BrandWebScrapeMdParams,
+ requestOptions: RequestOptions,
+ ): CompletableFuture =
+ // get /web/scrape/markdown
+ withRawResponse().webScrapeMd(params, requestOptions).thenApply { it.parse() }
+
+ override fun webScrapeSitemap(
+ params: BrandWebScrapeSitemapParams,
+ requestOptions: RequestOptions,
+ ): CompletableFuture =
+ // get /web/scrape/sitemap
+ withRawResponse().webScrapeSitemap(params, requestOptions).thenApply { it.parse() }
+
class WithRawResponseImpl internal constructor(private val clientOptions: ClientOptions) :
BrandServiceAsync.WithRawResponse {
@@ -671,5 +707,125 @@ class BrandServiceAsyncImpl internal constructor(private val clientOptions: Clie
}
}
}
+
+ private val webScrapeHtmlHandler: Handler =
+ jsonHandler(clientOptions.jsonMapper)
+
+ override fun webScrapeHtml(
+ params: BrandWebScrapeHtmlParams,
+ requestOptions: RequestOptions,
+ ): CompletableFuture> {
+ val request =
+ HttpRequest.builder()
+ .method(HttpMethod.GET)
+ .baseUrl(clientOptions.baseUrl())
+ .addPathSegments("web", "scrape", "html")
+ .build()
+ .prepareAsync(clientOptions, params)
+ val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions))
+ return request
+ .thenComposeAsync { clientOptions.httpClient.executeAsync(it, requestOptions) }
+ .thenApply { response ->
+ errorHandler.handle(response).parseable {
+ response
+ .use { webScrapeHtmlHandler.handle(it) }
+ .also {
+ if (requestOptions.responseValidation!!) {
+ it.validate()
+ }
+ }
+ }
+ }
+ }
+
+ private val webScrapeImagesHandler: Handler =
+ jsonHandler(clientOptions.jsonMapper)
+
+ override fun webScrapeImages(
+ params: BrandWebScrapeImagesParams,
+ requestOptions: RequestOptions,
+ ): CompletableFuture> {
+ val request =
+ HttpRequest.builder()
+ .method(HttpMethod.GET)
+ .baseUrl(clientOptions.baseUrl())
+ .addPathSegments("web", "scrape", "images")
+ .build()
+ .prepareAsync(clientOptions, params)
+ val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions))
+ return request
+ .thenComposeAsync { clientOptions.httpClient.executeAsync(it, requestOptions) }
+ .thenApply { response ->
+ errorHandler.handle(response).parseable {
+ response
+ .use { webScrapeImagesHandler.handle(it) }
+ .also {
+ if (requestOptions.responseValidation!!) {
+ it.validate()
+ }
+ }
+ }
+ }
+ }
+
+ private val webScrapeMdHandler: Handler =
+ jsonHandler(clientOptions.jsonMapper)
+
+ override fun webScrapeMd(
+ params: BrandWebScrapeMdParams,
+ requestOptions: RequestOptions,
+ ): CompletableFuture> {
+ val request =
+ HttpRequest.builder()
+ .method(HttpMethod.GET)
+ .baseUrl(clientOptions.baseUrl())
+ .addPathSegments("web", "scrape", "markdown")
+ .build()
+ .prepareAsync(clientOptions, params)
+ val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions))
+ return request
+ .thenComposeAsync { clientOptions.httpClient.executeAsync(it, requestOptions) }
+ .thenApply { response ->
+ errorHandler.handle(response).parseable {
+ response
+ .use { webScrapeMdHandler.handle(it) }
+ .also {
+ if (requestOptions.responseValidation!!) {
+ it.validate()
+ }
+ }
+ }
+ }
+ }
+
+ private val webScrapeSitemapHandler: Handler =
+ jsonHandler(clientOptions.jsonMapper)
+
+ override fun webScrapeSitemap(
+ params: BrandWebScrapeSitemapParams,
+ requestOptions: RequestOptions,
+ ): CompletableFuture> {
+ val request =
+ HttpRequest.builder()
+ .method(HttpMethod.GET)
+ .baseUrl(clientOptions.baseUrl())
+ .addPathSegments("web", "scrape", "sitemap")
+ .build()
+ .prepareAsync(clientOptions, params)
+ val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions))
+ return request
+ .thenComposeAsync { clientOptions.httpClient.executeAsync(it, requestOptions) }
+ .thenApply { response ->
+ errorHandler.handle(response).parseable {
+ response
+ .use { webScrapeSitemapHandler.handle(it) }
+ .also {
+ if (requestOptions.responseValidation!!) {
+ it.validate()
+ }
+ }
+ }
+ }
+ }
}
}
diff --git a/brand-dev-java-core/src/main/kotlin/com/branddev/api/services/blocking/BrandService.kt b/brand-dev-java-core/src/main/kotlin/com/branddev/api/services/blocking/BrandService.kt
index 01b6c49..e7ea6df 100644
--- a/brand-dev-java-core/src/main/kotlin/com/branddev/api/services/blocking/BrandService.kt
+++ b/brand-dev-java-core/src/main/kotlin/com/branddev/api/services/blocking/BrandService.kt
@@ -37,6 +37,14 @@ import com.branddev.api.models.brand.BrandScreenshotParams
import com.branddev.api.models.brand.BrandScreenshotResponse
import com.branddev.api.models.brand.BrandStyleguideParams
import com.branddev.api.models.brand.BrandStyleguideResponse
+import com.branddev.api.models.brand.BrandWebScrapeHtmlParams
+import com.branddev.api.models.brand.BrandWebScrapeHtmlResponse
+import com.branddev.api.models.brand.BrandWebScrapeImagesParams
+import com.branddev.api.models.brand.BrandWebScrapeImagesResponse
+import com.branddev.api.models.brand.BrandWebScrapeMdParams
+import com.branddev.api.models.brand.BrandWebScrapeMdResponse
+import com.branddev.api.models.brand.BrandWebScrapeSitemapParams
+import com.branddev.api.models.brand.BrandWebScrapeSitemapResponse
import com.google.errorprone.annotations.MustBeClosed
import java.util.function.Consumer
@@ -300,6 +308,59 @@ interface BrandService {
requestOptions: RequestOptions = RequestOptions.none(),
): BrandStyleguideResponse
+ /**
+ * Scrapes the given URL and returns the raw HTML content of the page. Uses automatic proxy
+ * escalation to handle blocked sites.
+ */
+ fun webScrapeHtml(params: BrandWebScrapeHtmlParams): BrandWebScrapeHtmlResponse =
+ webScrapeHtml(params, RequestOptions.none())
+
+ /** @see webScrapeHtml */
+ fun webScrapeHtml(
+ params: BrandWebScrapeHtmlParams,
+ requestOptions: RequestOptions = RequestOptions.none(),
+ ): BrandWebScrapeHtmlResponse
+
+ /**
+ * Scrapes all images from the given URL. Extracts images from img, svg, picture/source, link,
+ * and video elements including inline SVGs, base64 data URIs, and standard URLs.
+ */
+ fun webScrapeImages(params: BrandWebScrapeImagesParams): BrandWebScrapeImagesResponse =
+ webScrapeImages(params, RequestOptions.none())
+
+ /** @see webScrapeImages */
+ fun webScrapeImages(
+ params: BrandWebScrapeImagesParams,
+ requestOptions: RequestOptions = RequestOptions.none(),
+ ): BrandWebScrapeImagesResponse
+
+ /**
+ * Scrapes the given URL, converts the HTML content to GitHub Flavored Markdown (GFM), and
+ * returns the result. Uses automatic proxy escalation to handle blocked sites.
+ */
+ fun webScrapeMd(params: BrandWebScrapeMdParams): BrandWebScrapeMdResponse =
+ webScrapeMd(params, RequestOptions.none())
+
+ /** @see webScrapeMd */
+ fun webScrapeMd(
+ params: BrandWebScrapeMdParams,
+ requestOptions: RequestOptions = RequestOptions.none(),
+ ): BrandWebScrapeMdResponse
+
+ /**
+ * Crawls the sitemap of the given domain and returns all discovered page URLs. Supports sitemap
+ * index files (recursive), parallel fetching with concurrency control, deduplication, and
+ * filters out non-page resources (images, PDFs, etc.).
+ */
+ fun webScrapeSitemap(params: BrandWebScrapeSitemapParams): BrandWebScrapeSitemapResponse =
+ webScrapeSitemap(params, RequestOptions.none())
+
+ /** @see webScrapeSitemap */
+ fun webScrapeSitemap(
+ params: BrandWebScrapeSitemapParams,
+ requestOptions: RequestOptions = RequestOptions.none(),
+ ): BrandWebScrapeSitemapResponse
+
/** A view of [BrandService] that provides access to raw HTTP responses for each method. */
interface WithRawResponse {
@@ -606,5 +667,71 @@ interface BrandService {
params: BrandStyleguideParams,
requestOptions: RequestOptions = RequestOptions.none(),
): HttpResponseFor
+
+ /**
+ * Returns a raw HTTP response for `get /web/scrape/html`, but is otherwise the same as
+ * [BrandService.webScrapeHtml].
+ */
+ @MustBeClosed
+ fun webScrapeHtml(
+ params: BrandWebScrapeHtmlParams
+ ): HttpResponseFor =
+ webScrapeHtml(params, RequestOptions.none())
+
+ /** @see webScrapeHtml */
+ @MustBeClosed
+ fun webScrapeHtml(
+ params: BrandWebScrapeHtmlParams,
+ requestOptions: RequestOptions = RequestOptions.none(),
+ ): HttpResponseFor
+
+ /**
+ * Returns a raw HTTP response for `get /web/scrape/images`, but is otherwise the same as
+ * [BrandService.webScrapeImages].
+ */
+ @MustBeClosed
+ fun webScrapeImages(
+ params: BrandWebScrapeImagesParams
+ ): HttpResponseFor =
+ webScrapeImages(params, RequestOptions.none())
+
+ /** @see webScrapeImages */
+ @MustBeClosed
+ fun webScrapeImages(
+ params: BrandWebScrapeImagesParams,
+ requestOptions: RequestOptions = RequestOptions.none(),
+ ): HttpResponseFor
+
+ /**
+ * Returns a raw HTTP response for `get /web/scrape/markdown`, but is otherwise the same as
+ * [BrandService.webScrapeMd].
+ */
+ @MustBeClosed
+ fun webScrapeMd(params: BrandWebScrapeMdParams): HttpResponseFor =
+ webScrapeMd(params, RequestOptions.none())
+
+ /** @see webScrapeMd */
+ @MustBeClosed
+ fun webScrapeMd(
+ params: BrandWebScrapeMdParams,
+ requestOptions: RequestOptions = RequestOptions.none(),
+ ): HttpResponseFor
+
+ /**
+ * Returns a raw HTTP response for `get /web/scrape/sitemap`, but is otherwise the same as
+ * [BrandService.webScrapeSitemap].
+ */
+ @MustBeClosed
+ fun webScrapeSitemap(
+ params: BrandWebScrapeSitemapParams
+ ): HttpResponseFor =
+ webScrapeSitemap(params, RequestOptions.none())
+
+ /** @see webScrapeSitemap */
+ @MustBeClosed
+ fun webScrapeSitemap(
+ params: BrandWebScrapeSitemapParams,
+ requestOptions: RequestOptions = RequestOptions.none(),
+ ): HttpResponseFor
}
}
diff --git a/brand-dev-java-core/src/main/kotlin/com/branddev/api/services/blocking/BrandServiceImpl.kt b/brand-dev-java-core/src/main/kotlin/com/branddev/api/services/blocking/BrandServiceImpl.kt
index 1fa6a43..9a70742 100644
--- a/brand-dev-java-core/src/main/kotlin/com/branddev/api/services/blocking/BrandServiceImpl.kt
+++ b/brand-dev-java-core/src/main/kotlin/com/branddev/api/services/blocking/BrandServiceImpl.kt
@@ -47,6 +47,14 @@ import com.branddev.api.models.brand.BrandScreenshotParams
import com.branddev.api.models.brand.BrandScreenshotResponse
import com.branddev.api.models.brand.BrandStyleguideParams
import com.branddev.api.models.brand.BrandStyleguideResponse
+import com.branddev.api.models.brand.BrandWebScrapeHtmlParams
+import com.branddev.api.models.brand.BrandWebScrapeHtmlResponse
+import com.branddev.api.models.brand.BrandWebScrapeImagesParams
+import com.branddev.api.models.brand.BrandWebScrapeImagesResponse
+import com.branddev.api.models.brand.BrandWebScrapeMdParams
+import com.branddev.api.models.brand.BrandWebScrapeMdResponse
+import com.branddev.api.models.brand.BrandWebScrapeSitemapParams
+import com.branddev.api.models.brand.BrandWebScrapeSitemapResponse
import java.util.function.Consumer
class BrandServiceImpl internal constructor(private val clientOptions: ClientOptions) :
@@ -173,6 +181,34 @@ class BrandServiceImpl internal constructor(private val clientOptions: ClientOpt
// get /brand/styleguide
withRawResponse().styleguide(params, requestOptions).parse()
+ override fun webScrapeHtml(
+ params: BrandWebScrapeHtmlParams,
+ requestOptions: RequestOptions,
+ ): BrandWebScrapeHtmlResponse =
+ // get /web/scrape/html
+ withRawResponse().webScrapeHtml(params, requestOptions).parse()
+
+ override fun webScrapeImages(
+ params: BrandWebScrapeImagesParams,
+ requestOptions: RequestOptions,
+ ): BrandWebScrapeImagesResponse =
+ // get /web/scrape/images
+ withRawResponse().webScrapeImages(params, requestOptions).parse()
+
+ override fun webScrapeMd(
+ params: BrandWebScrapeMdParams,
+ requestOptions: RequestOptions,
+ ): BrandWebScrapeMdResponse =
+ // get /web/scrape/markdown
+ withRawResponse().webScrapeMd(params, requestOptions).parse()
+
+ override fun webScrapeSitemap(
+ params: BrandWebScrapeSitemapParams,
+ requestOptions: RequestOptions,
+ ): BrandWebScrapeSitemapResponse =
+ // get /web/scrape/sitemap
+ withRawResponse().webScrapeSitemap(params, requestOptions).parse()
+
class WithRawResponseImpl internal constructor(private val clientOptions: ClientOptions) :
BrandService.WithRawResponse {
@@ -622,5 +658,113 @@ class BrandServiceImpl internal constructor(private val clientOptions: ClientOpt
}
}
}
+
+ private val webScrapeHtmlHandler: Handler =
+ jsonHandler(clientOptions.jsonMapper)
+
+ override fun webScrapeHtml(
+ params: BrandWebScrapeHtmlParams,
+ requestOptions: RequestOptions,
+ ): HttpResponseFor {
+ val request =
+ HttpRequest.builder()
+ .method(HttpMethod.GET)
+ .baseUrl(clientOptions.baseUrl())
+ .addPathSegments("web", "scrape", "html")
+ .build()
+ .prepare(clientOptions, params)
+ val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions))
+ val response = clientOptions.httpClient.execute(request, requestOptions)
+ return errorHandler.handle(response).parseable {
+ response
+ .use { webScrapeHtmlHandler.handle(it) }
+ .also {
+ if (requestOptions.responseValidation!!) {
+ it.validate()
+ }
+ }
+ }
+ }
+
+ private val webScrapeImagesHandler: Handler