diff --git a/CHANGELOG.md b/CHANGELOG.md index b33941d..e508bb8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,31 @@ # Changelog All notable changes to this project will be documented in this file. +## [1.99.1] + +### Added + +- Add support for form features: + - `AccessControl` model - Represents permissions for view/edit access to forms + - `Form` model - Complete form definition including field definitions + - `FormField` abstract model – for all form field types + - `FormState` enum - Enumeration for form publishing states (NEW, PUBLISHED, UNPUBLISHED, OLD) + - `ChoiceFormField` model - Checkbox field with multiple selection support + - `DateFormField` model - Date input field with min/max validation + - `NumberFormField` model - Numeric field with range and decimal places + - `RadioFormField` model - Radio button field for single selection + - `StringFormField` model - Short text input field (max 255 characters) + - `TextFormField` model - Long text field supporting HTML content + - `TimeFormField` model - Time input field storing millisecond values + +### Changed + +- Enhanced `FormInfo` model with additional properties: + - Added `formState` property for tracking publishing status + - Added `accessControl` property for permission management + - Added `tags` property for form categorization + - Added `iconId` property for form visual identification + ## [1.99.0] - switch from rspace-os-parent to rspace-parent as parent pom @@ -17,10 +42,10 @@ All notable changes to this project will be documented in this file. ### Added - Introduced support for Inventory features: - - `Barcode` model - - `TagInfo` model - - `RecordInfo` model - - `SharedWith` model + - `Barcode` model + - `TagInfo` model + - `RecordInfo` model + - `SharedWith` model - Added the ability to represent sharing mode, barcodes, and tags for Inventory. ## [1.97.0] diff --git a/pom.xml b/pom.xml index 3929352..628e493 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ 4.0.0 rspace-client-java-model - 1.99.0 + 1.99.1 rspace-client-java-model com.github.rspace-os diff --git a/src/main/java/com/researchspace/api/clientmodel/AccessControl.java b/src/main/java/com/researchspace/api/clientmodel/AccessControl.java new file mode 100644 index 0000000..28b3d16 --- /dev/null +++ b/src/main/java/com/researchspace/api/clientmodel/AccessControl.java @@ -0,0 +1,21 @@ +package com.researchspace.api.clientmodel; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * Information about the permissions to view or edit a form. + * + * @author rspace + */ +@Data +@NoArgsConstructor +@AllArgsConstructor +@Builder +public class AccessControl { + private String ownerPermissionType; + private String groupPermissionType; + private String worldPermissionType; +} \ No newline at end of file diff --git a/src/main/java/com/researchspace/api/clientmodel/ChoiceFormField.java b/src/main/java/com/researchspace/api/clientmodel/ChoiceFormField.java new file mode 100644 index 0000000..a6d2ac3 --- /dev/null +++ b/src/main/java/com/researchspace/api/clientmodel/ChoiceFormField.java @@ -0,0 +1,41 @@ +package com.researchspace.api.clientmodel; + +import com.fasterxml.jackson.annotation.JsonPropertyOrder; +import lombok.Builder; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import lombok.experimental.SuperBuilder; + +import javax.validation.constraints.Size; +import java.util.ArrayList; +import java.util.List; + +/** + * A Choice form field defines a list of checkboxes, some of which can be selected by default. + * @author rspace + */ +@Data +@EqualsAndHashCode(callSuper = true) +@NoArgsConstructor +@SuperBuilder +@JsonPropertyOrder(value = {"id", "globalId", "name", "type", "lastModified", "index", + "multipleChoice", "options", "defaultOptions"}) +public class ChoiceFormField extends FormField { + + private boolean multipleChoice; + @Size(min = 1, message = "Please provide at least one option") + @Builder.Default + private List options = new ArrayList<>(); + @Builder.Default + private List defaultOptions = new ArrayList<>(); + + public ChoiceFormField(String name, boolean multipleChoice, List options, List defaultOptions) { + super(); + setName(name); + setType("Choice"); + this.multipleChoice = multipleChoice; + this.options = options != null ? options : new ArrayList<>(); + this.defaultOptions = defaultOptions != null ? defaultOptions : new ArrayList<>(); + } +} \ No newline at end of file diff --git a/src/main/java/com/researchspace/api/clientmodel/DateFormField.java b/src/main/java/com/researchspace/api/clientmodel/DateFormField.java new file mode 100644 index 0000000..0ea57ae --- /dev/null +++ b/src/main/java/com/researchspace/api/clientmodel/DateFormField.java @@ -0,0 +1,42 @@ +package com.researchspace.api.clientmodel; + +import com.fasterxml.jackson.annotation.JsonPropertyOrder; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.researchspace.api.jackson.ISO8601DateSerialiser; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import lombok.experimental.SuperBuilder; + +import java.util.Date; + +/** + * A Date form field - defines a DateField. + * @author rspace + */ +@Data +@EqualsAndHashCode(callSuper = true) +@NoArgsConstructor +@SuperBuilder +@JsonPropertyOrder(value = {"id", "globalId", "name", "type", "lastModified", "index", + "defaultValue", "min", "max"}) +public class DateFormField extends FormField { + + + @JsonSerialize(using = ISO8601DateSerialiser.class) + private Date defaultValue; + @JsonSerialize(using = ISO8601DateSerialiser.class) + private Date min; + + @JsonSerialize(using = ISO8601DateSerialiser.class) + private Date max; + + public DateFormField(String name, Date defaultValue, Date min, Date max) { + super(); + setName(name); + setType("Date"); + this.defaultValue = defaultValue != null ? new Date(defaultValue.getTime()) : null; + this.min = min != null ? new Date(min.getTime()) : null; + this.max = max != null ? new Date(max.getTime()) : null; + } +} \ No newline at end of file diff --git a/src/main/java/com/researchspace/api/clientmodel/Form.java b/src/main/java/com/researchspace/api/clientmodel/Form.java new file mode 100644 index 0000000..a5592af --- /dev/null +++ b/src/main/java/com/researchspace/api/clientmodel/Form.java @@ -0,0 +1,42 @@ +package com.researchspace.api.clientmodel; + +import com.fasterxml.jackson.annotation.JsonPropertyOrder; +import lombok.Builder; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import lombok.experimental.SuperBuilder; + +import java.util.ArrayList; +import java.util.List; + +/** + * A Form. A complete form definition including field definitions. + * @author rspace + */ +@Data +@EqualsAndHashCode(callSuper = true) +@NoArgsConstructor +@SuperBuilder +@JsonPropertyOrder(value = { + "id", "globalId", "stableId", "version", "name", "tags", + "formState", "accessControl", "fields", "_links" +}) +public class Form extends FormInfo { + + @Builder.Default + private List fields = new ArrayList<>(); + + /** + * Adds a field to this form + * @param field the FormField to add + * @return this Form for method chaining + */ + public Form addField(FormField field) { + if (this.fields == null) { + this.fields = new ArrayList<>(); + } + this.fields.add(field); + return this; + } +} \ No newline at end of file diff --git a/src/main/java/com/researchspace/api/clientmodel/FormField.java b/src/main/java/com/researchspace/api/clientmodel/FormField.java new file mode 100644 index 0000000..56f7eef --- /dev/null +++ b/src/main/java/com/researchspace/api/clientmodel/FormField.java @@ -0,0 +1,49 @@ +package com.researchspace.api.clientmodel; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonPropertyOrder; +import com.fasterxml.jackson.annotation.JsonSubTypes; +import com.fasterxml.jackson.annotation.JsonSubTypes.Type; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import com.fasterxml.jackson.annotation.JsonTypeInfo.Id; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import lombok.experimental.SuperBuilder; + +/** + * A FormField definition. This is an abstract type for all form field types. + * The properties listed here are common to all types. + * + * @author rspace + */ +@Data +@EqualsAndHashCode(callSuper = true) +@NoArgsConstructor +@AllArgsConstructor +@SuperBuilder +@JsonTypeInfo( + use = Id.NAME, + include = JsonTypeInfo.As.PROPERTY, + property = "type", + visible = true +) +@JsonSubTypes({ + @Type(value = ChoiceFormField.class, name = "Choice"), + @Type(value = TextFormField.class, name = "Text"), + @Type(value = StringFormField.class, name = "String"), + @Type(value = NumberFormField.class, name = "Number"), + @Type(value = RadioFormField.class, name = "Radio"), + @Type(value = DateFormField.class, name = "Date"), + @Type(value = TimeFormField.class, name = "Time"), +}) +@JsonPropertyOrder(value = {"id", "globalId", "name", "type", "lastModified", "index"}) +public abstract class FormField extends IdentifiableNameable { + + @JsonProperty("lastModified") + private String lastModifiedMillis; + private Integer index; + @JsonProperty("type") + private String type; +} \ No newline at end of file diff --git a/src/main/java/com/researchspace/api/clientmodel/FormInfo.java b/src/main/java/com/researchspace/api/clientmodel/FormInfo.java index 7f59f14..08209fa 100644 --- a/src/main/java/com/researchspace/api/clientmodel/FormInfo.java +++ b/src/main/java/com/researchspace/api/clientmodel/FormInfo.java @@ -24,9 +24,12 @@ package com.researchspace.api.clientmodel; +import com.fasterxml.jackson.annotation.JsonPropertyOrder; +import lombok.AllArgsConstructor; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; +import lombok.experimental.SuperBuilder; /** * The Form on which the document structure is based. @@ -34,9 +37,19 @@ @Data @EqualsAndHashCode(callSuper=true) @NoArgsConstructor -public class FormInfo extends IdentifiableNameable { +@AllArgsConstructor +@SuperBuilder +@JsonPropertyOrder(value = { + "id", "globalId", "stableId", "version", "name", "tags", + "formState", "accessControl", "iconId", "_links" +}) +public class FormInfo extends IdentifiableNameable { - private String stableId = null; - private Integer version = null; + private String stableId; + private Integer version; + private FormState formState; + private AccessControl accessControl; + private String tags; + private Long iconId; } diff --git a/src/main/java/com/researchspace/api/clientmodel/FormState.java b/src/main/java/com/researchspace/api/clientmodel/FormState.java new file mode 100644 index 0000000..33c61f5 --- /dev/null +++ b/src/main/java/com/researchspace/api/clientmodel/FormState.java @@ -0,0 +1,14 @@ +package com.researchspace.api.clientmodel; + +/** + * Enumeration representing the publishing state of a Form. + * + * @author rspace + */ +public enum FormState { + + NEW, + PUBLISHED, + UNPUBLISHED, + OLD +} \ No newline at end of file diff --git a/src/main/java/com/researchspace/api/clientmodel/NumberFormField.java b/src/main/java/com/researchspace/api/clientmodel/NumberFormField.java new file mode 100644 index 0000000..d88bb9d --- /dev/null +++ b/src/main/java/com/researchspace/api/clientmodel/NumberFormField.java @@ -0,0 +1,37 @@ +package com.researchspace.api.clientmodel; + +import com.fasterxml.jackson.annotation.JsonPropertyOrder; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import lombok.experimental.SuperBuilder; + +import javax.validation.constraints.Min; + +/** + * A Number form field - for numeric input with constraints. + * @author rspace + */ +@Data +@EqualsAndHashCode(callSuper = true) +@NoArgsConstructor +@SuperBuilder +@JsonPropertyOrder(value = {"id", "globalId", "name", "type", "lastModified", "index", + "min", "max", "decimalPlaces", "defaultValue"}) +public class NumberFormField extends FormField { + private Double min; + private Double max; + @Min(0) + private Byte decimalPlaces; + private Double defaultValue; + + public NumberFormField(String name, Double defaultValue, Double min, Double max, Byte decimalPlaces) { + super(); + setName(name); + setType("Number"); + this.defaultValue = defaultValue; + this.min = min; + this.max = max; + this.decimalPlaces = decimalPlaces; + } +} \ No newline at end of file diff --git a/src/main/java/com/researchspace/api/clientmodel/RadioFormField.java b/src/main/java/com/researchspace/api/clientmodel/RadioFormField.java new file mode 100644 index 0000000..764fa60 --- /dev/null +++ b/src/main/java/com/researchspace/api/clientmodel/RadioFormField.java @@ -0,0 +1,38 @@ +package com.researchspace.api.clientmodel; + +import com.fasterxml.jackson.annotation.JsonPropertyOrder; +import lombok.Builder; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import lombok.experimental.SuperBuilder; + +import javax.validation.constraints.Size; +import java.util.ArrayList; +import java.util.List; + +/** + * A Radio form field defines a list of radio buttons, one of which can be selected by default. + * @author rspace + */ +@Data +@EqualsAndHashCode(callSuper = true) +@NoArgsConstructor +@SuperBuilder +@JsonPropertyOrder(value = {"id", "globalId", "name", "type", "lastModified", "index", + "options", "defaultOption"}) +public class RadioFormField extends FormField { + + @Size(min = 1, message = "Please provide at least one option") + @Builder.Default + private List options = new ArrayList<>(); + private String defaultOption; + + public RadioFormField(String name, List options, String defaultOption) { + super(); + setName(name); + setType("Radio"); + this.options = options != null ? options : new ArrayList<>(); + this.defaultOption = defaultOption; + } +} \ No newline at end of file diff --git a/src/main/java/com/researchspace/api/clientmodel/StringFormField.java b/src/main/java/com/researchspace/api/clientmodel/StringFormField.java new file mode 100644 index 0000000..a66c5e2 --- /dev/null +++ b/src/main/java/com/researchspace/api/clientmodel/StringFormField.java @@ -0,0 +1,28 @@ +package com.researchspace.api.clientmodel; + +import com.fasterxml.jackson.annotation.JsonPropertyOrder; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import lombok.experimental.SuperBuilder; + +/** + * A String form field - for short text input (max 255 characters). + * @author rspace + */ +@Data +@EqualsAndHashCode(callSuper = true) +@NoArgsConstructor +@SuperBuilder +@JsonPropertyOrder(value = {"id", "globalId", "name", "type", "lastModified", "index", "defaultValue"}) +public class StringFormField extends FormField { + + private String defaultValue; + + public StringFormField(String name, String defaultValue) { + super(); + setName(name); + setType("String"); + this.defaultValue = defaultValue; + } +} \ No newline at end of file diff --git a/src/main/java/com/researchspace/api/clientmodel/TextFormField.java b/src/main/java/com/researchspace/api/clientmodel/TextFormField.java new file mode 100644 index 0000000..a9cdb61 --- /dev/null +++ b/src/main/java/com/researchspace/api/clientmodel/TextFormField.java @@ -0,0 +1,28 @@ +package com.researchspace.api.clientmodel; + +import com.fasterxml.jackson.annotation.JsonPropertyOrder; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import lombok.experimental.SuperBuilder; + +/** + * A Text form field - for longer text input that can include HTML. + * @author rspace + */ +@Data +@EqualsAndHashCode(callSuper = true) +@NoArgsConstructor +@SuperBuilder +@JsonPropertyOrder(value = {"id", "globalId", "name", "type", "lastModified", "index", "defaultValue"}) +public class TextFormField extends FormField { + + private String defaultValue; + + public TextFormField(String name, String defaultValue) { + super(); + setName(name); + setType("Text"); + this.defaultValue = defaultValue; + } +} \ No newline at end of file diff --git a/src/main/java/com/researchspace/api/clientmodel/TimeFormField.java b/src/main/java/com/researchspace/api/clientmodel/TimeFormField.java new file mode 100644 index 0000000..a88657c --- /dev/null +++ b/src/main/java/com/researchspace/api/clientmodel/TimeFormField.java @@ -0,0 +1,28 @@ +package com.researchspace.api.clientmodel; + +import com.fasterxml.jackson.annotation.JsonPropertyOrder; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import lombok.experimental.SuperBuilder; + +/** + * A Time form field - for time input values stored as milliseconds. + * @author rspace + */ +@Data +@EqualsAndHashCode(callSuper = true) +@NoArgsConstructor +@SuperBuilder +@JsonPropertyOrder(value = {"id", "globalId", "name", "type", "lastModified", "index", "defaultValue"}) +public class TimeFormField extends FormField { + + private Long defaultValue; + + public TimeFormField(String name, Long defaultValue) { + super(); + setName(name); + setType("Time"); + this.defaultValue = defaultValue; + } +} \ No newline at end of file diff --git a/src/test/java/com/researchspace/api/clientmodel/AccessControlTest.java b/src/test/java/com/researchspace/api/clientmodel/AccessControlTest.java new file mode 100644 index 0000000..8d979c7 --- /dev/null +++ b/src/test/java/com/researchspace/api/clientmodel/AccessControlTest.java @@ -0,0 +1,63 @@ +package com.researchspace.api.clientmodel; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +class AccessControlTest { + + private ObjectMapper objectMapper; + + @BeforeEach + void setUp() { + objectMapper = new ObjectMapper(); + objectMapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES); + } + + @Test + void testAccessControlBuilder() { + AccessControl accessControl = AccessControl.builder() + .ownerPermissionType("WRITE") + .groupPermissionType("READ") + .worldPermissionType("NONE") + .build(); + + assertNotNull(accessControl); + assertEquals("WRITE", accessControl.getOwnerPermissionType()); + assertEquals("READ", accessControl.getGroupPermissionType()); + assertEquals("NONE", accessControl.getWorldPermissionType()); + } + + @Test + void testAccessControlConstructors() { + + AccessControl ac1 = new AccessControl(); + assertNotNull(ac1); + assertNull(ac1.getOwnerPermissionType()); + + AccessControl ac2 = new AccessControl("WRITE", "READ", "NONE"); + assertEquals("WRITE", ac2.getOwnerPermissionType()); + assertEquals("READ", ac2.getGroupPermissionType()); + assertEquals("NONE", ac2.getWorldPermissionType()); + } + + @Test + void testAccessControlSerialization() throws JsonProcessingException { + AccessControl accessControl = new AccessControl("WRITE", "READ", "NONE"); + + String json = objectMapper.writeValueAsString(accessControl); + assertNotNull(json); + assertTrue(json.contains("\"ownerPermissionType\":\"WRITE\"")); + assertTrue(json.contains("\"groupPermissionType\":\"READ\"")); + assertTrue(json.contains("\"worldPermissionType\":\"NONE\"")); + + AccessControl deserialized = objectMapper.readValue(json, AccessControl.class); + assertEquals("WRITE", deserialized.getOwnerPermissionType()); + assertEquals("READ", deserialized.getGroupPermissionType()); + assertEquals("NONE", deserialized.getWorldPermissionType()); + } +} \ No newline at end of file diff --git a/src/test/java/com/researchspace/api/clientmodel/FormFieldTest.java b/src/test/java/com/researchspace/api/clientmodel/FormFieldTest.java new file mode 100644 index 0000000..0e974f8 --- /dev/null +++ b/src/test/java/com/researchspace/api/clientmodel/FormFieldTest.java @@ -0,0 +1,135 @@ +package com.researchspace.api.clientmodel; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.SerializationFeature; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.util.Arrays; +import java.util.Date; + +import static org.junit.jupiter.api.Assertions.*; + +class FormFieldTest { + + private ObjectMapper objectMapper; + + @BeforeEach + void setUp() { + objectMapper = new ObjectMapper(); + objectMapper.enable(DeserializationFeature.READ_ENUMS_USING_TO_STRING); + objectMapper.enable(SerializationFeature.WRITE_ENUMS_USING_TO_STRING); + objectMapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES); + } + + @Test + void testStringFormFieldSerialization() throws JsonProcessingException { + StringFormField field = new StringFormField("Test String Field", "test default"); + field.setId(1L); + field.setIndex(0); + + String json = objectMapper.writeValueAsString(field); + assertNotNull(json); + assertTrue(json.contains("\"type\":\"String\"")); + assertTrue(json.contains("\"defaultValue\":\"test default\"")); + + FormField deserialized = objectMapper.readValue(json, FormField.class); + assertInstanceOf(StringFormField.class, deserialized); + StringFormField stringField = (StringFormField) deserialized; + assertEquals("Test String Field", stringField.getName()); + assertEquals("test default", stringField.getDefaultValue()); + } + + @Test + void testNumberFormFieldSerialization() throws JsonProcessingException { + NumberFormField field = new NumberFormField("Test Number Field", 50.5, 0.0, 100.0, (byte) 2); + field.setId(2L); + field.setIndex(1); + + String json = objectMapper.writeValueAsString(field); + assertNotNull(json); + assertTrue(json.contains("\"type\":\"Number\"")); + assertTrue(json.contains("\"min\":0.0")); + assertTrue(json.contains("\"max\":100.0")); + + FormField deserialized = objectMapper.readValue(json, FormField.class); + assertInstanceOf(NumberFormField.class, deserialized); + NumberFormField numberField = (NumberFormField) deserialized; + assertEquals("Test Number Field", numberField.getName()); + assertEquals(Double.valueOf(50.5), numberField.getDefaultValue()); + assertEquals(Double.valueOf(0.0), numberField.getMin()); + assertEquals(Double.valueOf(100.0), numberField.getMax()); + } + + @Test + void testChoiceFormFieldSerialization() throws JsonProcessingException { + ChoiceFormField field = new ChoiceFormField("Test Choice Field", true, + Arrays.asList("Option A", "Option B", "Option C"), + Arrays.asList("Option A", "Option B")); + field.setId(3L); + field.setIndex(2); + + String json = objectMapper.writeValueAsString(field); + assertNotNull(json); + assertTrue(json.contains("\"type\":\"Choice\"")); + assertTrue(json.contains("\"multipleChoice\":true")); + + FormField deserialized = objectMapper.readValue(json, FormField.class); + assertInstanceOf(ChoiceFormField.class, deserialized); + ChoiceFormField choiceField = (ChoiceFormField) deserialized; + assertEquals("Test Choice Field", choiceField.getName()); + assertTrue(choiceField.isMultipleChoice()); + assertEquals(3, choiceField.getOptions().size()); + assertEquals(2, choiceField.getDefaultOptions().size()); + } + + @Test + void testDateFormFieldSerialization() throws JsonProcessingException { + Date defaultDate = new Date(1640995200000L); // 2022-01-01 + Date minDate = new Date(1609459200000L); // 2021-01-01 + Date maxDate = new Date(1672531200000L); // 2023-01-01 + + DateFormField field = new DateFormField("Test Date Field", defaultDate, minDate, maxDate); + field.setId(4L); + field.setIndex(3); + + String json = objectMapper.writeValueAsString(field); + assertNotNull(json); + assertTrue(json.contains("\"type\":\"Date\"")); + + FormField deserialized = objectMapper.readValue(json, FormField.class); + assertInstanceOf(DateFormField.class, deserialized); + DateFormField dateField = (DateFormField) deserialized; + assertEquals("Test Date Field", dateField.getName()); + assertNotNull(dateField.getDefaultValue()); + assertNotNull(dateField.getMin()); + assertNotNull(dateField.getMax()); + } + + @Test + void testTimeFormFieldSerialization() throws JsonProcessingException { + long defaultTime = 3600000L; + TimeFormField field = new TimeFormField("Test Time Field", defaultTime); + field.setId(7L); + field.setIndex(6); + String json = objectMapper.writeValueAsString(field); + assertNotNull(json); + assertTrue(json.contains("\"type\":\"Time\"")); + assertTrue(json.contains("\"defaultValue\":" + defaultTime)); + FormField deserialized = objectMapper.readValue(json, FormField.class); + assertInstanceOf(TimeFormField.class, deserialized); + TimeFormField timeField = (TimeFormField) deserialized; + assertEquals("Test Time Field", timeField.getName()); + assertEquals(defaultTime, timeField.getDefaultValue()); + } + + @Test + void testConstructors() { + StringFormField stringField = new StringFormField("String Test", "default"); + assertEquals("String Test", stringField.getName()); + assertEquals("String", stringField.getType()); + assertEquals("default", stringField.getDefaultValue()); + } +} \ No newline at end of file diff --git a/src/test/java/com/researchspace/api/clientmodel/FormInfoTest.java b/src/test/java/com/researchspace/api/clientmodel/FormInfoTest.java new file mode 100644 index 0000000..6ed0b50 --- /dev/null +++ b/src/test/java/com/researchspace/api/clientmodel/FormInfoTest.java @@ -0,0 +1,83 @@ +package com.researchspace.api.clientmodel; + +import org.junit.jupiter.api.Test; + +import java.io.File; +import java.io.IOException; + +import static org.junit.jupiter.api.Assertions.*; + +class FormInfoTest extends AbstractModelTest { + + @Test + void testFormInfoFromFormSearchResult() throws IOException { + + File formSearchResultJson = new File("src/test/resources/FormSearchResult.json"); + FormSearchResult formSearchResult = readFileToClass(formSearchResultJson, FormSearchResult.class); + + assertNotNull(formSearchResult.getForms()); + assertFalse(formSearchResult.getForms().isEmpty()); + + FormInfo firstForm = formSearchResult.getForms().get(0); + assertNotNull(firstForm); + assertEquals(Long.valueOf(32785), firstForm.getId()); + assertEquals("FM32785", firstForm.getGlobalId()); + assertEquals("formName", firstForm.getName()); + assertEquals("a,b,c", firstForm.getTags()); + assertEquals(FormState.PUBLISHED, firstForm.getFormState()); + assertNotNull(firstForm.getAccessControl()); + assertEquals("WRITE", firstForm.getAccessControl().getOwnerPermissionType()); + assertEquals("NONE", firstForm.getAccessControl().getGroupPermissionType()); + assertEquals("NONE", firstForm.getAccessControl().getWorldPermissionType()); + assertEquals(Long.valueOf(-1), firstForm.getIconId()); + } + + @Test + void testFormInfoBuilder() { + AccessControl accessControl = AccessControl.builder() + .ownerPermissionType("WRITE") + .groupPermissionType("READ") + .worldPermissionType("NONE") + .build(); + + FormInfo formInfo = FormInfo.builder() + .id(123L) + .globalId("FM123") + .name("Test Form") + .stableId("stable123") + .version(1) + .tags("tag1,tag2") + .formState(FormState.NEW) + .accessControl(accessControl) + .iconId(456L) + .build(); + + assertNotNull(formInfo); + assertEquals(Long.valueOf(123), formInfo.getId()); + assertEquals("FM123", formInfo.getGlobalId()); + assertEquals("Test Form", formInfo.getName()); + assertEquals("stable123", formInfo.getStableId()); + assertEquals(Integer.valueOf(1), formInfo.getVersion()); + assertEquals("tag1,tag2", formInfo.getTags()); + assertEquals(FormState.NEW, formInfo.getFormState()); + assertEquals(accessControl, formInfo.getAccessControl()); + assertEquals(Long.valueOf(456), formInfo.getIconId()); + } + + @Test + void testFormInfoConstructors() { + + FormInfo formInfo1 = new FormInfo(); + assertNotNull(formInfo1); + assertNull(formInfo1.getId()); + + AccessControl ac = new AccessControl("WRITE", "READ", "NONE"); + FormInfo formInfo2 = new FormInfo("stable", 1, FormState.PUBLISHED, ac, "tags", 123L); + assertEquals("stable", formInfo2.getStableId()); + assertEquals(Integer.valueOf(1), formInfo2.getVersion()); + assertEquals(FormState.PUBLISHED, formInfo2.getFormState()); + assertEquals(ac, formInfo2.getAccessControl()); + assertEquals("tags", formInfo2.getTags()); + assertEquals(Long.valueOf(123), formInfo2.getIconId()); + } +} \ No newline at end of file diff --git a/src/test/java/com/researchspace/api/clientmodel/FormTest.java b/src/test/java/com/researchspace/api/clientmodel/FormTest.java new file mode 100644 index 0000000..f4db6ea --- /dev/null +++ b/src/test/java/com/researchspace/api/clientmodel/FormTest.java @@ -0,0 +1,85 @@ +package com.researchspace.api.clientmodel; + +import org.junit.jupiter.api.Test; + +import java.io.File; +import java.io.IOException; + +import static org.junit.jupiter.api.Assertions.*; + +class FormTest extends AbstractModelTest { + + File formJson = new File("src/test/resources/Form.json"); + + @Test + void testFormDeserialization() throws IOException { + Form form = readFileToClass(formJson, Form.class); + + assertNotNull(form); + assertEquals(Long.valueOf(123), form.getId()); + assertEquals("FM123", form.getGlobalId()); + assertEquals("Sample Form", form.getName()); + assertEquals("tag1,tag2", form.getTags()); + assertEquals(FormState.PUBLISHED, form.getFormState()); + assertNotNull(form.getAccessControl()); + assertEquals("WRITE", form.getAccessControl().getOwnerPermissionType()); + + assertNotNull(form.getFields()); + assertEquals(3, form.getFields().size()); + + assertInstanceOf(StringFormField.class, form.getFields().get(0)); + assertInstanceOf(NumberFormField.class, form.getFields().get(1)); + assertInstanceOf(ChoiceFormField.class, form.getFields().get(2)); + + StringFormField stringField = (StringFormField) form.getFields().get(0); + assertEquals("Sample Text Field", stringField.getName()); + assertEquals("String", stringField.getType()); + assertEquals("default value", stringField.getDefaultValue()); + + NumberFormField numberField = (NumberFormField) form.getFields().get(1); + assertEquals("Sample Number Field", numberField.getName()); + assertEquals("Number", numberField.getType()); + assertEquals(Double.valueOf(10.5), numberField.getDefaultValue()); + assertEquals(Double.valueOf(0.0), numberField.getMin()); + assertEquals(Double.valueOf(100.0), numberField.getMax()); + + ChoiceFormField choiceField = (ChoiceFormField) form.getFields().get(2); + assertEquals("Sample Choice Field", choiceField.getName()); + assertEquals("Choice", choiceField.getType()); + assertEquals(3, choiceField.getOptions().size()); + assertEquals(2, choiceField.getDefaultOptions().size()); + } + + @Test + void testFormBuilder() { + Form form = Form.builder() + .id(456L) + .name("Test Form") + .tags("test,builder") + .formState(FormState.NEW) + .build(); + + assertNotNull(form); + assertEquals(Long.valueOf(456), form.getId()); + assertEquals("Test Form", form.getName()); + assertEquals("test,builder", form.getTags()); + assertEquals(FormState.NEW, form.getFormState()); + assertNotNull(form.getFields()); // Should be initialized as empty list + assertTrue(form.getFields().isEmpty()); + } + + @Test + void testAddField() { + Form form = Form.builder().build(); + StringFormField field = new StringFormField("test", "default"); + + form.addField(field); + + assertEquals(1, form.getFields().size()); + assertEquals(field, form.getFields().get(0)); + + Form result = form.addField(new TextFormField("text", "text default")); + assertSame(form, result); + assertEquals(2, form.getFields().size()); + } +} \ No newline at end of file diff --git a/src/test/resources/Form.json b/src/test/resources/Form.json new file mode 100644 index 0000000..72420ac --- /dev/null +++ b/src/test/resources/Form.json @@ -0,0 +1,55 @@ +{ + "id": 123, + "globalId": "FM123", + "stableId": "stable123", + "version": 1, + "name": "Sample Form", + "tags": "tag1,tag2", + "formState": "PUBLISHED", + "accessControl": { + "ownerPermissionType": "WRITE", + "groupPermissionType": "READ", + "worldPermissionType": "NONE" + }, + "iconId": 456, + "fields": [ + { + "id": 1, + "globalId": "FF1", + "name": "Sample Text Field", + "type": "String", + "lastModified": "2022-01-01T00:00:00Z", + "index": 0, + "defaultValue": "default value" + }, + { + "id": 2, + "globalId": "FF2", + "name": "Sample Number Field", + "type": "Number", + "lastModified": "2022-01-01T00:00:00Z", + "index": 1, + "min": 0.0, + "max": 100.0, + "decimalPlaces": 1, + "defaultValue": 10.5 + }, + { + "id": 3, + "globalId": "FF3", + "name": "Sample Choice Field", + "type": "Choice", + "lastModified": "2022-01-01T00:00:00Z", + "index": 2, + "multipleChoice": true, + "options": ["Option 1", "Option 2", "Option 3"], + "defaultOptions": ["Option 1", "Option 2"] + } + ], + "_links": [ + { + "link": "http://localhost:8080/api/v1/forms/123", + "rel": "self" + } + ] +} \ No newline at end of file