diff --git a/dotCMS/src/main/java/com/dotcms/rest/api/v3/contenttype/FieldResource.java b/dotCMS/src/main/java/com/dotcms/rest/api/v3/contenttype/FieldResource.java index d2e6477d056e..e3cce2423c14 100644 --- a/dotCMS/src/main/java/com/dotcms/rest/api/v3/contenttype/FieldResource.java +++ b/dotCMS/src/main/java/com/dotcms/rest/api/v3/contenttype/FieldResource.java @@ -8,13 +8,20 @@ import com.dotcms.rest.ResponseEntityView; import com.dotcms.rest.WebResource; import com.dotcms.rest.annotation.NoCache; +import com.dotcms.util.filtering.Specification; import com.dotmarketing.business.APILocator; import com.dotmarketing.exception.DotDataException; import com.dotmarketing.exception.DotSecurityException; +import com.dotmarketing.util.Logger; import com.liferay.portal.model.User; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.ExampleObject; +import io.swagger.v3.oas.annotations.responses.ApiResponse; import org.glassfish.jersey.server.JSONP; import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; import javax.ws.rs.Consumes; import javax.ws.rs.DELETE; import javax.ws.rs.GET; @@ -22,11 +29,13 @@ import javax.ws.rs.Path; import javax.ws.rs.PathParam; import javax.ws.rs.Produces; +import javax.ws.rs.QueryParam; import javax.ws.rs.core.Context; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; import java.util.List; import java.util.Map; +import java.util.Set; import java.util.stream.Collectors; /** @@ -36,6 +45,7 @@ */ @Path("/v3/contenttype/{typeIdOrVarName}/fields") public class FieldResource { + private final WebResource webResource; private final ContentTypeFieldLayoutAPI contentTypeFieldLayoutAPI; @@ -233,4 +243,122 @@ public Response deleteFields( ) )).build(); } + + /** + * Returns the list of fields in a Content Type that meet the specified criteria. For instance, + * if you need to retrieve all fields marked as 'required', 'unique'`, and 'system indexed', + * you can call the endpoint like this: + *
+     *     {@code
+     *     {{serverURL}}/api/v3/contenttype/AAA/fields/allfields?filter=REQUIRED&filter=SYSTEM_INDEXED&filter=UNIQUE
+     *     }
+     * 
+ * Just add the same `filter` parameter for each criterion you want to apply. + * + * @param request The current instance of the {@link HttpServletRequest}. + * @param response The current instance of the {@link HttpServletResponse}. + * @param typeIdOrVarName The Identifier or Velocity Variable Name of a given + * {@link ContentType}. + * @param criteria A set of {@link FilteringCriteria} objects that will be used to filter + * the fields. + * + * @return A {@link FieldResponseView} object that contains the list of {@link Field} objects + * that meet the specified criteria. + * + * @throws DotDataException An error occurred when interacting with the database. + * @throws DotSecurityException The specified User does not have the necessary permissions to + * execute this operation. + */ + @GET + @Path("/allfields") + @JSONP + @NoCache + @Consumes(MediaType.APPLICATION_JSON) + @Produces({MediaType.APPLICATION_JSON, "application/javascript"}) + @Operation( + operationId = "allfields", + summary = "Returns filtered Content Type fields", + description = "Returns the list of fields in a Content Type that meet the specified criteria.", + tags = {"Content Type Field"}, + requestBody = @io.swagger.v3.oas.annotations.parameters.RequestBody( + content = @Content( + examples = { + @ExampleObject( + name = "filter", + value = "REQUIRED,SYSTEM_INDEXED,UNIQUE,SHOW_IN_LIST,USER_SEARCHABLE", + summary = "Filter fields by one or more of the specified criteria" + ) + } + ) + ), + responses = { + @ApiResponse(responseCode = "200", description = "Content type retrieved successfully", + content = @Content(mediaType = "application/json", + examples = { + @ExampleObject( + description = "Returning a list of one Field matching the filtering criteria.", + value = "{\n" + + " \"entity\": [\n" + + " {\n" + + " \"clazz\": \"com.dotcms.contenttype.model.field.ImmutableTextField\",\n" + + " \"contentTypeId\": \"3b70f386cf65117a675f284eea928415\",\n" + + " \"dataType\": \"TEXT\",\n" + + " \"dbColumn\": \"text2\",\n" + + " \"defaultValue\": null,\n" + + " \"fixed\": false,\n" + + " \"forceIncludeInApi\": false,\n" + + " \"hint\": null,\n" + + " \"iDate\": 1732992811000,\n" + + " \"id\": \"e39533a92ee05d8c083f7e6a1a5ee5e5\",\n" + + " \"indexed\": true,\n" + + " \"listed\": false,\n" + + " \"modDate\": 1732992838000,\n" + + " \"name\": \"Description\",\n" + + " \"owner\": null,\n" + + " \"readOnly\": false,\n" + + " \"regexCheck\": null,\n" + + " \"relationType\": null,\n" + + " \"required\": true,\n" + + " \"searchable\": false,\n" + + " \"sortOrder\": 3,\n" + + " \"unique\": true,\n" + + " \"values\": null,\n" + + " \"variable\": \"description\"\n" + + " }\n" + + " ],\n" + + " \"errors\": [],\n" + + " \"i18nMessagesMap\": {},\n" + + " \"messages\": [],\n" + + " \"pagination\": null,\n" + + " \"permissions\": []\n" + + "}" + ) + } + ) + ), + @ApiResponse(responseCode = "400", description = "Bad Request, when using invalid filter names"), + @ApiResponse(responseCode = "401", description = "Invalid User"), + @ApiResponse(responseCode = "404", description = "Content Type was not found"), + @ApiResponse(responseCode = "500", description = "Internal Server Error") + } + ) + public FieldResponseView allFieldsBy(@Context final HttpServletRequest request, + @Context final HttpServletResponse response, + @PathParam("typeIdOrVarName") final String typeIdOrVarName, + @QueryParam("filter") final Set criteria) throws DotDataException, DotSecurityException { + final InitDataObject initDataObject = new WebResource.InitBuilder(this.webResource) + .requestAndResponse(request, response) + .requiredBackendUser(true) + .rejectWhenNoUser(true) + .init(); + final User user = initDataObject.getUser(); + Logger.debug(this, () -> String.format("Returning filtered fields from Content Type '%s' " + + "using the criteria: %s", typeIdOrVarName, criteria)); + final ContentType contentType = APILocator.getContentTypeAPI(user).find(typeIdOrVarName); + final Specification fieldSpecification = FilteringCriteria.specificationsFrom(criteria); + final List filteredFields = contentType.fields().stream().filter(fieldSpecification::isSatisfiedBy) + .collect(Collectors.toList()); + return new FieldResponseView(filteredFields); + } + } diff --git a/dotCMS/src/main/java/com/dotcms/rest/api/v3/contenttype/FieldResponseView.java b/dotCMS/src/main/java/com/dotcms/rest/api/v3/contenttype/FieldResponseView.java new file mode 100644 index 000000000000..924db74c1b2f --- /dev/null +++ b/dotCMS/src/main/java/com/dotcms/rest/api/v3/contenttype/FieldResponseView.java @@ -0,0 +1,21 @@ +package com.dotcms.rest.api.v3.contenttype; + +import com.dotcms.contenttype.model.field.Field; +import com.dotcms.rest.ResponseEntityView; + +import java.util.List; + +/** + * This class represents a Response View for objects of type {@link Field}. It's very useful for + * returning a list of fields, or a filtered sub-set if necessary. + * + * @author Jose Castro + * @since Nov 29th, 2024 + */ +public class FieldResponseView extends ResponseEntityView> { + + public FieldResponseView(final List entity) { + super(entity); + } + +} diff --git a/dotCMS/src/main/java/com/dotcms/rest/api/v3/contenttype/FilteringCriteria.java b/dotCMS/src/main/java/com/dotcms/rest/api/v3/contenttype/FilteringCriteria.java new file mode 100644 index 000000000000..2010d0e15d31 --- /dev/null +++ b/dotCMS/src/main/java/com/dotcms/rest/api/v3/contenttype/FilteringCriteria.java @@ -0,0 +1,66 @@ +package com.dotcms.rest.api.v3.contenttype; + +import com.dotcms.contenttype.model.field.Field; +import com.dotcms.util.filtering.Specification; +import com.dotcms.util.filtering.contenttype.field.CombinedSpecification; +import com.dotcms.util.filtering.contenttype.field.RequiredSpecification; +import com.dotcms.util.filtering.contenttype.field.ShowInListSpecification; +import com.dotcms.util.filtering.contenttype.field.SystemIndexedSpecification; +import com.dotcms.util.filtering.contenttype.field.UniqueSpecification; +import com.dotcms.util.filtering.contenttype.field.UserSearchableSpecification; + +import java.util.Set; + +/** + * Enum that represents the different criteria that can be used to filter fields in a Content Type. + * This allows you to get a specific list of {@link Field}s from a Content Type that meet your + * specified criteria via @{link Specification} classes. In here, you can define as many + * Specifications as you need. + *

The initial specifications represent the default attributes that you can enable for a given + * field, such as:

+ *
    + *
  • Required.
  • + *
  • User Searchable.
  • + *
  • System Indexed.
  • + *
  • Show in List.
  • + *
  • Unique.
  • + *
+ * + * @author Jose Castro + * @since Nov 30th, 2024 + */ +public enum FilteringCriteria { + + REQUIRED(new RequiredSpecification()), + USER_SEARCHABLE(new UserSearchableSpecification()), + SYSTEM_INDEXED(new SystemIndexedSpecification()), + SHOW_IN_LIST(new ShowInListSpecification()), + UNIQUE(new UniqueSpecification()); + + final Specification specification; + + FilteringCriteria(final Specification specification) { + this.specification = specification; + } + + public Specification specification() { + return this.specification; + } + + /** + * Takes the list of criteria that will be used to query for specific fields in a Content Type + * and generates the appropriate Specification object to be used in the filtering. + * + * @param criteria A Set of FilteringCriteria objects that will be used to filter the fields. + * + * @return The {@link Specification} object that will be used to filter the fields. + */ + public static Specification specificationsFrom(final Set criteria) { + Specification mainSpec = new CombinedSpecification(); + for (final FilteringCriteria criterion : criteria) { + mainSpec = mainSpec.and(criterion.specification()); + } + return mainSpec; + } + +} diff --git a/dotCMS/src/main/java/com/dotcms/rest/config/DotRestApplication.java b/dotCMS/src/main/java/com/dotcms/rest/config/DotRestApplication.java index 1c155bc5823b..51f0cc796c68 100644 --- a/dotCMS/src/main/java/com/dotcms/rest/config/DotRestApplication.java +++ b/dotCMS/src/main/java/com/dotcms/rest/config/DotRestApplication.java @@ -43,6 +43,7 @@ @Tag(name = "Workflow"), @Tag(name = "Page"), @Tag(name = "Content Type"), + @Tag(name = "Content Type Field"), @Tag(name = "Content Delivery"), @Tag(name = "Bundle"), @Tag(name = "Navigation"), diff --git a/dotCMS/src/main/java/com/dotcms/util/filtering/Specification.java b/dotCMS/src/main/java/com/dotcms/util/filtering/Specification.java new file mode 100644 index 000000000000..348524674fa3 --- /dev/null +++ b/dotCMS/src/main/java/com/dotcms/util/filtering/Specification.java @@ -0,0 +1,61 @@ +package com.dotcms.util.filtering; + +import java.io.Serializable; + +/** + * This class represents the base Specification interface for filtering objects that meet given + * satisfaction criteria using the Specification Pattern, which is a behavioral pattern + * often used to encapsulate filtering logic. + *

By implementing this interface, you can create your own custom Specification class to filter + * objects based on given criteria.

+ * + * @param The type of the object to be filtered. + * + * @author Jose Castro + * @since Nov 29th, 2024 + */ +public interface Specification extends Serializable { + + /** + * Determines whether the provided object meets the satisfaction criteria for your Specification + * or not. + * + * @param item The object to be evaluated. + * + * @return If the object meets your criteria, returns {@code true}. + */ + boolean isSatisfiedBy(final T item); + + /** + * Allows you to chain multiple Specifications together using the logical AND operator. + * + * @param other The other Specification to be chained. + * + * @return If all chained Specifications are satisfied, returns {@code true}. + */ + default Specification and(final Specification other) { + return item -> Specification.this.isSatisfiedBy(item) && other.isSatisfiedBy(item); + } + + /** + * Allows you to chain multiple Specifications together using the logical OR operator. + * + * @param other The other Specification to be chained. + * + * @return If any of the chained Specifications are satisfied, returns {@code true}. + */ + default Specification or(final Specification other) { + return item -> this.isSatisfiedBy(item) || other.isSatisfiedBy(item); + } + + /** + * Allows you to chain multiple Specifications together using the logical NOT operator. This is + * particularly useful for excluding objects from your result set. + * + * @return If the Specification is not satisfied, returns {@code true}. + */ + default Specification not() { + return item -> !this.isSatisfiedBy(item); + } + +} diff --git a/dotCMS/src/main/java/com/dotcms/util/filtering/contenttype/field/CombinedSpecification.java b/dotCMS/src/main/java/com/dotcms/util/filtering/contenttype/field/CombinedSpecification.java new file mode 100644 index 000000000000..388547acbdfe --- /dev/null +++ b/dotCMS/src/main/java/com/dotcms/util/filtering/contenttype/field/CombinedSpecification.java @@ -0,0 +1,22 @@ +package com.dotcms.util.filtering.contenttype.field; + +import com.dotcms.contenttype.model.field.Field; +import com.dotcms.util.filtering.Specification; + +/** + * This simple Specification serves as a base/placeholder for developers to combine multiple + * {@link Specification} objects. This is very useful when the given list of Specifications is + * dynamic. + * + * @author Jose Castro + * @since Nov 29th, 2024 + */ +public class CombinedSpecification implements Specification { + + @Override + public boolean isSatisfiedBy(final Field field) { + // This base Specification returns 'true' by default + return true; + } + +} diff --git a/dotCMS/src/main/java/com/dotcms/util/filtering/contenttype/field/RequiredSpecification.java b/dotCMS/src/main/java/com/dotcms/util/filtering/contenttype/field/RequiredSpecification.java new file mode 100644 index 000000000000..aef7a30f35cc --- /dev/null +++ b/dotCMS/src/main/java/com/dotcms/util/filtering/contenttype/field/RequiredSpecification.java @@ -0,0 +1,19 @@ +package com.dotcms.util.filtering.contenttype.field; + +import com.dotcms.contenttype.model.field.Field; +import com.dotcms.util.filtering.Specification; + +/** + * This Specification is responsible for verifying if a Field is flagged as {@code Required}. + * + * @author Jose Castro + * @since Nov 29th, 2024 + */ +public class RequiredSpecification implements Specification { + + @Override + public boolean isSatisfiedBy(final Field field) { + return field.required(); + } + +} diff --git a/dotCMS/src/main/java/com/dotcms/util/filtering/contenttype/field/ShowInListSpecification.java b/dotCMS/src/main/java/com/dotcms/util/filtering/contenttype/field/ShowInListSpecification.java new file mode 100644 index 000000000000..6c51684b03a3 --- /dev/null +++ b/dotCMS/src/main/java/com/dotcms/util/filtering/contenttype/field/ShowInListSpecification.java @@ -0,0 +1,19 @@ +package com.dotcms.util.filtering.contenttype.field; + +import com.dotcms.contenttype.model.field.Field; +import com.dotcms.util.filtering.Specification; + +/** + * This Specification is responsible for verifying if a Field is flagged as {@code Show in List}. + * + * @author Jose Castro + * @since Nov 29th, 2024 + */ +public class ShowInListSpecification implements Specification { + + @Override + public boolean isSatisfiedBy(final Field field) { + return field.listed(); + } + +} diff --git a/dotCMS/src/main/java/com/dotcms/util/filtering/contenttype/field/SystemIndexedSpecification.java b/dotCMS/src/main/java/com/dotcms/util/filtering/contenttype/field/SystemIndexedSpecification.java new file mode 100644 index 000000000000..4aa8e59f151d --- /dev/null +++ b/dotCMS/src/main/java/com/dotcms/util/filtering/contenttype/field/SystemIndexedSpecification.java @@ -0,0 +1,19 @@ +package com.dotcms.util.filtering.contenttype.field; + +import com.dotcms.contenttype.model.field.Field; +import com.dotcms.util.filtering.Specification; + +/** + * This Specification is responsible for verifying if a Field is flagged as {@code System Indexed}. + * + * @author Jose Castro + * @since Nov 29th, 2024 + */ +public class SystemIndexedSpecification implements Specification { + + @Override + public boolean isSatisfiedBy(final Field field) { + return field.indexed(); + } + +} diff --git a/dotCMS/src/main/java/com/dotcms/util/filtering/contenttype/field/UniqueSpecification.java b/dotCMS/src/main/java/com/dotcms/util/filtering/contenttype/field/UniqueSpecification.java new file mode 100644 index 000000000000..64675649a0c2 --- /dev/null +++ b/dotCMS/src/main/java/com/dotcms/util/filtering/contenttype/field/UniqueSpecification.java @@ -0,0 +1,19 @@ +package com.dotcms.util.filtering.contenttype.field; + +import com.dotcms.contenttype.model.field.Field; +import com.dotcms.util.filtering.Specification; + +/** + * This Specification is responsible for verifying if a Field is flagged as {@code Unique}. + * + * @author Jose Castro + * @since Nov 29th, 2024 + */ +public class UniqueSpecification implements Specification { + + @Override + public boolean isSatisfiedBy(final Field field) { + return field.unique(); + } + +} diff --git a/dotCMS/src/main/java/com/dotcms/util/filtering/contenttype/field/UserSearchableSpecification.java b/dotCMS/src/main/java/com/dotcms/util/filtering/contenttype/field/UserSearchableSpecification.java new file mode 100644 index 000000000000..2a28ea000592 --- /dev/null +++ b/dotCMS/src/main/java/com/dotcms/util/filtering/contenttype/field/UserSearchableSpecification.java @@ -0,0 +1,20 @@ +package com.dotcms.util.filtering.contenttype.field; + +import com.dotcms.contenttype.model.field.Field; +import com.dotcms.util.filtering.Specification; + +/** + * This Specification is responsible for verifying if a Field is flagged as + * {@code User Searchable}. + * + * @author Jose Castro + * @since Nov 29th, 2024 + */ +public class UserSearchableSpecification implements Specification { + + @Override + public boolean isSatisfiedBy(final Field field) { + return field.searchable(); + } + +} diff --git a/dotcms-postman/src/main/resources/postman/ContentTypeResourceTests.json b/dotcms-postman/src/main/resources/postman/ContentTypeResourceTests.json index 999a261cdaa7..9bb863b3aeea 100644 --- a/dotcms-postman/src/main/resources/postman/ContentTypeResourceTests.json +++ b/dotcms-postman/src/main/resources/postman/ContentTypeResourceTests.json @@ -1,9 +1,10 @@ { "info": { - "_postman_id": "c2e4152e-4233-43ae-8e7c-c2e682ade9f8", + "_postman_id": "9f75619d-685f-4bdb-b16d-4baa2f6d62d6", "name": "ContentType Resource", "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json", - "_exporter_id": "31066048" + "_exporter_id": "5403727", + "_collection_link": "https://cloudy-robot-285072.postman.co/workspace/JCastro-Workspace~5bfa586e-54db-429b-b7d5-c4ff997e3a0d/collection/5403727-9f75619d-685f-4bdb-b16d-4baa2f6d62d6?action=share&source=collection_link&creator=5403727" }, "item": [ { @@ -379,6 +380,546 @@ { "name": "Test CRUD Fields", "item": [ + { + "name": "Filtering Fields", + "item": [ + { + "name": "Generate Test Data", + "item": [ + { + "name": "Create Test Content Type", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "pm.collectionVariables.set(\"typeNameWithMetadata\", \"test-type-\" + \"{{$randomInt}}\");" + ], + "type": "text/javascript", + "packages": {} + } + }, + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Test Content Type was created successfully\", function () {", + " const jsonData = pm.response.json();", + " const entity = jsonData.entity[0];", + " pm.expect(jsonData.errors.length).to.eql(0);", + " pm.collectionVariables.set(\"contentTypeId\", entity.id);", + "});", + "" + ], + "type": "text/javascript", + "packages": {} + } + } + ], + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "{\n \"defaultType\": false,\n \"icon\": null,\n \"fixed\": false,\n \"system\": false,\n \"clazz\": \"com.dotcms.contenttype.model.type.ImmutableSimpleContentType\",\n \"description\": \"\",\n \"host\": \"48190c8c-42c4-46af-8d1a-0cd5db894797\",\n \"name\": \"My Test CT\",\n \"metadata\": {\n \"edit_mode\": true\n },\n \"systemActionMappings\": {\n \"NEW\": \"\"\n },\n \"workflow\": [\n \"d61a59e1-a49c-46f2-a929-db2b4bfa88b2\"\n ]\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{serverURL}}/api/v1/contenttype", + "host": [ + "{{serverURL}}" + ], + "path": [ + "api", + "v1", + "contenttype" + ] + } + }, + "response": [] + }, + { + "name": "Add Fields to Content Type", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Test Content Type Fields were created successfully\", function () {", + " const jsonData = pm.response.json();", + " pm.expect(jsonData.errors.length).to.eql(0);", + "});", + "" + ], + "type": "text/javascript", + "packages": {} + } + } + ], + "request": { + "method": "PUT", + "header": [], + "body": { + "mode": "raw", + "raw": "{\n \"layout\": [\n {\n \"divider\": {\n \"clazz\": \"com.dotcms.contenttype.model.field.ImmutableRowField\",\n \"contentTypeId\": \"{{contentTypeId}}\",\n \"dataType\": \"SYSTEM\",\n \"fieldContentTypeProperties\": [],\n \"fieldType\": \"Row\",\n \"fieldTypeLabel\": \"Row\",\n \"fieldVariables\": [],\n \"fixed\": false,\n \"forceIncludeInApi\": false,\n \"iDate\": 1705078587000,\n \"indexed\": false,\n \"listed\": false,\n \"modDate\": 1705078587000,\n \"name\": \"Row Field\",\n \"readOnly\": false,\n \"required\": false,\n \"searchable\": false,\n \"sortOrder\": -1,\n \"unique\": false\n },\n \"columns\": [\n {\n \"columnDivider\": {\n \"clazz\": \"com.dotcms.contenttype.model.field.ImmutableColumnField\",\n \"dataType\": \"SYSTEM\",\n \"fieldContentTypeProperties\": [],\n \"fieldType\": \"Column\",\n \"fieldTypeLabel\": \"Column\",\n \"fieldVariables\": [],\n \"fixed\": false,\n \"forceIncludeInApi\": false,\n \"iDate\": 1705078587000,\n \"indexed\": false,\n \"listed\": false,\n \"modDate\": 1705078587000,\n \"name\": \"Column Field\",\n \"readOnly\": false,\n \"required\": false,\n \"searchable\": false,\n \"sortOrder\": -1,\n \"unique\": false\n },\n \"fields\": [\n {\n \"clazz\": \"com.dotcms.contenttype.model.field.ImmutableTextField\",\n \"name\": \"Title\",\n \"dataType\": \"TEXT\",\n \"regexCheck\": \"\",\n \"defaultValue\": \"\",\n \"hint\": \"\",\n \"required\": true,\n \"searchable\": true,\n \"listed\": true,\n \"unique\": false,\n \"id\": null\n },\n {\n \"clazz\": \"com.dotcms.contenttype.model.field.ImmutableTextField\",\n \"name\": \"Description\",\n \"dataType\": \"TEXT\",\n \"regexCheck\": \"\",\n \"defaultValue\": \"\",\n \"hint\": \"\",\n \"required\": false,\n \"searchable\": true,\n \"listed\": false,\n \"unique\": false,\n \"id\": null\n }\n ]\n }\n ]\n }\n ]\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{serverURL}}/api/v3/contenttype/{{contentTypeId}}/fields/move", + "host": [ + "{{serverURL}}" + ], + "path": [ + "api", + "v3", + "contenttype", + "{{contentTypeId}}", + "fields", + "move" + ] + } + }, + "response": [] + } + ], + "event": [ + { + "listen": "prerequest", + "script": { + "type": "text/javascript", + "packages": {}, + "exec": [ + "" + ] + } + }, + { + "listen": "test", + "script": { + "type": "text/javascript", + "packages": {}, + "exec": [ + "pm.test(\"HTTP Status code must be successful\", function () {", + " pm.response.to.have.status(200);", + "});", + "" + ] + } + } + ] + }, + { + "name": "Success", + "item": [ + { + "name": "Filter By Required Fields", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Data was returned successfully\", function () {", + " const jsonData = pm.response.json();", + " pm.expect(jsonData.errors.length).to.eql(0);", + "});", + "", + "pm.test(\"There must be only one 'Required' field\", function () {", + " const entity = pm.response.json().entity;", + " pm.expect(entity.length).to.equal(1, \"Just one field must be marked as 'Required'\");", + "});", + "" + ], + "type": "text/javascript", + "packages": {} + } + } + ], + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{serverURL}}/api/v3/contenttype/{{contentTypeId}}/fields/allfields?filter=REQUIRED", + "host": [ + "{{serverURL}}" + ], + "path": [ + "api", + "v3", + "contenttype", + "{{contentTypeId}}", + "fields", + "allfields" + ], + "query": [ + { + "key": "filter", + "value": "REQUIRED" + } + ] + } + }, + "response": [] + }, + { + "name": "Filter By Indexed Fields", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Data was returned successfully\", function () {", + " const jsonData = pm.response.json();", + " pm.expect(jsonData.errors.length).to.eql(0);", + "});", + "", + "pm.test(\"There must be two 'System Indexed' fields\", function () {", + " const entity = pm.response.json().entity;", + " pm.expect(entity.length).to.equal(2, \"Two fields must be marked as 'System Indexed'\");", + "});", + "" + ], + "type": "text/javascript", + "packages": {} + } + } + ], + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{serverURL}}/api/v3/contenttype/{{contentTypeId}}/fields/allfields?filter=SYSTEM_INDEXED", + "host": [ + "{{serverURL}}" + ], + "path": [ + "api", + "v3", + "contenttype", + "{{contentTypeId}}", + "fields", + "allfields" + ], + "query": [ + { + "key": "filter", + "value": "SYSTEM_INDEXED" + } + ] + } + }, + "response": [] + }, + { + "name": "Filter By Several Criteria", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Data was returned successfully\", function () {", + " const jsonData = pm.response.json();", + " pm.expect(jsonData.errors.length).to.eql(0);", + "});", + "", + "pm.test(\"There must be only one 'Required', 'User Searchable', and 'Show in List' field\", function () {", + " const entity = pm.response.json().entity;", + " pm.expect(entity.length).to.equal(1, \"Just one field must be marked as 'Required', 'User Searchable', and 'Show in List' field\");", + "});", + "" + ], + "type": "text/javascript", + "packages": {} + } + } + ], + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{serverURL}}/api/v3/contenttype/{{contentTypeId}}/fields/allfields?filter=REQUIRED&filter=USER_SEARCHABLE&filter=SHOW_IN_LIST", + "host": [ + "{{serverURL}}" + ], + "path": [ + "api", + "v3", + "contenttype", + "{{contentTypeId}}", + "fields", + "allfields" + ], + "query": [ + { + "key": "filter", + "value": "REQUIRED" + }, + { + "key": "filter", + "value": "USER_SEARCHABLE" + }, + { + "key": "filter", + "value": "SHOW_IN_LIST" + } + ] + } + }, + "response": [] + }, + { + "name": "Filter By Non-met Criteria", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Data was returned successfully\", function () {", + " const jsonData = pm.response.json();", + " pm.expect(jsonData.errors.length).to.eql(0);", + "});", + "", + "pm.test(\"No field must match 'Required', 'User Searchable', 'Show in List' and 'Unique'\", function () {", + " const entity = pm.response.json().entity;", + " pm.expect(entity.length).to.equal(0, \"No field must match the four specified filters\");", + "});", + "" + ], + "type": "text/javascript", + "packages": {} + } + } + ], + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{serverURL}}/api/v3/contenttype/{{contentTypeId}}/fields/allfields?filter=REQUIRED&filter=USER_SEARCHABLE&filter=SHOW_IN_LIST&filter=UNIQUE", + "host": [ + "{{serverURL}}" + ], + "path": [ + "api", + "v3", + "contenttype", + "{{contentTypeId}}", + "fields", + "allfields" + ], + "query": [ + { + "key": "filter", + "value": "REQUIRED" + }, + { + "key": "filter", + "value": "USER_SEARCHABLE" + }, + { + "key": "filter", + "value": "SHOW_IN_LIST" + }, + { + "key": "filter", + "value": "UNIQUE" + } + ] + } + }, + "response": [] + } + ], + "event": [ + { + "listen": "prerequest", + "script": { + "type": "text/javascript", + "packages": {}, + "exec": [ + "" + ] + } + }, + { + "listen": "test", + "script": { + "type": "text/javascript", + "packages": {}, + "exec": [ + "pm.test(\"HTTP Status code must be successful\", function () {", + " pm.response.to.have.status(200);", + "});", + "" + ] + } + } + ] + }, + { + "name": "Failure", + "item": [ + { + "name": "Log User out", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{serverURL}}/dotAdmin/logout", + "host": [ + "{{serverURL}}" + ], + "path": [ + "dotAdmin", + "logout" + ] + }, + "description": "This request just makes sure that the next request does NOT have an authenticated User." + }, + "response": [] + }, + { + "name": "No Authentication", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"HTTP Status code must be 'Unauthorized'\", function () {", + " pm.response.to.have.status(401);", + "});", + "" + ], + "type": "text/javascript", + "packages": {} + } + } + ], + "request": { + "auth": { + "type": "noauth" + }, + "method": "GET", + "header": [], + "url": { + "raw": "{{serverURL}}/api/v3/contenttype/{{contentTypeId}}/fields/allfields?filter=REQUIRED", + "host": [ + "{{serverURL}}" + ], + "path": [ + "api", + "v3", + "contenttype", + "{{contentTypeId}}", + "fields", + "allfields" + ], + "query": [ + { + "key": "filter", + "value": "REQUIRED" + } + ] + } + }, + "response": [] + }, + { + "name": "Using Invalid Filter", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"HTTP Status code must be 'Bad Request'\", function () {", + " pm.response.to.have.status(400);", + "});", + "" + ], + "type": "text/javascript", + "packages": {} + } + } + ], + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{serverURL}}/api/v3/contenttype/{{contentTypeId}}/fields/allfields?filter=INVALID_FILTER", + "host": [ + "{{serverURL}}" + ], + "path": [ + "api", + "v3", + "contenttype", + "{{contentTypeId}}", + "fields", + "allfields" + ], + "query": [ + { + "key": "filter", + "value": "INVALID_FILTER" + } + ] + } + }, + "response": [] + }, + { + "name": "Using Invalid Content Type", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"HTTP Status code must be 'Not Found'\", function () {", + " pm.response.to.have.status(404);", + "});", + "" + ], + "type": "text/javascript", + "packages": {} + } + } + ], + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{serverURL}}/api/v3/contenttype/INVALID_CONTENT_TYPE/fields/allfields?filter=REQUIRED", + "host": [ + "{{serverURL}}" + ], + "path": [ + "api", + "v3", + "contenttype", + "INVALID_CONTENT_TYPE", + "fields", + "allfields" + ], + "query": [ + { + "key": "filter", + "value": "REQUIRED" + } + ] + } + }, + "response": [] + } + ] + } + ] + }, { "name": "Create ContentType", "event": [ @@ -13486,95 +14027,5 @@ ] } } - ], - "variable": [ - { - "key": "testDetailPagePageId1", - "value": "" - }, - { - "key": "testDetailPagePageURL1", - "value": "" - }, - { - "key": "testDetailPagePageId2", - "value": "" - }, - { - "key": "testDetailPagePageURL2", - "value": "" - }, - { - "key": "testDetailPagePageInode1", - "value": "" - }, - { - "key": "testDetailPagePageInode2", - "value": "" - }, - { - "key": "contentTypeID", - "value": "" - }, - { - "key": "contentTypeVAR", - "value": "" - }, - { - "key": "contentTypeFieldID", - "value": "" - }, - { - "key": "pageDetailSiteId", - "value": "" - }, - { - "key": "pageDetailSiteName", - "value": "" - }, - { - "key": "contentTypeId", - "value": "" - }, - { - "key": "pageDetailPageId1", - "value": "" - }, - { - "key": "pageDetailPageInode1", - "value": "" - }, - { - "key": "pageDetailPageURL1", - "value": "" - }, - { - "key": "pageDetailPageId2", - "value": "" - }, - { - "key": "pageDetailPageInode2", - "value": "" - }, - { - "key": "pageDetailPageURL2", - "value": "" - }, - { - "key": "contentTypeVariable", - "value": "" - }, - { - "key": "contentTypeField1Id", - "value": "" - }, - { - "key": "contentTypeField1Variable", - "value": "" - }, - { - "key": "skipPreResquest", - "value": "" - } ] } \ No newline at end of file