Skip to content

Commit 4eb3c84

Browse files
committed
feat: entry variants branch support
1 parent 226b2ef commit 4eb3c84

4 files changed

Lines changed: 195 additions & 3 deletions

File tree

src/main/java/com/contentstack/cms/core/ErrorMessages.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ private ErrorMessages() {
3737
public static final String RELEASE_UID_REQUIRED = "Release UID is required. Provide a valid Release UID and try again.";
3838
public static final String ROLE_UID_REQUIRED = "Role UID is required. Provide a valid Role UID and try again.";
3939
public static final String VARIANT_GROUP_UID_REQUIRED = "Variant Group UID is required. Provide a valid Variant Group UID and try again.";
40+
public static final String VARIANT_UID_REQUIRED = "Variant UID is required. Provide a valid Variant UID and try again.";
4041
public static final String WEBHOOK_UID_REQUIRED = "Webhook UID is required. Provide a valid Webhook UID and try again.";
4142
public static final String WORKFLOW_UID_REQUIRED = "Workflow UID is required. Provide a valid Workflow UID and try again.";
4243

src/main/java/com/contentstack/cms/core/Util.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,20 @@ public class Util {
3939
public static final String AUTHTOKEN = "authtoken";
4040
public static final String EARLY_ACCESS_HEADER = "x-header-ea";
4141
public static final String BRANCH = "branch";
42+
43+
/**
44+
* Request header to fetch a base entry with a specific entry variant applied (personalization).
45+
*/
46+
public static final String X_CS_VARIANT_UID = "x-cs-variant-uid";
47+
48+
/**
49+
* Required on publish/unpublish when the request body includes {@code entry.variants} (entry variant flows).
50+
*/
51+
public static final String API_VERSION = "api_version";
52+
53+
/** Value for {@link #API_VERSION} when publishing or unpublishing entry variants per Content Management API. */
54+
public static final String API_VERSION_ENTRY_VARIANTS_PUBLISH = "3.2";
55+
4256
public static final String X_USER_AGENT = "X-User-Agent";
4357
public static final String USER_AGENT = "User-Agent";
4458
public static final String CONTENT_TYPE = "Content-Type";

src/main/java/com/contentstack/cms/stack/Entry.java

Lines changed: 129 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package com.contentstack.cms.stack;
22

33
import com.contentstack.cms.core.ErrorMessages;
4+
import com.contentstack.cms.core.Util;
45

56
import com.contentstack.cms.BaseImplementation;
67
import okhttp3.ResponseBody;
@@ -68,6 +69,37 @@ private void validateCT() {
6869
Objects.requireNonNull(this.contentTypeUid, ERROR_CT_UID);
6970
}
7071

72+
private void validateVariantUid(@NotNull String variantUid) {
73+
Objects.requireNonNull(variantUid, ErrorMessages.VARIANT_UID_REQUIRED);
74+
if (variantUid.isEmpty()) {
75+
throw new IllegalArgumentException(ErrorMessages.VARIANT_UID_REQUIRED);
76+
}
77+
}
78+
79+
/**
80+
* Sets the branch header for requests scoped to a stack branch (e.g. development).
81+
*
82+
* @param branchUid branch UID or alias target branch UID
83+
* @return this entry instance for chaining
84+
*/
85+
public Entry addBranch(@NotNull String branchUid) {
86+
this.headers.put(Util.BRANCH, branchUid);
87+
return this;
88+
}
89+
90+
/**
91+
* Sets {@value Util#X_CS_VARIANT_UID} for {@link #fetch()} / {@link #fetchAsPojo()} to retrieve the base entry with a
92+
* specific variant applied (personalization).
93+
*
94+
* @param variantUid Content variant UID (e.g. {@code cs…})
95+
* @return this entry instance for chaining
96+
*/
97+
public Entry withAppliedVariantUid(@NotNull String variantUid) {
98+
validateVariantUid(variantUid);
99+
this.headers.put(Util.X_CS_VARIANT_UID, variantUid);
100+
return this;
101+
}
102+
71103
/**
72104
* Sets header for the request
73105
*
@@ -710,6 +742,75 @@ public Call<ResponseBody> importExisting() {
710742
return this.service.importExisting(this.headers, this.contentTypeUid, this.entryUid, this.params);
711743
}
712744

745+
/**
746+
* Retrieves all entry variants for this entry.
747+
* <p>
748+
* Use {@link #addParam(String, Object)} for optional queries such as {@code locale}, {@code include_workflow},
749+
* {@link #addBranch(String)} or stack-level branch header for branch-scoped stacks.
750+
*
751+
* @return Retrofit call for GET …/entries/{entry_uid}/variants
752+
* @see <a href="https://www.contentstack.com/docs/developers/apis/content-management-api/#get-all-entry-variants">Get all entry variants</a>
753+
*/
754+
public Call<ResponseBody> fetchEntryVariants() {
755+
validateCT();
756+
validateEntry();
757+
return this.service.fetchEntryVariants(this.headers, this.contentTypeUid, this.entryUid, this.params);
758+
}
759+
760+
/**
761+
* Retrieves a single entry variant.
762+
*
763+
* @param variantUid variant UID path segment
764+
* @return Retrofit call for GET …/variants/{variant_uid}
765+
*/
766+
public Call<ResponseBody> fetchEntryVariant(@NotNull String variantUid) {
767+
validateCT();
768+
validateEntry();
769+
validateVariantUid(variantUid);
770+
return this.service.fetchEntryVariant(this.headers, this.contentTypeUid, this.entryUid, variantUid, this.params);
771+
}
772+
773+
/**
774+
* Creates an entry variant. Uses PUT …/variants/{variant_uid} (CMA upsert — same URL as {@link #updateEntryVariant}).
775+
*
776+
* @param variantUid variant UID path segment
777+
* @param requestBody JSON body per API (typically wraps fields under {@code entry})
778+
* @see <a href="https://www.contentstack.com/docs/developers/apis/content-management-api/#create-entry-variant">Create Entry Variant</a>
779+
*/
780+
public Call<ResponseBody> createEntryVariant(@NotNull String variantUid, @NotNull JSONObject requestBody) {
781+
validateCT();
782+
validateEntry();
783+
validateVariantUid(variantUid);
784+
return this.service.createEntryVariant(this.headers, this.contentTypeUid, this.entryUid, variantUid, this.params,
785+
requestBody);
786+
}
787+
788+
/**
789+
* Updates an entry variant. Same HTTP request shape as create (PUT upsert).
790+
*
791+
* @see <a href="https://www.contentstack.com/docs/developers/apis/content-management-api/#update-entry-variant">Update Entry Variant</a>
792+
*/
793+
public Call<ResponseBody> updateEntryVariant(@NotNull String variantUid, @NotNull JSONObject requestBody) {
794+
validateCT();
795+
validateEntry();
796+
validateVariantUid(variantUid);
797+
return this.service.updateEntryVariant(this.headers, this.contentTypeUid, this.entryUid, variantUid, this.params,
798+
requestBody);
799+
}
800+
801+
/**
802+
* Deletes an entry variant.
803+
*
804+
* @param variantUid variant UID path segment
805+
* @return Retrofit call for DELETE …/variants/{variant_uid}
806+
*/
807+
public Call<ResponseBody> deleteEntryVariant(@NotNull String variantUid) {
808+
validateCT();
809+
validateEntry();
810+
validateVariantUid(variantUid);
811+
return this.service.deleteEntryVariant(this.headers, this.contentTypeUid, this.entryUid, variantUid, this.params);
812+
}
813+
713814
/**
714815
* To Publish an entry request lets you publish an entry either immediately or
715816
* schedule it for a later date/time.
@@ -752,7 +853,22 @@ public Call<ResponseBody> importExisting() {
752853
public Call<ResponseBody> publish(@NotNull JSONObject requestBody) {
753854
validateCT();
754855
validateEntry();
755-
return this.service.publish(this.headers, this.contentTypeUid, this.entryUid, requestBody);
856+
return this.service.publish(this.headers, this.contentTypeUid, this.entryUid, this.params, requestBody);
857+
}
858+
859+
/**
860+
* Publishes entry variants using the entry publish endpoint with {@code entry.variants} in the body.
861+
* Sends header {@value Util#API_VERSION}={@value Util#API_VERSION_ENTRY_VARIANTS_PUBLISH} unless already set on this entry instance.
862+
* Use {@link #addParam(String, Object)} for optional {@code locale} query parameter; use {@link #addBranch(String)} for branch scope.
863+
*
864+
* @param requestBody full publish payload including {@code entry}, {@code locale}, etc.
865+
*/
866+
public Call<ResponseBody> publishEntryVariants(@NotNull JSONObject requestBody) {
867+
validateCT();
868+
validateEntry();
869+
HashMap<String, Object> publishHeaders = new HashMap<>(this.headers);
870+
publishHeaders.putIfAbsent(Util.API_VERSION, Util.API_VERSION_ENTRY_VARIANTS_PUBLISH);
871+
return this.service.publish(publishHeaders, this.contentTypeUid, this.entryUid, this.params, requestBody);
756872
}
757873

758874
/**
@@ -816,9 +932,20 @@ public Call<ResponseBody> publishWithReference(@NotNull JSONObject requestBody)
816932
public Call<ResponseBody> unpublish(@NotNull JSONObject requestBody) {
817933
validateCT();
818934
validateEntry();
819-
return this.service.unpublish(this.headers, this.contentTypeUid, this.entryUid, requestBody);
935+
return this.service.unpublish(this.headers, this.contentTypeUid, this.entryUid, this.params, requestBody);
820936
}
821937

938+
/**
939+
* Unpublishes entry variants via the entry unpublish endpoint with {@code entry.variants} in the body.
940+
* Sends header {@value Util#API_VERSION}={@value Util#API_VERSION_ENTRY_VARIANTS_PUBLISH} unless already set.
941+
*/
942+
public Call<ResponseBody> unpublishEntryVariants(@NotNull JSONObject requestBody) {
943+
validateCT();
944+
validateEntry();
945+
HashMap<String, Object> unpublishHeaders = new HashMap<>(this.headers);
946+
unpublishHeaders.putIfAbsent(Util.API_VERSION, Util.API_VERSION_ENTRY_VARIANTS_PUBLISH);
947+
return this.service.unpublish(unpublishHeaders, this.contentTypeUid, this.entryUid, this.params, requestBody);
948+
}
822949

823950
/**
824951
* Get instance of taxonomy search filter class instance through which we can query on taxonomy based on content type

src/main/java/com/contentstack/cms/stack/EntryService.java

Lines changed: 51 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
import retrofit2.Call;
66
import retrofit2.http.*;
77

8-
import java.util.List;
98
import java.util.Map;
109

1110
public interface EntryService {
@@ -147,6 +146,7 @@ Call<ResponseBody> publish(
147146
@HeaderMap Map<String, Object> headers,
148147
@Path("content_type_uid") String contentTypeUid,
149148
@Path("entry_uid") String entryUid,
149+
@QueryMap(encoded = true) Map<String, Object> queryParameters,
150150
@Body JSONObject requestBody);
151151

152152
@POST("bulk/publish?x-bulk-action=publish")
@@ -160,8 +160,58 @@ Call<ResponseBody> unpublish(
160160
@HeaderMap Map<String, Object> headers,
161161
@Path("content_type_uid") String contentTypeUid,
162162
@Path("entry_uid") String entryUid,
163+
@QueryMap(encoded = true) Map<String, Object> queryParameters,
163164
@Body JSONObject requestBody);
164165

166+
@GET("content_types/{content_type_uid}/entries/{entry_uid}/variants")
167+
Call<ResponseBody> fetchEntryVariants(
168+
@HeaderMap Map<String, Object> headers,
169+
@Path("content_type_uid") String contentTypeUid,
170+
@Path("entry_uid") String entryUid,
171+
@QueryMap(encoded = true) Map<String, Object> queryParameters);
172+
173+
@GET("content_types/{content_type_uid}/entries/{entry_uid}/variants/{variant_uid}")
174+
Call<ResponseBody> fetchEntryVariant(
175+
@HeaderMap Map<String, Object> headers,
176+
@Path("content_type_uid") String contentTypeUid,
177+
@Path("entry_uid") String entryUid,
178+
@Path("variant_uid") String variantUid,
179+
@QueryMap(encoded = true) Map<String, Object> queryParameters);
180+
181+
/**
182+
* Create entry variant (PUT …/variants/{variant_uid}). Same HTTP contract as update — CMA upserts on this path.
183+
*/
184+
@Headers("Content-Type: application/json")
185+
@PUT("content_types/{content_type_uid}/entries/{entry_uid}/variants/{variant_uid}")
186+
Call<ResponseBody> createEntryVariant(
187+
@HeaderMap Map<String, Object> headers,
188+
@Path("content_type_uid") String contentTypeUid,
189+
@Path("entry_uid") String entryUid,
190+
@Path("variant_uid") String variantUid,
191+
@QueryMap(encoded = true) Map<String, Object> queryParameters,
192+
@Body JSONObject requestBody);
193+
194+
/**
195+
* Update entry variant — delegates to {@link #createEntryVariant}; API uses one PUT upsert for both operations.
196+
*/
197+
default Call<ResponseBody> updateEntryVariant(
198+
Map<String, Object> headers,
199+
String contentTypeUid,
200+
String entryUid,
201+
String variantUid,
202+
Map<String, Object> queryParameters,
203+
JSONObject requestBody) {
204+
return createEntryVariant(headers, contentTypeUid, entryUid, variantUid, queryParameters, requestBody);
205+
}
206+
207+
@DELETE("content_types/{content_type_uid}/entries/{entry_uid}/variants/{variant_uid}")
208+
Call<ResponseBody> deleteEntryVariant(
209+
@HeaderMap Map<String, Object> headers,
210+
@Path("content_type_uid") String contentTypeUid,
211+
@Path("entry_uid") String entryUid,
212+
@Path("variant_uid") String variantUid,
213+
@QueryMap(encoded = true) Map<String, Object> queryParameters);
214+
165215
@GET("content_types/{content_type_uid}/entries")
166216
Call<ResponseBody> filterTaxonomy(
167217
@HeaderMap Map<String, Object> headers,

0 commit comments

Comments
 (0)