diff --git a/src/main/java/com/apple/itunes/storekit/client/APIError.java b/src/main/java/com/apple/itunes/storekit/client/APIError.java index dc3a84d2..92225258 100644 --- a/src/main/java/com/apple/itunes/storekit/client/APIError.java +++ b/src/main/java/com/apple/itunes/storekit/client/APIError.java @@ -356,6 +356,62 @@ public enum APIError { */ TRANSACTION_ID_IS_NOT_ORIGINAL_TRANSACTION_ID_ERROR(4000187L), + /** + * An error the API returns that indicates the performance test request is invalid. + * + * @see InvalidPerformanceTestRequestError + */ + INVALID_PERFORMANCE_TEST_REQUEST(4000211L), + + /** + * An error that indicates the request ID is invalid. + * + * @see InvalidRequestIdError + */ + INVALID_REQUEST_ID(4000212L), + + /** + * An error that indicates an error with an existing test. + * + * @see ExistingPerformanceTestRunError + */ + EXISTING_PERFORMANCE_TEST_RUN(4000213L), + + /** + * An error that indicates the URL is invalid. + * + * @see BadRequestRealtimeUrlError + */ + BAD_REQUEST_REALTIME_URL(4000215L), + + /** + * An error that indicates the image size provided is invalid. + * + * @see BadRequestImageSizeError + */ + BAD_REQUEST_IMAGE_SIZE(4000216L), + + /** + * An error that indicates there are too many bullet points. + * + * @see BadRequestTooManyBulletPointsError + */ + BAD_REQUEST_TOO_MANY_BULLET_POINTS(4000218L), + + /** + * An error that indicates the text for a bullet point is too long. + * + * @see BadRequestBulletPointTextTooLongError + */ + BAD_REQUEST_BULLET_POINT_TEXT_TOO_LONG(4000219L), + + /** + * An error that indicates that no image object is included, but the request indicates that the header should be placed above the image. + * + * @see BadRequestAboveImageRequiresAnImageError + */ + BAD_REQUEST_ABOVE_IMAGE_REQUIRES_AN_IMAGE(4000224L), + /** * An error that indicates the subscription doesn't qualify for a renewal-date extension due to its subscription state. * @@ -412,6 +468,13 @@ public enum APIError { */ IMAGE_IN_USE(4030019L), + /** + * An error that indicates that passing a performance test is required before you can set a URL for the production environment. + * + * @see ForbiddenNoPassingTestError + */ + FORBIDDEN_NO_PASSING_TEST(4030026L), + /** * An error that indicates the App Store account wasn't found. * @@ -496,6 +559,13 @@ public enum APIError { */ MESSAGE_NOT_FOUND(4040015L), + /** + * An error the API returns if the service can’t find the specified test run. + * + * @see PerformanceTestRunNotFoundError + */ + PERFORMANCE_TEST_RUN_NOT_FOUND(4040018L), + /** * An error response that indicates an app transaction doesn’t exist for the specified customer. * @@ -503,6 +573,20 @@ public enum APIError { */ APP_TRANSACTION_DOES_NOT_EXIST_ERROR(4040019L), + /** + * An error that indicates a default message isn’t configured. + * + * @see DefaultMessageNotFoundError + */ + DEFAULT_MESSAGE_NOT_FOUND(4040020L), + + /** + * An error that indicates that the URL for your endpoint isn’t configured. + * + * @see RealtimeUrlNotFoundError + */ + REALTIME_URL_NOT_FOUND(4040021L), + /** * An error that indicates the image identifier already exists. * diff --git a/src/main/java/com/apple/itunes/storekit/client/BaseAppStoreServerAPIClient.java b/src/main/java/com/apple/itunes/storekit/client/BaseAppStoreServerAPIClient.java index 4aa30f1a..6a73cd4b 100644 --- a/src/main/java/com/apple/itunes/storekit/client/BaseAppStoreServerAPIClient.java +++ b/src/main/java/com/apple/itunes/storekit/client/BaseAppStoreServerAPIClient.java @@ -7,6 +7,7 @@ import com.apple.itunes.storekit.model.ConsumptionRequest; import com.apple.itunes.storekit.model.ConsumptionRequestV1; import com.apple.itunes.storekit.model.DefaultConfigurationRequest; +import com.apple.itunes.storekit.model.DefaultConfigurationResponse; import com.apple.itunes.storekit.model.Environment; import com.apple.itunes.storekit.model.ErrorPayload; import com.apple.itunes.storekit.model.ExtendRenewalDateRequest; @@ -14,12 +15,18 @@ import com.apple.itunes.storekit.model.GetImageListResponse; import com.apple.itunes.storekit.model.GetMessageListResponse; import com.apple.itunes.storekit.model.HistoryResponse; +import com.apple.itunes.storekit.model.ImageSize; import com.apple.itunes.storekit.model.MassExtendRenewalDateRequest; import com.apple.itunes.storekit.model.MassExtendRenewalDateResponse; import com.apple.itunes.storekit.model.MassExtendRenewalDateStatusResponse; import com.apple.itunes.storekit.model.NotificationHistoryRequest; import com.apple.itunes.storekit.model.NotificationHistoryResponse; import com.apple.itunes.storekit.model.OrderLookupResponse; +import com.apple.itunes.storekit.model.PerformanceTestRequest; +import com.apple.itunes.storekit.model.PerformanceTestResponse; +import com.apple.itunes.storekit.model.PerformanceTestResultResponse; +import com.apple.itunes.storekit.model.RealtimeUrlRequest; +import com.apple.itunes.storekit.model.RealtimeUrlResponse; import com.apple.itunes.storekit.model.RefundHistoryResponse; import com.apple.itunes.storekit.model.SendTestNotificationResponse; import com.apple.itunes.storekit.model.Status; @@ -282,7 +289,7 @@ public HistoryResponse getTransactionHistory(String transactionId, String revisi * Get a customer’s in-app purchase transaction history for your app. * * @param transactionId The identifier of a transaction that belongs to the customer, and which may be an original transaction identifier. - * @param revision A token you provide to get the next set of up to 20 transactions. All responses include a revision token. Note: For requests that use the revision token, include the same query parameters from the initial request. Use the revision token from the previous HistoryResponse. + * @param revision A token you provide to get the next set of up to 20 transactions. All responses include a revision token. Note: For requests that use the revision token, include the same query parameters from the initial request. Use the revision token from the previous HistoryResponse. * @param version The version of the Get Transaction History endpoint to use. V2 is recommended. * @return A response that contains the customer’s transaction history for an app. * @throws APIException If a response was returned indicating the request could not be processed @@ -402,16 +409,29 @@ public void setAppAccountToken(String originalTransactionId, UpdateAppAccountTok } /** - * Upload an image to use for retention messaging. + * @see #uploadImage(UUID, byte[], ImageSize) + */ + @Deprecated(since = "5.1.0") + public void uploadImage(UUID imageIdentifier, byte[] image) throws APIException, IOException { + uploadImage(imageIdentifier, image, null); + } + + /** + * Uploads an image to use for retention messaging. * * @param imageIdentifier A UUID you provide to uniquely identify the image you upload. * @param image The image file to upload. + * @param imageSize The size of the image you upload. * @throws APIException If a response was returned indicating the request could not be processed * @throws IOException If an exception was thrown while making the request * @see Upload Image */ - public void uploadImage(UUID imageIdentifier, byte[] image) throws APIException, IOException { - makeHttpCall("/inApps/v1/messaging/image/" + imageIdentifier, "PUT", Map.of(), image, Void.class, PNG); + public void uploadImage(UUID imageIdentifier, byte[] image, ImageSize imageSize) throws APIException, IOException { + HashMap> queryParameters = new HashMap<>(); + if (imageSize != null) { + queryParameters.put("imageSize", List.of(imageSize.name())); + } + makeHttpCall("/inApps/v1/messaging/image/" + imageIdentifier, "PUT", queryParameters, image, Void.class, PNG); } /** @@ -502,6 +522,81 @@ public void deleteDefaultMessage(String productId, String locale) throws APIExce makeHttpCall("/inApps/v1/messaging/default/" + productId + "/" + locale, "DELETE", Map.of(), null, Void.class, null); } + /** + * Gets the default message for a specific product in a specific locale, if it’s configured. + * + * @param productId The product identifier of the message. + * @param locale The locale of the message. + * @return The response body that contains the default configuration information. + * @throws APIException If a response was returned indicating the request could not be processed + * @throws IOException If an exception was thrown while making the request + * @see Get Default Message + */ + public DefaultConfigurationResponse getDefaultMessage(String productId, String locale) throws APIException, IOException { + return makeHttpCall("/inApps/v1/messaging/default/" + productId + "/" + locale, "GET", Map.of(), null, DefaultConfigurationResponse.class, null); + } + + /** + * Configures the URL for your Get Retention Message endpoint in the sandbox and production environments. + * + * @param realtimeUrlRequest The request body that includes your endpoint’s URL. + * @throws APIException If a response was returned indicating the request could not be processed + * @throws IOException If an exception was thrown while making the request + * @see Configure Realtime URL + */ + public void configureRealtimeURL(RealtimeUrlRequest realtimeUrlRequest) throws APIException, IOException { + makeHttpCall("/inApps/v1/messaging/realtime/url", "PUT", Map.of(), realtimeUrlRequest, Void.class, JSON); + } + + /** + * Deletes the URL for your Get Retention Message endpoint, in the sandbox or production environments. + * + * @throws APIException If a response was returned indicating the request could not be processed + * @throws IOException If an exception was thrown while making the request + * @see Delete Realtime URL + */ + public void deleteRealtimeURL() throws APIException, IOException { + makeHttpCall("/inApps/v1/messaging/realtime/url", "DELETE", Map.of(), null, Void.class, null); + } + + /** + * Gets the URL for real-time messages that points to your Get Retention Message endpoint, which you previously configured. + * + * @return The response body that contains the URL for your Get Retention Message endpoint. + * @throws APIException If a response was returned indicating the request could not be processed + * @throws IOException If an exception was thrown while making the request + * @see Get Realtime URL + */ + public RealtimeUrlResponse getRealtimeURL() throws APIException, IOException { + return makeHttpCall("/inApps/v1/messaging/realtime/url", "GET", Map.of(), null, RealtimeUrlResponse.class, null); + } + + /** + * Initiates a performance test of your Get Retention Message endpoint in the sandbox environment. + * + * @param performanceTestRequest The request body which specifies a transaction identifier of an In-App Purchase to use for this test. + * @return The performance test response object. + * @throws APIException If a response was returned indicating the request could not be processed + * @throws IOException If an exception was thrown while making the request + * @see Initiate Performance Test + */ + public PerformanceTestResponse initiatePerformanceTest(PerformanceTestRequest performanceTestRequest) throws APIException, IOException { + return makeHttpCall("/inApps/v1/messaging/performanceTest", "POST", Map.of(), performanceTestRequest, PerformanceTestResponse.class, JSON); + } + + /** + * Gets the results of the performance test for the specified identifier. + * + * @param requestId The ID of the performance test to return, which you receive in the PerformanceTestResponse when you call Initiate Performance Test. + * @return An object the API returns that describes the performance test results. + * @throws APIException If a response was returned indicating the request could not be processed + * @throws IOException If an exception was thrown while making the request + * @see Get Performance Test Results + */ + public PerformanceTestResultResponse getPerformanceTestResults(String requestId) throws APIException, IOException { + return makeHttpCall("/inApps/v1/messaging/performanceTest/result/" + requestId, "GET", Map.of(), null, PerformanceTestResultResponse.class, null); + } + /** * Get a customer’s app transaction information for your app. * diff --git a/src/main/java/com/apple/itunes/storekit/model/BulletPoint.java b/src/main/java/com/apple/itunes/storekit/model/BulletPoint.java new file mode 100644 index 00000000..3b00182e --- /dev/null +++ b/src/main/java/com/apple/itunes/storekit/model/BulletPoint.java @@ -0,0 +1,142 @@ +// Copyright (c) 2026 Apple Inc. Licensed under MIT License. + +package com.apple.itunes.storekit.model; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; + +import java.util.Objects; +import java.util.UUID; + +/** + * The text and its bullet-point image to include in a retention message’s bulleted list. + * + * @see BulletPoint + */ +@JsonInclude(JsonInclude.Include.NON_NULL) +public class BulletPoint { + private static final String SERIALIZED_NAME_TEXT = "text"; + private static final String SERIALIZED_NAME_IMAGE_IDENTIFIER = "imageIdentifier"; + private static final String SERIALIZED_NAME_ALT_TEXT = "altText"; + @JsonProperty(value = SERIALIZED_NAME_TEXT, required = true) + private String text; + @JsonProperty(value = SERIALIZED_NAME_IMAGE_IDENTIFIER, required = true) + private UUID imageIdentifier; + @JsonProperty(value = SERIALIZED_NAME_ALT_TEXT, required = true) + private String altText; + + private static final int MAXIMUM_TEXT_LENGTH = 66; + private static final int MAXIMUM_ALT_TEXT_LENGTH = 150; + + private BulletPoint() { + } + + public BulletPoint(String text, + UUID imageIdentifier, + String altText) { + this.text = validateText(text); + this.imageIdentifier = Objects.requireNonNull(imageIdentifier); + this.altText = validateAltText(altText); + } + + public BulletPoint text(String text) { + this.text = validateText(text); + return this; + } + + /** + * The text of the individual bullet point. + * + * @return text + * @see bulletPointText + **/ + public String getText() { + return text; + } + + public void setText(String text) { + this.text = validateText(text); + } + + public BulletPoint imageIdentifier(UUID imageIdentifier) { + this.imageIdentifier = Objects.requireNonNull(imageIdentifier); + return this; + } + + /** + * The identifier of the image to use as the bullet point. + * + * @return imageIdentifier + * @see imageIdentifier + **/ + public UUID getImageIdentifier() { + return imageIdentifier; + } + + public void setImageIdentifier(UUID imageIdentifier) { + this.imageIdentifier = imageIdentifier; + } + + public BulletPoint altText(String altText) { + this.altText = validateAltText(altText); + return this; + } + + /** + * The alternative text you provide for the corresponding image of the bullet point. + * + * @return altText + * @see altText + **/ + public String getAltText() { + return altText; + } + + public void setAltText(String altText) { + this.altText = validateAltText(altText); + } + + private String validateText(String text) { + Objects.requireNonNull(text); + if (text.length() > MAXIMUM_TEXT_LENGTH) { + throw new IllegalArgumentException("text length longer than maximum allowed"); + } + return text; + } + + private String validateAltText(String altText) { + Objects.requireNonNull(altText); + if (altText.length() > MAXIMUM_ALT_TEXT_LENGTH) { + throw new IllegalArgumentException("altText length longer than maximum allowed"); + } + return altText; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + BulletPoint bulletPoint = (BulletPoint) o; + return Objects.equals(this.text, bulletPoint.text) && + Objects.equals(this.imageIdentifier, bulletPoint.imageIdentifier) && + Objects.equals(this.altText, bulletPoint.altText); + } + + @Override + public int hashCode() { + return Objects.hash(text, imageIdentifier, altText); + } + + @Override + public String toString() { + return "BulletPoint{" + + "text='" + text + '\'' + + ", imageIdentifier=" + imageIdentifier + + ", altText='" + altText + '\'' + + '}'; + } +} \ No newline at end of file diff --git a/src/main/java/com/apple/itunes/storekit/model/DefaultConfigurationRequest.java b/src/main/java/com/apple/itunes/storekit/model/DefaultConfigurationRequest.java index 76c98c2f..bb0f49dd 100644 --- a/src/main/java/com/apple/itunes/storekit/model/DefaultConfigurationRequest.java +++ b/src/main/java/com/apple/itunes/storekit/model/DefaultConfigurationRequest.java @@ -19,9 +19,17 @@ public class DefaultConfigurationRequest { @JsonProperty(SERIALIZED_NAME_MESSAGE_IDENTIFIER) private UUID messageIdentifier; + /** + * @deprecated Use {@link #DefaultConfigurationRequest(UUID)} instead. + */ + @Deprecated public DefaultConfigurationRequest() { } + public DefaultConfigurationRequest(UUID messageIdentifier) { + this.messageIdentifier = Objects.requireNonNull(messageIdentifier); + } + public DefaultConfigurationRequest messageIdentifier(UUID messageIdentifier) { this.messageIdentifier = messageIdentifier; return this; diff --git a/src/main/java/com/apple/itunes/storekit/model/DefaultConfigurationResponse.java b/src/main/java/com/apple/itunes/storekit/model/DefaultConfigurationResponse.java new file mode 100644 index 00000000..e7164fb8 --- /dev/null +++ b/src/main/java/com/apple/itunes/storekit/model/DefaultConfigurationResponse.java @@ -0,0 +1,72 @@ +// Copyright (c) 2026 Apple Inc. Licensed under MIT License. + +package com.apple.itunes.storekit.model; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; + +import java.util.Objects; +import java.util.UUID; + +/** + * The response body that contains the default configuration information. + * + * @see DefaultConfigurationResponse + */ +@JsonInclude(JsonInclude.Include.NON_NULL) +public class DefaultConfigurationResponse { + private static final String SERIALIZED_NAME_MESSAGE_IDENTIFIER = "messageIdentifier"; + @JsonProperty(value = SERIALIZED_NAME_MESSAGE_IDENTIFIER, required = true) + private UUID messageIdentifier; + + private DefaultConfigurationResponse() { + } + + public DefaultConfigurationResponse(UUID messageIdentifier) { + this.messageIdentifier = Objects.requireNonNull(messageIdentifier); + } + + public DefaultConfigurationResponse messageIdentifier(UUID messageIdentifier) { + this.messageIdentifier = Objects.requireNonNull(messageIdentifier); + return this; + } + + /** + * The message identifier of the retention message you configured as a default. + * + * @return messageIdentifier + * @see messageIdentifier + **/ + public UUID getMessageIdentifier() { + return messageIdentifier; + } + + public void setMessageIdentifier(UUID messageIdentifier) { + this.messageIdentifier = Objects.requireNonNull(messageIdentifier); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + DefaultConfigurationResponse defaultConfigurationResponse = (DefaultConfigurationResponse) o; + return Objects.equals(this.messageIdentifier, defaultConfigurationResponse.messageIdentifier); + } + + @Override + public int hashCode() { + return Objects.hash(messageIdentifier); + } + + @Override + public String toString() { + return "DefaultConfigurationResponse{" + + "messageIdentifier=" + messageIdentifier + + '}'; + } +} + diff --git a/src/main/java/com/apple/itunes/storekit/model/GetImageListResponseItem.java b/src/main/java/com/apple/itunes/storekit/model/GetImageListResponseItem.java index 8f5b5bf4..d906ebd6 100644 --- a/src/main/java/com/apple/itunes/storekit/model/GetImageListResponseItem.java +++ b/src/main/java/com/apple/itunes/storekit/model/GetImageListResponseItem.java @@ -17,10 +17,13 @@ public class GetImageListResponseItem { private static final String SERIALIZED_NAME_IMAGE_IDENTIFIER = "imageIdentifier"; private static final String SERIALIZED_NAME_IMAGE_STATE = "imageState"; + private static final String SERIALIZED_NAME_IMAGE_SIZE = "imageSize"; @JsonProperty(SERIALIZED_NAME_IMAGE_IDENTIFIER) private UUID imageIdentifier; @JsonProperty(SERIALIZED_NAME_IMAGE_STATE) private String imageState; + @JsonProperty(SERIALIZED_NAME_IMAGE_SIZE) + private String imageSize; @JsonAnySetter private Map unknownFields; @@ -76,6 +79,36 @@ public void setRawImageState(String rawImageState) { this.imageState = rawImageState; } + public GetImageListResponseItem imageSize(ImageSize imageSize) { + this.imageSize = imageSize != null ? imageSize.getValue() : null; + return this; + } + + /** + * The size of the image. + * + * @return imageSize + * @see imageSize + **/ + public ImageSize getImageSize() { + return imageSize != null ? ImageSize.fromValue(imageSize) : null; + } + + /** + * @see #getImageSize() + */ + public String getRawImageSize() { + return imageSize; + } + + public void setImageSize(ImageSize imageSize) { + this.imageSize = imageSize != null ? imageSize.getValue() : null; + } + + public void setRawImageSize(String rawImageSize) { + this.imageSize = rawImageSize; + } + public GetImageListResponseItem unknownFields(Map unknownFields) { this.unknownFields = unknownFields; @@ -106,12 +139,13 @@ public boolean equals(Object o) { GetImageListResponseItem getImageListResponseItem = (GetImageListResponseItem) o; return Objects.equals(this.imageIdentifier, getImageListResponseItem.imageIdentifier) && Objects.equals(this.imageState, getImageListResponseItem.imageState) && + Objects.equals(this.imageSize, getImageListResponseItem.imageSize) && Objects.equals(this.unknownFields, getImageListResponseItem.unknownFields); } @Override public int hashCode() { - return Objects.hash(imageIdentifier, imageState, unknownFields); + return Objects.hash(imageIdentifier, imageState, imageSize, unknownFields); } @Override @@ -119,6 +153,7 @@ public String toString() { return "GetImageListResponseItem{" + "imageIdentifier=" + imageIdentifier + ", imageState='" + imageState + '\'' + + ", imageSize='" + imageSize + '\'' + ", unknownFields=" + unknownFields + '}'; } diff --git a/src/main/java/com/apple/itunes/storekit/model/HeaderPosition.java b/src/main/java/com/apple/itunes/storekit/model/HeaderPosition.java new file mode 100644 index 00000000..dc45526b --- /dev/null +++ b/src/main/java/com/apple/itunes/storekit/model/HeaderPosition.java @@ -0,0 +1,42 @@ +// Copyright (c) 2026 Apple Inc. Licensed under MIT License. + +package com.apple.itunes.storekit.model; + +import com.fasterxml.jackson.annotation.JsonValue; + +/** + * The position where the header text appears in a message. + * + * @see headerPosition + */ +public enum HeaderPosition { + + ABOVE_BODY("ABOVE_BODY"), + ABOVE_IMAGE("ABOVE_IMAGE"); + + private final String value; + + HeaderPosition(String value) { + this.value = value; + } + + public static HeaderPosition fromValue(String value) { + for (HeaderPosition b : HeaderPosition.values()) { + if (b.value.equals(value)) { + return b; + } + } + return null; + } + + @JsonValue + public String getValue() { + return value; + } + + @Override + public String toString() { + return String.valueOf(value); + } +} + diff --git a/src/main/java/com/apple/itunes/storekit/model/ImageSize.java b/src/main/java/com/apple/itunes/storekit/model/ImageSize.java new file mode 100644 index 00000000..c9bb360b --- /dev/null +++ b/src/main/java/com/apple/itunes/storekit/model/ImageSize.java @@ -0,0 +1,42 @@ +// Copyright (c) 2026 Apple Inc. Licensed under MIT License. + +package com.apple.itunes.storekit.model; + +import com.fasterxml.jackson.annotation.JsonValue; + +/** + * The size of an image. + * + * @see imageSize + */ +public enum ImageSize { + + FULL_SIZE("FULL_SIZE"), + BULLET_POINT("BULLET_POINT"); + + private final String value; + + ImageSize(String value) { + this.value = value; + } + + public static ImageSize fromValue(String value) { + for (ImageSize b : ImageSize.values()) { + if (b.value.equals(value)) { + return b; + } + } + return null; + } + + @JsonValue + public String getValue() { + return value; + } + + @Override + public String toString() { + return String.valueOf(value); + } +} + diff --git a/src/main/java/com/apple/itunes/storekit/model/PerformanceTestConfig.java b/src/main/java/com/apple/itunes/storekit/model/PerformanceTestConfig.java new file mode 100644 index 00000000..4560e20a --- /dev/null +++ b/src/main/java/com/apple/itunes/storekit/model/PerformanceTestConfig.java @@ -0,0 +1,186 @@ +// Copyright (c) 2026 Apple Inc. Licensed under MIT License. + +package com.apple.itunes.storekit.model; + +import com.fasterxml.jackson.annotation.JsonAnySetter; +import com.fasterxml.jackson.annotation.JsonProperty; + +import java.util.Map; +import java.util.Objects; + +/** + * An object that enumerates the test configuration parameters. + * + * @see PerformanceTestConfig + */ +public class PerformanceTestConfig { + private static final String SERIALIZED_NAME_MAX_CONCURRENT_REQUESTS = "maxConcurrentRequests"; + private static final String SERIALIZED_NAME_TOTAL_REQUESTS = "totalRequests"; + private static final String SERIALIZED_NAME_TOTAL_DURATION = "totalDuration"; + private static final String SERIALIZED_NAME_RESPONSE_TIME_THRESHOLD = "responseTimeThreshold"; + private static final String SERIALIZED_NAME_SUCCESS_RATE_THRESHOLD = "successRateThreshold"; + @JsonProperty(SERIALIZED_NAME_MAX_CONCURRENT_REQUESTS) + private Long maxConcurrentRequests; + @JsonProperty(SERIALIZED_NAME_TOTAL_REQUESTS) + private Long totalRequests; + @JsonProperty(SERIALIZED_NAME_TOTAL_DURATION) + private Long totalDuration; + @JsonProperty(SERIALIZED_NAME_RESPONSE_TIME_THRESHOLD) + private Long responseTimeThreshold; + @JsonProperty(SERIALIZED_NAME_SUCCESS_RATE_THRESHOLD) + private Integer successRateThreshold; + @JsonAnySetter + private Map unknownFields; + + + public PerformanceTestConfig() { + } + + public PerformanceTestConfig maxConcurrentRequests(Long maxConcurrentRequests) { + this.maxConcurrentRequests = maxConcurrentRequests; + return this; + } + + /** + * The maximum number of concurrent requests the API allows. + * + * @return maxConcurrentRequests + * @see maxConcurrentRequests + **/ + public Long getMaxConcurrentRequests() { + return maxConcurrentRequests; + } + + public void setMaxConcurrentRequests(Long maxConcurrentRequests) { + this.maxConcurrentRequests = maxConcurrentRequests; + } + + public PerformanceTestConfig totalRequests(Long totalRequests) { + this.totalRequests = totalRequests; + return this; + } + + /** + * The total number of requests to make during the test. + * + * @return totalRequests + * @see totalRequests + **/ + public Long getTotalRequests() { + return totalRequests; + } + + public void setTotalRequests(Long totalRequests) { + this.totalRequests = totalRequests; + } + + public PerformanceTestConfig totalDuration(Long totalDuration) { + this.totalDuration = totalDuration; + return this; + } + + /** + * The total duration of the test in milliseconds. + * + * @return totalDuration + * @see totalDuration + **/ + public Long getTotalDuration() { + return totalDuration; + } + + public void setTotalDuration(Long totalDuration) { + this.totalDuration = totalDuration; + } + + public PerformanceTestConfig responseTimeThreshold(Long responseTimeThreshold) { + this.responseTimeThreshold = responseTimeThreshold; + return this; + } + + /** + * The maximum time your server has to respond when the system calls your Get Retention Message endpoint in the sandbox environment. + * + * @return responseTimeThreshold + * @see responseTimeThreshold + **/ + public Long getResponseTimeThreshold() { + return responseTimeThreshold; + } + + public void setResponseTimeThreshold(Long responseTimeThreshold) { + this.responseTimeThreshold = responseTimeThreshold; + } + + public PerformanceTestConfig successRateThreshold(Integer successRateThreshold) { + this.successRateThreshold = successRateThreshold; + return this; + } + + /** + * The success rate threshold percentage. + * + * @return successRateThreshold + * @see successRateThreshold + **/ + public Integer getSuccessRateThreshold() { + return successRateThreshold; + } + + public void setSuccessRateThreshold(Integer successRateThreshold) { + this.successRateThreshold = successRateThreshold; + } + + + public PerformanceTestConfig unknownFields(Map unknownFields) { + this.unknownFields = unknownFields; + return this; + } + + /** + Fields that are not recognized for this object + + @return A map of JSON keys to objects + */ + public Map getUnknownFields() { + return unknownFields; + } + + public void setUnknownFields(Map unknownFields) { + this.unknownFields = unknownFields; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + PerformanceTestConfig performanceTestConfig = (PerformanceTestConfig) o; + return Objects.equals(this.maxConcurrentRequests, performanceTestConfig.maxConcurrentRequests) && + Objects.equals(this.totalRequests, performanceTestConfig.totalRequests) && + Objects.equals(this.totalDuration, performanceTestConfig.totalDuration) && + Objects.equals(this.responseTimeThreshold, performanceTestConfig.responseTimeThreshold) && + Objects.equals(this.successRateThreshold, performanceTestConfig.successRateThreshold) && + Objects.equals(this.unknownFields, performanceTestConfig.unknownFields); + } + + @Override + public int hashCode() { + return Objects.hash(maxConcurrentRequests, totalRequests, totalDuration, responseTimeThreshold, successRateThreshold, unknownFields); + } + + @Override + public String toString() { + return "PerformanceTestConfig{" + + "maxConcurrentRequests=" + maxConcurrentRequests + + ", totalRequests=" + totalRequests + + ", totalDuration=" + totalDuration + + ", responseTimeThreshold=" + responseTimeThreshold + + ", successRateThreshold=" + successRateThreshold + + ", unknownFields=" + unknownFields + + '}'; + } +} diff --git a/src/main/java/com/apple/itunes/storekit/model/PerformanceTestRequest.java b/src/main/java/com/apple/itunes/storekit/model/PerformanceTestRequest.java new file mode 100644 index 00000000..c6f86cfa --- /dev/null +++ b/src/main/java/com/apple/itunes/storekit/model/PerformanceTestRequest.java @@ -0,0 +1,70 @@ +// Copyright (c) 2026 Apple Inc. Licensed under MIT License. + +package com.apple.itunes.storekit.model; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; + +import java.util.Objects; + +/** + * The request object you provide for a performance test that contains an original transaction identifier. + * + * @see PerformanceTestRequest + */ +@JsonInclude(JsonInclude.Include.NON_NULL) +public class PerformanceTestRequest { + private static final String SERIALIZED_NAME_ORIGINAL_TRANSACTION_ID = "originalTransactionId"; + @JsonProperty(value = SERIALIZED_NAME_ORIGINAL_TRANSACTION_ID, required = true) + private String originalTransactionId; + + private PerformanceTestRequest() { + } + + public PerformanceTestRequest(String originalTransactionId) { + this.originalTransactionId = Objects.requireNonNull(originalTransactionId); + } + + public PerformanceTestRequest originalTransactionId(String originalTransactionId) { + this.originalTransactionId = Objects.requireNonNull(originalTransactionId); + return this; + } + + /** + * The original transaction identifier of an In-App Purchase you initiate in the sandbox environment, to use as the purchase for this test. + * + * @return originalTransactionId + * @see originalTransactionId + **/ + public String getOriginalTransactionId() { + return originalTransactionId; + } + + public void setOriginalTransactionId(String originalTransactionId) { + this.originalTransactionId = Objects.requireNonNull(originalTransactionId); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + PerformanceTestRequest performanceTestRequest = (PerformanceTestRequest) o; + return Objects.equals(this.originalTransactionId, performanceTestRequest.originalTransactionId); + } + + @Override + public int hashCode() { + return Objects.hash(originalTransactionId); + } + + @Override + public String toString() { + return "PerformanceTestRequest{" + + "originalTransactionId='" + originalTransactionId + '\'' + + '}'; + } +} diff --git a/src/main/java/com/apple/itunes/storekit/model/PerformanceTestResponse.java b/src/main/java/com/apple/itunes/storekit/model/PerformanceTestResponse.java new file mode 100644 index 00000000..81f143de --- /dev/null +++ b/src/main/java/com/apple/itunes/storekit/model/PerformanceTestResponse.java @@ -0,0 +1,114 @@ +// Copyright (c) 2026 Apple Inc. Licensed under MIT License. + +package com.apple.itunes.storekit.model; + +import com.fasterxml.jackson.annotation.JsonAnySetter; +import com.fasterxml.jackson.annotation.JsonProperty; + +import java.util.Map; +import java.util.Objects; + +/** + * The performance test response object. + * + * @see PerformanceTestResponse + */ +public class PerformanceTestResponse { + private static final String SERIALIZED_NAME_CONFIG = "config"; + private static final String SERIALIZED_NAME_REQUEST_ID = "requestId"; + @JsonProperty(SERIALIZED_NAME_CONFIG) + private PerformanceTestConfig config; + @JsonProperty(SERIALIZED_NAME_REQUEST_ID) + private String requestId; + @JsonAnySetter + private Map unknownFields; + + + public PerformanceTestResponse() { + } + + public PerformanceTestResponse config(PerformanceTestConfig config) { + this.config = config; + return this; + } + + /** + * The performance test configuration object. + * + * @return config + * @see PerformanceTestConfig + **/ + public PerformanceTestConfig getConfig() { + return config; + } + + public void setConfig(PerformanceTestConfig config) { + this.config = config; + } + + public PerformanceTestResponse requestId(String requestId) { + this.requestId = requestId; + return this; + } + + /** + * The performance test request identifier. + * + * @return requestId + * @see requestId + **/ + public String getRequestId() { + return requestId; + } + + public void setRequestId(String requestId) { + this.requestId = requestId; + } + + + public PerformanceTestResponse unknownFields(Map unknownFields) { + this.unknownFields = unknownFields; + return this; + } + + /** + Fields that are not recognized for this object + + @return A map of JSON keys to objects + */ + public Map getUnknownFields() { + return unknownFields; + } + + public void setUnknownFields(Map unknownFields) { + this.unknownFields = unknownFields; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + PerformanceTestResponse performanceTestResponse = (PerformanceTestResponse) o; + return Objects.equals(this.config, performanceTestResponse.config) && + Objects.equals(this.requestId, performanceTestResponse.requestId) && + Objects.equals(this.unknownFields, performanceTestResponse.unknownFields); + } + + @Override + public int hashCode() { + return Objects.hash(config, requestId, unknownFields); + } + + @Override + public String toString() { + return "PerformanceTestResponse{" + + "config=" + config + + ", requestId='" + requestId + '\'' + + ", unknownFields=" + unknownFields + + '}'; + } +} diff --git a/src/main/java/com/apple/itunes/storekit/model/PerformanceTestResponseTimes.java b/src/main/java/com/apple/itunes/storekit/model/PerformanceTestResponseTimes.java new file mode 100644 index 00000000..8128ddd1 --- /dev/null +++ b/src/main/java/com/apple/itunes/storekit/model/PerformanceTestResponseTimes.java @@ -0,0 +1,186 @@ +// Copyright (c) 2026 Apple Inc. Licensed under MIT License. + +package com.apple.itunes.storekit.model; + +import com.fasterxml.jackson.annotation.JsonAnySetter; +import com.fasterxml.jackson.annotation.JsonProperty; + +import java.util.Map; +import java.util.Objects; + +/** + * An object that describes test response times. + * + * @see PerformanceTestResponseTimes + */ +public class PerformanceTestResponseTimes { + private static final String SERIALIZED_NAME_AVERAGE = "average"; + private static final String SERIALIZED_NAME_P50 = "p50"; + private static final String SERIALIZED_NAME_P90 = "p90"; + private static final String SERIALIZED_NAME_P95 = "p95"; + private static final String SERIALIZED_NAME_P99 = "p99"; + @JsonProperty(SERIALIZED_NAME_AVERAGE) + private Long average; + @JsonProperty(SERIALIZED_NAME_P50) + private Long p50; + @JsonProperty(SERIALIZED_NAME_P90) + private Long p90; + @JsonProperty(SERIALIZED_NAME_P95) + private Long p95; + @JsonProperty(SERIALIZED_NAME_P99) + private Long p99; + @JsonAnySetter + private Map unknownFields; + + + public PerformanceTestResponseTimes() { + } + + public PerformanceTestResponseTimes average(Long average) { + this.average = average; + return this; + } + + /** + * Average response time in milliseconds. + * + * @return average + * @see average + **/ + public Long getAverage() { + return average; + } + + public void setAverage(Long average) { + this.average = average; + } + + public PerformanceTestResponseTimes p50(Long p50) { + this.p50 = p50; + return this; + } + + /** + * The 50th percentile response time in milliseconds. + * + * @return p50 + * @see p50 + **/ + public Long getP50() { + return p50; + } + + public void setP50(Long p50) { + this.p50 = p50; + } + + public PerformanceTestResponseTimes p90(Long p90) { + this.p90 = p90; + return this; + } + + /** + * The 90th percentile response time in milliseconds. + * + * @return p90 + * @see p90 + **/ + public Long getP90() { + return p90; + } + + public void setP90(Long p90) { + this.p90 = p90; + } + + public PerformanceTestResponseTimes p95(Long p95) { + this.p95 = p95; + return this; + } + + /** + * The 95th percentile response time in milliseconds. + * + * @return p95 + * @see p95 + **/ + public Long getP95() { + return p95; + } + + public void setP95(Long p95) { + this.p95 = p95; + } + + public PerformanceTestResponseTimes p99(Long p99) { + this.p99 = p99; + return this; + } + + /** + * The 99th percentile response time in milliseconds. + * + * @return p99 + * @see p99 + **/ + public Long getP99() { + return p99; + } + + public void setP99(Long p99) { + this.p99 = p99; + } + + + public PerformanceTestResponseTimes unknownFields(Map unknownFields) { + this.unknownFields = unknownFields; + return this; + } + + /** + Fields that are not recognized for this object + + @return A map of JSON keys to objects + */ + public Map getUnknownFields() { + return unknownFields; + } + + public void setUnknownFields(Map unknownFields) { + this.unknownFields = unknownFields; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + PerformanceTestResponseTimes performanceTestResponseTimes = (PerformanceTestResponseTimes) o; + return Objects.equals(this.average, performanceTestResponseTimes.average) && + Objects.equals(this.p50, performanceTestResponseTimes.p50) && + Objects.equals(this.p90, performanceTestResponseTimes.p90) && + Objects.equals(this.p95, performanceTestResponseTimes.p95) && + Objects.equals(this.p99, performanceTestResponseTimes.p99) && + Objects.equals(this.unknownFields, performanceTestResponseTimes.unknownFields); + } + + @Override + public int hashCode() { + return Objects.hash(average, p50, p90, p95, p99, unknownFields); + } + + @Override + public String toString() { + return "PerformanceTestResponseTimes{" + + "average=" + average + + ", p50=" + p50 + + ", p90=" + p90 + + ", p95=" + p95 + + ", p99=" + p99 + + ", unknownFields=" + unknownFields + + '}'; + } +} diff --git a/src/main/java/com/apple/itunes/storekit/model/PerformanceTestResultResponse.java b/src/main/java/com/apple/itunes/storekit/model/PerformanceTestResultResponse.java new file mode 100644 index 00000000..c97b1175 --- /dev/null +++ b/src/main/java/com/apple/itunes/storekit/model/PerformanceTestResultResponse.java @@ -0,0 +1,281 @@ +// Copyright (c) 2026 Apple Inc. Licensed under MIT License. + +package com.apple.itunes.storekit.model; + +import com.fasterxml.jackson.annotation.JsonAnySetter; +import com.fasterxml.jackson.annotation.JsonProperty; + +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; + +/** + * An object the API returns that describes the performance test results. + * + * @see PerformanceTestResultResponse + */ +public class PerformanceTestResultResponse { + private static final String SERIALIZED_NAME_CONFIG = "config"; + private static final String SERIALIZED_NAME_TARGET = "target"; + private static final String SERIALIZED_NAME_RESULT = "result"; + private static final String SERIALIZED_NAME_SUCCESS_RATE = "successRate"; + private static final String SERIALIZED_NAME_NUM_PENDING = "numPending"; + private static final String SERIALIZED_NAME_RESPONSE_TIMES = "responseTimes"; + private static final String SERIALIZED_NAME_FAILURES = "failures"; + @JsonProperty(SERIALIZED_NAME_CONFIG) + private PerformanceTestConfig config; + @JsonProperty(SERIALIZED_NAME_TARGET) + private String target; + @JsonProperty(SERIALIZED_NAME_RESULT) + private String result; + @JsonProperty(SERIALIZED_NAME_SUCCESS_RATE) + private Integer successRate; + @JsonProperty(SERIALIZED_NAME_NUM_PENDING) + private Integer numPending; + @JsonProperty(SERIALIZED_NAME_RESPONSE_TIMES) + private PerformanceTestResponseTimes responseTimes; + @JsonProperty(SERIALIZED_NAME_FAILURES) + private Map failures; + @JsonAnySetter + private Map unknownFields; + + + public PerformanceTestResultResponse() { + } + + public PerformanceTestResultResponse config(PerformanceTestConfig config) { + this.config = config; + return this; + } + + /** + * A PerformanceTestConfig object that enumerates the test parameters. + * + * @return config + * @see PerformanceTestConfig + **/ + public PerformanceTestConfig getConfig() { + return config; + } + + public void setConfig(PerformanceTestConfig config) { + this.config = config; + } + + public PerformanceTestResultResponse target(String target) { + this.target = target; + return this; + } + + /** + * The target URL for the performance test. + * + * @return target + * @see target + **/ + public String getTarget() { + return target; + } + + public void setTarget(String target) { + this.target = target; + } + + public PerformanceTestResultResponse result(PerformanceTestStatus result) { + this.result = result != null ? result.getValue() : null; + return this; + } + + /** + * A PerformanceTestStatus object that describes the overall result of the test. + * + * @return result + * @see performanceTestStatus + **/ + public PerformanceTestStatus getResult() { + return result != null ? PerformanceTestStatus.fromValue(result) : null; + } + + /** + * @see #getResult() + */ + public String getRawResult() { + return result; + } + + public void setResult(PerformanceTestStatus result) { + this.result = result != null ? result.getValue() : null; + } + + public void setRawResult(String rawResult) { + this.result = rawResult; + } + + public PerformanceTestResultResponse successRate(Integer successRate) { + this.successRate = successRate; + return this; + } + + /** + * An integer that describes he success rate percentage of the performance test. + * + * @return successRate + * @see successRate + **/ + public Integer getSuccessRate() { + return successRate; + } + + public void setSuccessRate(Integer successRate) { + this.successRate = successRate; + } + + public PerformanceTestResultResponse numPending(Integer numPending) { + this.numPending = numPending; + return this; + } + + /** + * An integer that describes the number of pending requests in the performance test. + * + * @return numPending + * @see numPending + **/ + public Integer getNumPending() { + return numPending; + } + + public void setNumPending(Integer numPending) { + this.numPending = numPending; + } + + public PerformanceTestResultResponse responseTimes(PerformanceTestResponseTimes responseTimes) { + this.responseTimes = responseTimes; + return this; + } + + /** + * A PerformanceTestResponseTimes object that enumerates the response times measured during the test. + * + * @return responseTimes + * @see PerformanceTestResponseTimes + **/ + public PerformanceTestResponseTimes getResponseTimes() { + return responseTimes; + } + + public void setResponseTimes(PerformanceTestResponseTimes responseTimes) { + this.responseTimes = responseTimes; + } + + public PerformanceTestResultResponse failures(Map failures) { + if (failures != null) { + this.failures = new HashMap<>(); + for (Map.Entry entry : failures.entrySet()) { + this.failures.put(entry.getKey().getValue(), entry.getValue()); + } + } else { + this.failures = null; + } + return this; + } + + /** + * A Failures object that represents a map of server-to-server notification failure reasons and counts that represent the number of failures encountered during the performance test. + * + * @return failures + * @see Failures + **/ + public Map getFailures() { + if (failures == null) { + return null; + } + Map result = new HashMap<>(); + for (Map.Entry entry : failures.entrySet()) { + SendAttemptResult key = SendAttemptResult.fromValue(entry.getKey()); + if (key != null) { + result.put(key, entry.getValue()); + } + } + return result; + } + + /** + * @see #getFailures() + */ + public Map getRawFailures() { + return failures; + } + + public void setFailures(Map failures) { + if (failures != null) { + this.failures = new HashMap<>(); + for (Map.Entry entry : failures.entrySet()) { + this.failures.put(entry.getKey().getValue(), entry.getValue()); + } + } else { + this.failures = null; + } + } + + public void setRawFailures(Map rawFailures) { + this.failures = rawFailures; + } + + + public PerformanceTestResultResponse unknownFields(Map unknownFields) { + this.unknownFields = unknownFields; + return this; + } + + /** + Fields that are not recognized for this object + + @return A map of JSON keys to objects + */ + public Map getUnknownFields() { + return unknownFields; + } + + public void setUnknownFields(Map unknownFields) { + this.unknownFields = unknownFields; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + PerformanceTestResultResponse performanceTestResultResponse = (PerformanceTestResultResponse) o; + return Objects.equals(this.config, performanceTestResultResponse.config) && + Objects.equals(this.target, performanceTestResultResponse.target) && + Objects.equals(this.result, performanceTestResultResponse.result) && + Objects.equals(this.successRate, performanceTestResultResponse.successRate) && + Objects.equals(this.numPending, performanceTestResultResponse.numPending) && + Objects.equals(this.responseTimes, performanceTestResultResponse.responseTimes) && + Objects.equals(this.failures, performanceTestResultResponse.failures) && + Objects.equals(this.unknownFields, performanceTestResultResponse.unknownFields); + } + + @Override + public int hashCode() { + return Objects.hash(config, target, result, successRate, numPending, responseTimes, failures, unknownFields); + } + + @Override + public String toString() { + return "PerformanceTestResultResponse{" + + "config=" + config + + ", target='" + target + '\'' + + ", result='" + result + '\'' + + ", successRate=" + successRate + + ", numPending=" + numPending + + ", responseTimes=" + responseTimes + + ", failures=" + failures + + ", unknownFields=" + unknownFields + + '}'; + } +} diff --git a/src/main/java/com/apple/itunes/storekit/model/PerformanceTestStatus.java b/src/main/java/com/apple/itunes/storekit/model/PerformanceTestStatus.java new file mode 100644 index 00000000..62b9d3ce --- /dev/null +++ b/src/main/java/com/apple/itunes/storekit/model/PerformanceTestStatus.java @@ -0,0 +1,42 @@ +// Copyright (c) 2026 Apple Inc. Licensed under MIT License. + +package com.apple.itunes.storekit.model; + +import com.fasterxml.jackson.annotation.JsonValue; + +/** + * The status of the performance test. + * + * @see performanceTestStatus + */ +public enum PerformanceTestStatus { + + PENDING("PENDING"), + PASS("PASS"), + FAIL("FAIL"); + + private final String value; + + PerformanceTestStatus(String value) { + this.value = value; + } + + public static PerformanceTestStatus fromValue(String value) { + for (PerformanceTestStatus b : PerformanceTestStatus.values()) { + if (b.value.equals(value)) { + return b; + } + } + return null; + } + + @JsonValue + public String getValue() { + return value; + } + + @Override + public String toString() { + return String.valueOf(value); + } +} diff --git a/src/main/java/com/apple/itunes/storekit/model/RealtimeUrlRequest.java b/src/main/java/com/apple/itunes/storekit/model/RealtimeUrlRequest.java new file mode 100644 index 00000000..08ede516 --- /dev/null +++ b/src/main/java/com/apple/itunes/storekit/model/RealtimeUrlRequest.java @@ -0,0 +1,81 @@ +// Copyright (c) 2026 Apple Inc. Licensed under MIT License. + +package com.apple.itunes.storekit.model; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; + +import java.util.Objects; + +/** + * The request body for configuring the URL of your Get Retention Message endpoint. + * + * @see RealtimeUrlRequest + */ +@JsonInclude(JsonInclude.Include.NON_NULL) +public class RealtimeUrlRequest { + private static final String SERIALIZED_NAME_REALTIME_URL = "realtimeURL"; + @JsonProperty(value = SERIALIZED_NAME_REALTIME_URL, required = true) + private String realtimeURL; + + private static final int MAXIMUM_REALTIME_URL_LENGTH = 256; + + private RealtimeUrlRequest() { + } + + public RealtimeUrlRequest(String realtimeURL) { + this.realtimeURL = validateRealtimeUrl(realtimeURL); + } + + public RealtimeUrlRequest realtimeURL(String realtimeURL) { + this.realtimeURL = validateRealtimeUrl(realtimeURL); + return this; + } + + /** + * A string that contains the URL of your Get Retention Message endpoint for configuration. + * + * @return realtimeURL + * @see realtimeURL + **/ + public String getRealtimeURL() { + return realtimeURL; + } + + public void setRealtimeURL(String realtimeURL) { + this.realtimeURL = validateRealtimeUrl(realtimeURL); + } + + private String validateRealtimeUrl(String realtimeURL) { + Objects.requireNonNull(realtimeURL); + if (realtimeURL.length() > MAXIMUM_REALTIME_URL_LENGTH) { + throw new IllegalArgumentException("realtimeURL length longer than maximum allowed"); + } + return realtimeURL; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + RealtimeUrlRequest realtimeUrlRequest = (RealtimeUrlRequest) o; + return Objects.equals(this.realtimeURL, realtimeUrlRequest.realtimeURL); + } + + @Override + public int hashCode() { + return Objects.hash(realtimeURL); + } + + @Override + public String toString() { + return "RealtimeUrlRequest{" + + "realtimeURL=" + realtimeURL + + '}'; + } +} + diff --git a/src/main/java/com/apple/itunes/storekit/model/RealtimeUrlResponse.java b/src/main/java/com/apple/itunes/storekit/model/RealtimeUrlResponse.java new file mode 100644 index 00000000..9f84e477 --- /dev/null +++ b/src/main/java/com/apple/itunes/storekit/model/RealtimeUrlResponse.java @@ -0,0 +1,81 @@ +// Copyright (c) 2026 Apple Inc. Licensed under MIT License. + +package com.apple.itunes.storekit.model; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; + +import java.util.Objects; + +/** + * The response body that contains the URL for your Get Retention Message endpoint. + * + * @see RealtimeUrlResponse + */ +@JsonInclude(JsonInclude.Include.NON_NULL) +public class RealtimeUrlResponse { + private static final String SERIALIZED_NAME_REALTIME_URL = "realtimeURL"; + @JsonProperty(SERIALIZED_NAME_REALTIME_URL) + private String realtimeURL; + + private static final int MAXIMUM_REALTIME_URL_LENGTH = 256; + + private RealtimeUrlResponse() { + } + + public RealtimeUrlResponse(String realtimeURL) { + this.realtimeURL = validateRealtimeUrl(realtimeURL); + } + + public RealtimeUrlResponse realtimeURL(String realtimeURL) { + this.realtimeURL = validateRealtimeUrl(realtimeURL); + return this; + } + + /** + * A string that contains the URL you provided for your Get Retention Message endpoint. + * + * @return realtimeURL + * @see realtimeURL + **/ + public String getRealtimeURL() { + return realtimeURL; + } + + public void setRealtimeURL(String realtimeURL) { + this.realtimeURL = validateRealtimeUrl(realtimeURL); + } + + private String validateRealtimeUrl(String realtimeURL) { + Objects.requireNonNull(realtimeURL); + if (realtimeURL.length() > MAXIMUM_REALTIME_URL_LENGTH) { + throw new IllegalArgumentException("realtimeURL length longer than maximum allowed"); + } + return realtimeURL; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + RealtimeUrlResponse realtimeUrlRequest = (RealtimeUrlResponse) o; + return Objects.equals(this.realtimeURL, realtimeUrlRequest.realtimeURL); + } + + @Override + public int hashCode() { + return Objects.hash(realtimeURL); + } + + @Override + public String toString() { + return "RealtimeUrlResponse{" + + "realtimeURL=" + realtimeURL + + '}'; + } +} + diff --git a/src/main/java/com/apple/itunes/storekit/model/UploadMessageRequestBody.java b/src/main/java/com/apple/itunes/storekit/model/UploadMessageRequestBody.java index 482786e9..766053c0 100644 --- a/src/main/java/com/apple/itunes/storekit/model/UploadMessageRequestBody.java +++ b/src/main/java/com/apple/itunes/storekit/model/UploadMessageRequestBody.java @@ -5,6 +5,7 @@ import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonProperty; +import java.util.List; import java.util.Objects; /** @@ -17,19 +18,30 @@ public class UploadMessageRequestBody { private static final String SERIALIZED_NAME_HEADER = "header"; private static final String SERIALIZED_NAME_BODY = "body"; private static final String SERIALIZED_NAME_IMAGE = "image"; + private static final String SERIALIZED_NAME_HEADER_POSITION = "headerPosition"; + private static final String SERIALIZED_NAME_BULLET_POINTS = "bulletPoints"; @JsonProperty(value = SERIALIZED_NAME_HEADER, required = true) private String header; @JsonProperty(value = SERIALIZED_NAME_BODY, required = true) private String body; @JsonProperty(value = SERIALIZED_NAME_IMAGE) private UploadMessageImage image; + @JsonProperty(value = SERIALIZED_NAME_HEADER_POSITION) + private String headerPosition; + @JsonProperty(value = SERIALIZED_NAME_BULLET_POINTS) + private List bulletPoints; private static final int MAXIMUM_HEADER_LENGTH = 66; private static final int MAXIMUM_BODY_LENGTH = 144; + private static final int MAXIMUM_BULLET_POINTS_COUNT = 5; private UploadMessageRequestBody() { } + /** + * @deprecated Use {@link #UploadMessageRequestBody(String, String)} instead. + */ + @Deprecated public UploadMessageRequestBody(String header, String body, UploadMessageImage image) { @@ -38,6 +50,11 @@ public UploadMessageRequestBody(String header, this.image = image; } + public UploadMessageRequestBody(String header, + String body) { + this(header, body, null); + } + public UploadMessageRequestBody header(String header) { this.header = validateHeader(header); return this; @@ -111,6 +128,62 @@ public void setImage(UploadMessageImage image) { this.image = image; } + public UploadMessageRequestBody headerPosition(HeaderPosition headerPosition) { + this.headerPosition = headerPosition != null ? headerPosition.getValue() : null; + return this; + } + + /** + * The position of header text, which defaults to placing header text above the body. + * + * @return headerPosition + * @see headerPosition + **/ + public HeaderPosition getHeaderPosition() { + return headerPosition != null ? HeaderPosition.fromValue(headerPosition) : null; + } + + /** + * @see #getHeaderPosition() + */ + public String getRawHeaderPosition() { + return headerPosition; + } + + public void setHeaderPosition(HeaderPosition headerPosition) { + this.headerPosition = headerPosition != null ? headerPosition.getValue() : null; + } + + public void setRawHeaderPosition(String rawHeaderPosition) { + this.headerPosition = rawHeaderPosition; + } + + public UploadMessageRequestBody bulletPoints(List bulletPoints) { + this.bulletPoints = validateBulletPoints(bulletPoints); + return this; + } + + /** + * An optional array of bullet points. + * + * @return bulletPoints + * @see BulletPoint + **/ + public List getBulletPoints() { + return bulletPoints; + } + + public void setBulletPoints(List bulletPoints) { + this.bulletPoints = validateBulletPoints(bulletPoints); + } + + private List validateBulletPoints(List bulletPoints) { + if (bulletPoints != null && bulletPoints.size() > MAXIMUM_BULLET_POINTS_COUNT) { + throw new IllegalArgumentException("bulletPoints count exceeds the maximum allowed"); + } + return bulletPoints; + } + @Override public boolean equals(Object o) { if (this == o) { @@ -122,12 +195,14 @@ public boolean equals(Object o) { UploadMessageRequestBody uploadMessageRequestBody = (UploadMessageRequestBody) o; return Objects.equals(this.header, uploadMessageRequestBody.header) && Objects.equals(this.body, uploadMessageRequestBody.body) && - Objects.equals(this.image, uploadMessageRequestBody.image); + Objects.equals(this.image, uploadMessageRequestBody.image) && + Objects.equals(this.headerPosition, uploadMessageRequestBody.headerPosition) && + Objects.equals(this.bulletPoints, uploadMessageRequestBody.bulletPoints); } @Override public int hashCode() { - return Objects.hash(header, body, image); + return Objects.hash(header, body, image, headerPosition, bulletPoints); } @Override @@ -136,6 +211,8 @@ public String toString() { "header='" + header + '\'' + ", body='" + body + '\'' + ", image=" + image + + ", headerPosition='" + headerPosition + '\'' + + ", bulletPoints=" + bulletPoints + '}'; } } diff --git a/src/test/java/com/apple/itunes/storekit/client/AppStoreServerAPIClientTest.java b/src/test/java/com/apple/itunes/storekit/client/AppStoreServerAPIClientTest.java index 4f37185a..ca4f5384 100644 --- a/src/test/java/com/apple/itunes/storekit/client/AppStoreServerAPIClientTest.java +++ b/src/test/java/com/apple/itunes/storekit/client/AppStoreServerAPIClientTest.java @@ -4,11 +4,13 @@ import com.apple.itunes.storekit.model.AccountTenure; import com.apple.itunes.storekit.model.AppTransactionInfoResponse; +import com.apple.itunes.storekit.model.BulletPoint; import com.apple.itunes.storekit.model.CheckTestNotificationResponse; import com.apple.itunes.storekit.model.ConsumptionRequest; import com.apple.itunes.storekit.model.ConsumptionRequestV1; import com.apple.itunes.storekit.model.ConsumptionStatus; import com.apple.itunes.storekit.model.DefaultConfigurationRequest; +import com.apple.itunes.storekit.model.DefaultConfigurationResponse; import com.apple.itunes.storekit.model.DeliveryStatus; import com.apple.itunes.storekit.model.DeliveryStatusV1; import com.apple.itunes.storekit.model.Environment; @@ -17,7 +19,9 @@ import com.apple.itunes.storekit.model.ExtendRenewalDateResponse; import com.apple.itunes.storekit.model.GetImageListResponse; import com.apple.itunes.storekit.model.GetMessageListResponse; +import com.apple.itunes.storekit.model.HeaderPosition; import com.apple.itunes.storekit.model.HistoryResponse; +import com.apple.itunes.storekit.model.ImageSize; import com.apple.itunes.storekit.model.ImageState; import com.apple.itunes.storekit.model.InAppOwnershipType; import com.apple.itunes.storekit.model.LastTransactionsItem; @@ -33,8 +37,16 @@ import com.apple.itunes.storekit.model.NotificationTypeV2; import com.apple.itunes.storekit.model.OrderLookupResponse; import com.apple.itunes.storekit.model.OrderLookupStatus; +import com.apple.itunes.storekit.model.PerformanceTestConfig; +import com.apple.itunes.storekit.model.PerformanceTestRequest; +import com.apple.itunes.storekit.model.PerformanceTestResponse; +import com.apple.itunes.storekit.model.PerformanceTestResponseTimes; +import com.apple.itunes.storekit.model.PerformanceTestResultResponse; +import com.apple.itunes.storekit.model.PerformanceTestStatus; import com.apple.itunes.storekit.model.Platform; import com.apple.itunes.storekit.model.PlayTime; +import com.apple.itunes.storekit.model.RealtimeUrlRequest; +import com.apple.itunes.storekit.model.RealtimeUrlResponse; import com.apple.itunes.storekit.model.RefundHistoryResponse; import com.apple.itunes.storekit.model.RefundPreference; import com.apple.itunes.storekit.model.RefundPreferenceV1; @@ -593,6 +605,7 @@ public void testGetImageList() throws IOException, APIException { Assertions.assertEquals(1, response.getImageIdentifiers().size()); Assertions.assertEquals(UUID.fromString("a1b2c3d4-e5f6-7890-a1b2-c3d4e5f67890"), response.getImageIdentifiers().get(0).getImageIdentifier()); Assertions.assertEquals(ImageState.APPROVED, response.getImageIdentifiers().get(0).getImageState()); + Assertions.assertEquals(ImageSize.FULL_SIZE, response.getImageIdentifiers().get(0).getImageSize()); } @Test @@ -619,7 +632,7 @@ public void testUploadMessage() throws IOException, APIException { Assertions.assertEquals("Body text", root.get("body")); }); - UploadMessageRequestBody uploadMessageRequestBody = new UploadMessageRequestBody("Header text", "Body text", null); + UploadMessageRequestBody uploadMessageRequestBody = new UploadMessageRequestBody("Header text", "Body text"); client.uploadMessage(UUID.fromString("a1b2c3d4-e5f6-7890-a1b2-c3d4e5f67890"), uploadMessageRequestBody); } @@ -651,7 +664,8 @@ public void testUploadMessageWithImage() throws IOException, APIException { }); UploadMessageImage image = new UploadMessageImage(UUID.fromString("b2c3d4e5-f6a7-8901-b2c3-d4e5f6a78901"), "Alt text"); - UploadMessageRequestBody uploadMessageRequestBody = new UploadMessageRequestBody("Header text", "Body text", image); + UploadMessageRequestBody uploadMessageRequestBody = new UploadMessageRequestBody("Header text", "Body text") + .image(image); client.uploadMessage(UUID.fromString("a1b2c3d4-e5f6-7890-a1b2-c3d4e5f67890"), uploadMessageRequestBody); } @@ -716,6 +730,135 @@ public void testDeleteDefaultMessage() throws IOException, APIException { client.deleteDefaultMessage("com.example.product", "en-US"); } + @Test + public void testGetDefaultMessage() throws IOException, APIException { + AppStoreServerAPIClient client = getClientWithBody("models/getDefaultMessageResponse.json", request -> { + Assertions.assertEquals("GET", request.method()); + Assertions.assertEquals("/inApps/v1/messaging/default/com.example.product/en-US", request.url().encodedPath()); + }); + + DefaultConfigurationResponse response = client.getDefaultMessage("com.example.product", "en-US"); + Assertions.assertEquals(UUID.fromString("a1b2c3d4-e5f6-7890-a1b2-c3d4e5f67890"), response.getMessageIdentifier()); + } + + @Test + public void testUploadImageWithImageSize() throws IOException, APIException { + AppStoreServerAPIClient client = getAppStoreServerAPIClient("", request -> { + Assertions.assertEquals("PUT", request.method()); + Assertions.assertEquals("/inApps/v1/messaging/image/a1b2c3d4-e5f6-7890-a1b2-c3d4e5f67890", request.url().encodedPath()); + Assertions.assertEquals("FULL_SIZE", request.url().queryParameter("imageSize")); + RequestBody body = request.body(); + Assertions.assertNotNull(body); + Assertions.assertEquals(MediaType.parse("image/png"), body.contentType()); + Buffer buffer = new Buffer(); + try { + body.writeTo(buffer); + } catch (IOException e) { + throw new RuntimeException(e); + } + byte[] bytes = buffer.readByteArray(); + Assertions.assertArrayEquals(new byte[]{1, 2, 3}, bytes); + }); + + client.uploadImage(UUID.fromString("a1b2c3d4-e5f6-7890-a1b2-c3d4e5f67890"), new byte[]{1, 2, 3}, ImageSize.FULL_SIZE); + } + + @Test + public void testConfigureRealtimeURL() throws IOException, APIException { + AppStoreServerAPIClient client = getAppStoreServerAPIClient("", request -> { + Assertions.assertEquals("PUT", request.method()); + Assertions.assertEquals("/inApps/v1/messaging/realtime/url", request.url().encodedPath()); + RequestBody body = request.body(); + Assertions.assertNotNull(body); + Assertions.assertEquals(expectedMediaType, body.contentType()); + Buffer buffer = new Buffer(); + try { + body.writeTo(buffer); + } catch (IOException e) { + throw new RuntimeException(e); + } + Map root; + try { + root = new ObjectMapper().readValue(buffer.readUtf8(), Map.class); + } catch (JsonProcessingException e) { + throw new RuntimeException(e); + } + Assertions.assertEquals("https://example.com/realtime", root.get("realtimeURL")); + }); + + RealtimeUrlRequest realtimeUrlRequest = new RealtimeUrlRequest("https://example.com/realtime"); + client.configureRealtimeURL(realtimeUrlRequest); + } + + @Test + public void testDeleteRealtimeURL() throws IOException, APIException { + AppStoreServerAPIClient client = getAppStoreServerAPIClient("", request -> { + Assertions.assertEquals("DELETE", request.method()); + Assertions.assertEquals("/inApps/v1/messaging/realtime/url", request.url().encodedPath()); + }); + + client.deleteRealtimeURL(); + } + + @Test + public void testGetRealtimeURL() throws IOException, APIException { + AppStoreServerAPIClient client = getClientWithBody("models/getRealtimeUrlResponse.json", request -> { + Assertions.assertEquals("GET", request.method()); + Assertions.assertEquals("/inApps/v1/messaging/realtime/url", request.url().encodedPath()); + }); + + RealtimeUrlResponse response = client.getRealtimeURL(); + Assertions.assertEquals("https://example.com/realtime", response.getRealtimeURL()); + } + + @SuppressWarnings("unchecked") + @Test + public void testUploadMessageWithBulletPoints() throws IOException, APIException { + AppStoreServerAPIClient client = getAppStoreServerAPIClient("", request -> { + Assertions.assertEquals("PUT", request.method()); + Assertions.assertEquals("/inApps/v1/messaging/message/a1b2c3d4-e5f6-7890-a1b2-c3d4e5f67890", request.url().encodedPath()); + RequestBody body = request.body(); + Assertions.assertNotNull(body); + Assertions.assertEquals(expectedMediaType, body.contentType()); + Buffer buffer = new Buffer(); + try { + body.writeTo(buffer); + } catch (IOException e) { + throw new RuntimeException(e); + } + Map root; + try { + root = new ObjectMapper().readValue(buffer.readUtf8(), Map.class); + } catch (JsonProcessingException e) { + throw new RuntimeException(e); + } + Assertions.assertEquals("Header text", root.get("header")); + Assertions.assertEquals("Body text", root.get("body")); + Assertions.assertEquals("ABOVE_IMAGE", root.get("headerPosition")); + Map image = (Map) root.get("image"); + Assertions.assertEquals("b2c3d4e5-f6a7-8901-b2c3-d4e5f6a78901", image.get("imageIdentifier")); + Assertions.assertEquals("Image alt text", image.get("altText")); + List> bulletPoints = (List>) root.get("bulletPoints"); + Assertions.assertEquals(2, bulletPoints.size()); + Assertions.assertEquals("Point 1", bulletPoints.get(0).get("text")); + Assertions.assertEquals("c3d4e5f6-a7b8-9012-c3d4-e5f6a7b89012", bulletPoints.get(0).get("imageIdentifier")); + Assertions.assertEquals("Alt 1", bulletPoints.get(0).get("altText")); + Assertions.assertEquals("Point 2", bulletPoints.get(1).get("text")); + Assertions.assertEquals("d4e5f6a7-b8c9-0123-d4e5-f6a7b8c90123", bulletPoints.get(1).get("imageIdentifier")); + Assertions.assertEquals("Alt 2", bulletPoints.get(1).get("altText")); + }); + + UploadMessageImage image = new UploadMessageImage(UUID.fromString("b2c3d4e5-f6a7-8901-b2c3-d4e5f6a78901"), "Image alt text"); + UploadMessageRequestBody uploadMessageRequestBody = new UploadMessageRequestBody("Header text", "Body text") + .image(image) + .headerPosition(HeaderPosition.ABOVE_IMAGE) + .bulletPoints(List.of( + new BulletPoint("Point 1", UUID.fromString("c3d4e5f6-a7b8-9012-c3d4-e5f6a7b89012"), "Alt 1"), + new BulletPoint("Point 2", UUID.fromString("d4e5f6a7-b8c9-0123-d4e5-f6a7b8c90123"), "Alt 2") + )); + client.uploadMessage(UUID.fromString("a1b2c3d4-e5f6-7890-a1b2-c3d4e5f67890"), uploadMessageRequestBody); + } + @Test public void testHeaders() throws APIException, IOException { AppStoreServerAPIClient client = getClientWithBody("models/transactionInfoResponse.json", request -> { @@ -1046,6 +1189,78 @@ public void testSendConsumptionInformationWithoutOptionalFields() throws APIExce client.sendConsumptionInformation("49571273", consumptionRequest); } + @Test + public void testInitiatePerformanceTest() throws IOException, APIException { + AppStoreServerAPIClient client = getClientWithBody("models/performanceTestResponse.json", request -> { + Assertions.assertEquals("POST", request.method()); + Assertions.assertEquals("/inApps/v1/messaging/performanceTest", request.url().encodedPath()); + RequestBody body = request.body(); + Assertions.assertNotNull(body); + Assertions.assertEquals(expectedMediaType, body.contentType()); + Buffer buffer = new Buffer(); + try { + body.writeTo(buffer); + } catch (IOException e) { + throw new RuntimeException(e); + } + Map root; + try { + root = new ObjectMapper().readValue(buffer.readUtf8(), Map.class); + } catch (JsonProcessingException e) { + throw new RuntimeException(e); + } + Assertions.assertEquals("70000500092808", root.get("originalTransactionId")); + }); + + PerformanceTestRequest performanceTestRequest = new PerformanceTestRequest("70000500092808"); + + PerformanceTestResponse response = client.initiatePerformanceTest(performanceTestRequest); + + Assertions.assertNotNull(response); + Assertions.assertEquals("c4b87a1d-2e3f-4a5b-9c6d-7e8f9a0b1c2d", response.getRequestId()); + PerformanceTestConfig config = response.getConfig(); + Assertions.assertNotNull(config); + Assertions.assertEquals(10L, config.getMaxConcurrentRequests()); + Assertions.assertEquals(100L, config.getTotalRequests()); + Assertions.assertEquals(60000L, config.getTotalDuration()); + Assertions.assertEquals(500L, config.getResponseTimeThreshold()); + Assertions.assertEquals(95, config.getSuccessRateThreshold()); + } + + @Test + public void testGetPerformanceTestResults() throws IOException, APIException { + AppStoreServerAPIClient client = getClientWithBody("models/performanceTestResultResponse.json", request -> { + Assertions.assertEquals("GET", request.method()); + Assertions.assertEquals("/inApps/v1/messaging/performanceTest/result/c4b87a1d-2e3f-4a5b-9c6d-7e8f9a0b1c2d", request.url().encodedPath()); + Assertions.assertNull(request.body()); + }); + + PerformanceTestResultResponse response = client.getPerformanceTestResults("c4b87a1d-2e3f-4a5b-9c6d-7e8f9a0b1c2d"); + + Assertions.assertNotNull(response); + PerformanceTestConfig config = response.getConfig(); + Assertions.assertNotNull(config); + Assertions.assertEquals(10L, config.getMaxConcurrentRequests()); + Assertions.assertEquals(100L, config.getTotalRequests()); + Assertions.assertEquals(60000L, config.getTotalDuration()); + Assertions.assertEquals(500L, config.getResponseTimeThreshold()); + Assertions.assertEquals(95, config.getSuccessRateThreshold()); + Assertions.assertEquals("https://example.com/retention", response.getTarget()); + Assertions.assertEquals(PerformanceTestStatus.PASS, response.getResult()); + Assertions.assertEquals("PASS", response.getRawResult()); + Assertions.assertEquals(98, response.getSuccessRate()); + Assertions.assertEquals(0, response.getNumPending()); + PerformanceTestResponseTimes responseTimes = response.getResponseTimes(); + Assertions.assertNotNull(responseTimes); + Assertions.assertEquals(120L, responseTimes.getAverage()); + Assertions.assertEquals(100L, responseTimes.getP50()); + Assertions.assertEquals(200L, responseTimes.getP90()); + Assertions.assertEquals(250L, responseTimes.getP95()); + Assertions.assertEquals(400L, responseTimes.getP99()); + Assertions.assertEquals(Map.of(SendAttemptResult.TIMED_OUT, 1, SendAttemptResult.NO_RESPONSE, 1), response.getFailures()); + Assertions.assertEquals(Map.of("TIMED_OUT", 1, "NO_RESPONSE", 1), response.getRawFailures()); + } + public AppStoreServerAPIClient getClientWithBody(String path, Consumer requestVerifier) throws IOException { String body = TestingUtility.readFile(path); return getAppStoreServerAPIClient(body, requestVerifier); diff --git a/src/test/resources/models/getDefaultMessageResponse.json b/src/test/resources/models/getDefaultMessageResponse.json new file mode 100644 index 00000000..e3d2954b --- /dev/null +++ b/src/test/resources/models/getDefaultMessageResponse.json @@ -0,0 +1,3 @@ +{ + "messageIdentifier": "a1b2c3d4-e5f6-7890-a1b2-c3d4e5f67890" +} diff --git a/src/test/resources/models/getImageListResponse.json b/src/test/resources/models/getImageListResponse.json index 652d2e96..b668dd12 100644 --- a/src/test/resources/models/getImageListResponse.json +++ b/src/test/resources/models/getImageListResponse.json @@ -2,7 +2,8 @@ "imageIdentifiers": [ { "imageIdentifier": "a1b2c3d4-e5f6-7890-a1b2-c3d4e5f67890", - "imageState": "APPROVED" + "imageState": "APPROVED", + "imageSize": "FULL_SIZE" } ] } diff --git a/src/test/resources/models/getRealtimeUrlResponse.json b/src/test/resources/models/getRealtimeUrlResponse.json new file mode 100644 index 00000000..699d7fb5 --- /dev/null +++ b/src/test/resources/models/getRealtimeUrlResponse.json @@ -0,0 +1,3 @@ +{ + "realtimeURL": "https://example.com/realtime" +} diff --git a/src/test/resources/models/performanceTestResponse.json b/src/test/resources/models/performanceTestResponse.json new file mode 100644 index 00000000..09973994 --- /dev/null +++ b/src/test/resources/models/performanceTestResponse.json @@ -0,0 +1,10 @@ +{ + "config": { + "maxConcurrentRequests": 10, + "totalRequests": 100, + "totalDuration": 60000, + "responseTimeThreshold": 500, + "successRateThreshold": 95 + }, + "requestId": "c4b87a1d-2e3f-4a5b-9c6d-7e8f9a0b1c2d" +} diff --git a/src/test/resources/models/performanceTestResultResponse.json b/src/test/resources/models/performanceTestResultResponse.json new file mode 100644 index 00000000..a2ac006a --- /dev/null +++ b/src/test/resources/models/performanceTestResultResponse.json @@ -0,0 +1,24 @@ +{ + "config": { + "maxConcurrentRequests": 10, + "totalRequests": 100, + "totalDuration": 60000, + "responseTimeThreshold": 500, + "successRateThreshold": 95 + }, + "target": "https://example.com/retention", + "result": "PASS", + "successRate": 98, + "numPending": 0, + "responseTimes": { + "average": 120, + "p50": 100, + "p90": 200, + "p95": 250, + "p99": 400 + }, + "failures": { + "TIMED_OUT": 1, + "NO_RESPONSE": 1 + } +}