package au.com.dius.pact.consumer.dsl;
import au.com.dius.pact.consumer.InvalidMatcherException;
import com.mifmif.common.regex.Generex;
import org.apache.commons.lang3.RandomStringUtils;
import org.apache.commons.lang3.time.DateFormatUtils;
import org.apache.commons.lang3.time.FastDateFormat;
import org.json.JSONArray;
import org.json.JSONObject;
import java.math.BigDecimal;
import java.util.Date;
import java.util.UUID;
/**
* DSL to define a JSON array
*/
public class PactDslJsonArray extends DslPart {
private static final String EXAMPLE = "Example \"";
private final JSONArray body;
private boolean wildCard;
private int numberExamples = 1;
public PactDslJsonArray() {
this("", "", null, false);
}
public PactDslJsonArray(String rootPath, String rootName, DslPart parent) {
this(rootPath, rootName, parent, false);
}
public PactDslJsonArray(String rootPath, String rootName, DslPart parent, boolean wildCard) {
super(parent, rootPath, rootName);
this.wildCard = wildCard;
body = new JSONArray();
}
/**
* Closes the current array
*/
public DslPart closeArray() {
if (parent != null) {
parent.putArray(this);
}
closed = true;
return parent;
}
@Override
@Deprecated
public PactDslJsonBody arrayLike(String name) {
throw new UnsupportedOperationException("use the eachLike() form");
}
/**
* Element that is an array where each item must match the following example
* @deprecated use eachLike
*/
@Override
@Deprecated
public PactDslJsonBody arrayLike() {
return eachLike();
}
@Override
public PactDslJsonBody eachLike(String name) {
throw new UnsupportedOperationException("use the eachLike() form");
}
@Override
public PactDslJsonBody eachLike(String name, int numberExamples) {
throw new UnsupportedOperationException("use the eachLike(numberExamples) form");
}
/**
* Element that is an array where each item must match the following example
*/
@Override
public PactDslJsonBody eachLike() {
return eachLike(1);
}
/**
* Element that is an array where each item must match the following example
* @param numberExamples Number of examples to generate
*/
@Override
public PactDslJsonBody eachLike(int numberExamples) {
matchers.put(rootPath + appendArrayIndex(1), matchMin(0));
PactDslJsonArray parent = new PactDslJsonArray(rootPath, rootName, this, true);
parent.setNumberExamples(numberExamples);
return new PactDslJsonBody(".", "", parent);
}
@Override
public PactDslJsonBody minArrayLike(String name, Integer size) {
throw new UnsupportedOperationException("use the minArrayLike(Integer size) form");
}
/**
* Element that is an array with a minimum size where each item must match the following example
* @param size minimum size of the array
*/
@Override
public PactDslJsonBody minArrayLike(Integer size) {
return minArrayLike(size, size);
}
@Override
public PactDslJsonBody minArrayLike(String name, Integer size, int numberExamples) {
throw new UnsupportedOperationException("use the minArrayLike(Integer size, int numberExamples) form");
}
/**
* Element that is an array with a minimum size where each item must match the following example
* @param size minimum size of the array
* @param numberExamples number of examples to generate
*/
@Override
public PactDslJsonBody minArrayLike(Integer size, int numberExamples) {
if (numberExamples < size) {
throw new IllegalArgumentException(String.format("Number of example %d is less than the minimum size of %d",
numberExamples, size));
}
matchers.put(rootPath + appendArrayIndex(1), matchMin(size));
PactDslJsonArray parent = new PactDslJsonArray("", "", this, true);
parent.setNumberExamples(numberExamples);
return new PactDslJsonBody(".", "", parent);
}
@Override
public PactDslJsonBody maxArrayLike(String name, Integer size) {
throw new UnsupportedOperationException("use the maxArrayLike(Integer size) form");
}
/**
* Element that is an array with a maximum size where each item must match the following example
* @param size maximum size of the array
*/
@Override
public PactDslJsonBody maxArrayLike(Integer size) {
return maxArrayLike(size, 1);
}
@Override
public PactDslJsonBody maxArrayLike(String name, Integer size, int numberExamples) {
throw new UnsupportedOperationException("use the maxArrayLike(Integer size, int numberExamples) form");
}
/**
* Element that is an array with a maximum size where each item must match the following example
* @param size maximum size of the array
* @param numberExamples number of examples to generate
*/
@Override
public PactDslJsonBody maxArrayLike(Integer size, int numberExamples) {
if (numberExamples > size) {
throw new IllegalArgumentException(String.format("Number of example %d is more than the maximum size of %d",
numberExamples, size));
}
matchers.put(rootPath + appendArrayIndex(1), matchMax(size));
PactDslJsonArray parent = new PactDslJsonArray("", "", this, true);
parent.setNumberExamples(numberExamples);
return new PactDslJsonBody(".", "", parent);
}
protected void putObject(DslPart object) {
for(String matcherName: object.matchers.keySet()) {
matchers.put(rootPath + appendArrayIndex(1) + matcherName, object.matchers.get(matcherName));
}
for (int i = 0; i < getNumberExamples(); i++) {
body.put(object.getBody());
}
}
protected void putArray(DslPart object) {
for(String matcherName: object.matchers.keySet()) {
matchers.put(rootPath + appendArrayIndex(1) + matcherName, object.matchers.get(matcherName));
}
body.put(object.getBody());
}
@Override
public Object getBody() {
return body;
}
/**
* Element that must be the specified value
* @param value string value
*/
public PactDslJsonArray stringValue(String value) {
if (value == null) {
body.put(JSONObject.NULL);
} else {
body.put(value);
}
return this;
}
/**
* Element that must be the specified value
* @param value string value
*/
public PactDslJsonArray string(String value) {
return stringValue(value);
}
public PactDslJsonArray numberValue(Number value) {
body.put(value);
return this;
}
/**
* Element that must be the specified value
* @param value number value
*/
public PactDslJsonArray number(Number value) {
return numberValue(value);
}
/**
* Element that must be the specified value
* @param value boolean value
*/
public PactDslJsonArray booleanValue(Boolean value) {
body.put(value);
return this;
}
/**
* Element that can be any string
*/
public PactDslJsonArray stringType() {
body.put(RandomStringUtils.randomAlphabetic(20));
matchers.put(rootPath + appendArrayIndex(0), matchType());
return this;
}
/**
* Element that can be any string
* @param example example value to use for generated bodies
*/
public PactDslJsonArray stringType(String example) {
body.put(example);
matchers.put(rootPath + appendArrayIndex(0), matchType());
return this;
}
/**
* Element that can be any number
*/
public PactDslJsonArray numberType() {
return numberType(Long.parseLong(RandomStringUtils.randomNumeric(9)));
}
/**
* Element that can be any number
* @param number example number to use for generated bodies
*/
public PactDslJsonArray numberType(Number number) {
body.put(number);
matchers.put(rootPath + appendArrayIndex(0), matchType("type"));
return this;
}
/**
* Element that must be an integer
*/
public PactDslJsonArray integerType() {
return integerType(Long.parseLong(RandomStringUtils.randomNumeric(9)));
}
/**
* Element that must be an integer
* @param number example integer value to use for generated bodies
*/
public PactDslJsonArray integerType(Long number) {
body.put(number);
matchers.put(rootPath + appendArrayIndex(0), matchType("integer"));
return this;
}
/**
* Element that must be a real value
* @deprecated Use decimalType instead
*/
@Deprecated
public PactDslJsonArray realType() {
return decimalType();
}
/**
* Element that must be a real value
* @param number example real value
* @deprecated Use decimalType instead
*/
@Deprecated
public PactDslJsonArray realType(Double number) {
return decimalType(number);
}
/**
* Element that must be a decimal value
*/
public PactDslJsonArray decimalType() {
return decimalType(new BigDecimal(RandomStringUtils.randomNumeric(10)));
}
/**
* Element that must be a decimalType value
* @param number example decimalType value
*/
public PactDslJsonArray decimalType(BigDecimal number) {
body.put(number);
matchers.put(rootPath + appendArrayIndex(0), matchType("decimal"));
return this;
}
/**
* Attribute that must be a decimalType value
* @param number example decimalType value
*/
public PactDslJsonArray decimalType(Double number) {
body.put(number);
matchers.put(rootPath + appendArrayIndex(0), matchType("decimal"));
return this;
}
/**
* Element that must be a boolean
*/
public PactDslJsonArray booleanType() {
body.put(true);
matchers.put(rootPath + appendArrayIndex(0), matchType());
return this;
}
/**
* Element that must be a boolean
* @param example example boolean to use for generated bodies
*/
public PactDslJsonArray booleanType(Boolean example) {
body.put(example);
matchers.put(rootPath + appendArrayIndex(0), matchType());
return this;
}
/**
* Element that must match the regular expression
* @param regex regular expression
* @param value example value to use for generated bodies
*/
public PactDslJsonArray stringMatcher(String regex, String value) {
if (!value.matches(regex)) {
throw new InvalidMatcherException(EXAMPLE + value + "\" does not match regular expression \"" +
regex + "\"");
}
body.put(value);
matchers.put(rootPath + appendArrayIndex(0), regexp(regex));
return this;
}
/**
* Element that must match the regular expression
* @param regex regular expression
*/
public PactDslJsonArray stringMatcher(String regex) {
stringMatcher(regex, new Generex(regex).random());
return this;
}
/**
* Element that must be an ISO formatted timestamp
*/
public PactDslJsonArray timestamp() {
body.put(DateFormatUtils.ISO_DATETIME_FORMAT.format(new Date()));
matchers.put(rootPath + appendArrayIndex(0), matchTimestamp(DateFormatUtils.ISO_DATETIME_FORMAT.getPattern()));
return this;
}
/**
* Element that must match the given timestamp format
* @param format timestamp format
*/
public PactDslJsonArray timestamp(String format) {
FastDateFormat instance = FastDateFormat.getInstance(format);
body.put(instance.format(new Date()));
matchers.put(rootPath + appendArrayIndex(0), matchTimestamp(format));
return this;
}
/**
* Element that must match the given timestamp format
* @param format timestamp format
* @param example example date and time to use for generated bodies
*/
public PactDslJsonArray timestamp(String format, Date example) {
FastDateFormat instance = FastDateFormat.getInstance(format);
body.put(instance.format(example));
matchers.put(rootPath + appendArrayIndex(0), matchTimestamp(format));
return this;
}
/**
* Element that must be formatted as an ISO date
*/
public PactDslJsonArray date() {
body.put(DateFormatUtils.ISO_DATE_FORMAT.format(new Date()));
matchers.put(rootPath + appendArrayIndex(0), matchDate(DateFormatUtils.ISO_DATE_FORMAT.getPattern()));
return this;
}
/**
* Element that must match the provided date format
* @param format date format to match
*/
public PactDslJsonArray date(String format) {
FastDateFormat instance = FastDateFormat.getInstance(format);
body.put(instance.format(new Date()));
matchers.put(rootPath + appendArrayIndex(0), matchDate(format));
return this;
}
/**
* Element that must match the provided date format
* @param format date format to match
* @param example example date to use for generated values
*/
public PactDslJsonArray date(String format, Date example) {
FastDateFormat instance = FastDateFormat.getInstance(format);
body.put(instance.format(example));
matchers.put(rootPath + appendArrayIndex(0), matchDate(format));
return this;
}
/**
* Element that must be an ISO formatted time
*/
public PactDslJsonArray time() {
body.put(DateFormatUtils.ISO_TIME_FORMAT.format(new Date()));
matchers.put(rootPath + appendArrayIndex(0), matchTime(DateFormatUtils.ISO_TIME_FORMAT.getPattern()));
return this;
}
/**
* Element that must match the given time format
* @param format time format to match
*/
public PactDslJsonArray time(String format) {
FastDateFormat instance = FastDateFormat.getInstance(format);
body.put(instance.format(new Date()));
matchers.put(rootPath + appendArrayIndex(0), matchTime(format));
return this;
}
/**
* Element that must match the given time format
* @param format time format to match
* @param example example time to use for generated bodies
*/
public PactDslJsonArray time(String format, Date example) {
FastDateFormat instance = FastDateFormat.getInstance(format);
body.put(instance.format(example));
matchers.put(rootPath + appendArrayIndex(0), matchTime(format));
return this;
}
/**
* Element that must be an IP4 address
*/
public PactDslJsonArray ipAddress() {
body.put("127.0.0.1");
matchers.put(rootPath + appendArrayIndex(0), regexp("(\\d{1,3}\\.)+\\d{1,3}"));
return this;
}
public PactDslJsonBody object(String name) {
throw new UnsupportedOperationException("use the object() form");
}
/**
* Element that is a JSON object
*/
public PactDslJsonBody object() {
return new PactDslJsonBody(".", "", this);
}
@Override
public DslPart closeObject() {
throw new UnsupportedOperationException("can't call closeObject on an Array");
}
@Override
public DslPart close() {
DslPart parentToReturn = this;
if (!closed) {
DslPart parent = closeArray();
while (parent != null) {
parentToReturn = parent;
if (parent instanceof PactDslJsonArray) {
parent = parent.closeArray();
} else {
parent = parent.closeObject();
}
}
}
return parentToReturn;
}
public PactDslJsonArray array(String name) {
throw new UnsupportedOperationException("use the array() form");
}
/**
* Element that is a JSON array
*/
public PactDslJsonArray array() {
return new PactDslJsonArray("", "", this);
}
/**
* Element that must be a numeric identifier
*/
public PactDslJsonArray id() {
body.put(Long.parseLong(RandomStringUtils.randomNumeric(10)));
matchers.put(rootPath + appendArrayIndex(0), matchType());
return this;
}
/**
* Element that must be a numeric identifier
* @param id example id to use for generated bodies
*/
public PactDslJsonArray id(Long id) {
body.put(id);
matchers.put(rootPath + appendArrayIndex(0), matchType());
return this;
}
/**
* Element that must be encoded as a hexadecimal value
*/
public PactDslJsonArray hexValue() {
return hexValue(RandomStringUtils.random(10, "0123456789abcdef"));
}
/**
* Element that must be encoded as a hexadecimal value
* @param hexValue example value to use for generated bodies
*/
public PactDslJsonArray hexValue(String hexValue) {
if (!hexValue.matches(HEXADECIMAL)) {
throw new InvalidMatcherException(EXAMPLE + hexValue + "\" is not a hexadecimal value");
}
body.put(hexValue);
matchers.put(rootPath + appendArrayIndex(0), regexp("[0-9a-fA-F]+"));
return this;
}
/**
* Element that must be encoded as a GUID
* @deprecated use uuid instead
*/
@Deprecated
public PactDslJsonArray guid() {
return uuid();
}
/**
* Element that must be encoded as a GUID
* @param uuid example UUID to use for generated bodies
* @deprecated use uuid instead
*/
@Deprecated
public PactDslJsonArray guid(String uuid) {
return uuid(uuid);
}
/**
* Element that must be encoded as an UUID
*/
public PactDslJsonArray uuid() {
return uuid(UUID.randomUUID().toString());
}
/**
* Element that must be encoded as an UUID
* @param uuid example UUID to use for generated bodies
*/
public PactDslJsonArray uuid(String uuid) {
if (!uuid.matches(UUID_REGEX)) {
throw new InvalidMatcherException(EXAMPLE + uuid + "\" is not an UUID");
}
body.put(uuid);
matchers.put(rootPath + appendArrayIndex(0), regexp(UUID_REGEX));
return this;
}
/**
* Adds the template object to the array
* @param template template object
*/
public PactDslJsonArray template(DslPart template) {
putObject(template);
return this;
}
/**
* Adds a number of template objects to the array
* @param template template object
* @param occurrences number to add
*/
public PactDslJsonArray template(DslPart template, int occurrences) {
for(int i = 0; i < occurrences; i++) {
template(template);
}
return this;
}
@Override
public String toString() {
return body.toString();
}
private String appendArrayIndex(Integer offset) {
String index = "*";
if (!wildCard) {
index = String.valueOf(body.length() - 1 + offset);
}
return "[" + index + "]";
}
/**
* Array where each item must match the following example
*/
public static PactDslJsonBody arrayEachLike() {
return arrayEachLike(1);
}
/**
* Array where each item must match the following example
* @param numberExamples Number of examples to generate
*/
public static PactDslJsonBody arrayEachLike(Integer numberExamples) {
PactDslJsonArray parent = new PactDslJsonArray("", "", null, true);
parent.setNumberExamples(numberExamples);
parent.matchers.put("", parent.matchMin(0));
return new PactDslJsonBody(".", "", parent);
}
/**
* Array with a minimum size where each item must match the following example
* @param minSize minimum size
*/
public static PactDslJsonBody arrayMinLike(int minSize) {
return arrayMinLike(minSize, minSize);
}
/**
* Array with a minimum size where each item must match the following example
* @param minSize minimum size
* @param numberExamples Number of examples to generate
*/
public static PactDslJsonBody arrayMinLike(int minSize, int numberExamples) {
if (numberExamples < minSize) {
throw new IllegalArgumentException(String.format("Number of example %d is less than the minimum size of %d",
numberExamples, minSize));
}
PactDslJsonArray parent = new PactDslJsonArray("", "", null, true);
parent.setNumberExamples(numberExamples);
parent.matchers.put("", parent.matchMin(minSize));
return new PactDslJsonBody(".", "", parent);
}
/**
* Array with a maximum size where each item must match the following example
* @param maxSize maximum size
*/
public static PactDslJsonBody arrayMaxLike(int maxSize) {
return arrayMaxLike(maxSize, 1);
}
/**
* Array with a maximum size where each item must match the following example
* @param maxSize maximum size
* @param numberExamples Number of examples to generate
*/
public static PactDslJsonBody arrayMaxLike(int maxSize, int numberExamples) {
if (numberExamples > maxSize) {
throw new IllegalArgumentException(String.format("Number of example %d is more than the maximum size of %d",
numberExamples, maxSize));
}
PactDslJsonArray parent = new PactDslJsonArray("", "", null, true);
parent.setNumberExamples(numberExamples);
parent.matchers.put("", parent.matchMax(maxSize));
return new PactDslJsonBody(".", "", parent);
}
/**
* Adds a null value to the list
*/
public PactDslJsonArray nullValue() {
body.put(JSONObject.NULL);
return this;
}
/**
* Returns the number of example elements to generate for sample bodies
*/
public int getNumberExamples() {
return numberExamples;
}
/**
* Sets the number of example elements to generate for sample bodies
*/
public void setNumberExamples(int numberExamples) {
this.numberExamples = numberExamples;
}
@Override
public PactDslJsonArray eachArrayLike(String name) {
throw new UnsupportedOperationException("use the eachArrayLike() form");
}
@Override
public PactDslJsonArray eachArrayLike(String name, int numberExamples) {
throw new UnsupportedOperationException("use the eachArrayLike(numberExamples) form");
}
@Override
public PactDslJsonArray eachArrayLike() {
return eachArrayLike(1);
}
@Override
public PactDslJsonArray eachArrayLike(int numberExamples) {
matchers.put(rootPath + appendArrayIndex(1), matchMin(0));
PactDslJsonArray parent = new PactDslJsonArray(rootPath, "", this, true);
parent.setNumberExamples(numberExamples);
return new PactDslJsonArray("", "", parent);
}
@Override
public PactDslJsonArray eachArrayWithMaxLike(String name, Integer size) {
throw new UnsupportedOperationException("use the eachArrayWithMaxLike() form");
}
@Override
public PactDslJsonArray eachArrayWithMaxLike(String name, int numberExamples, Integer size) {
throw new UnsupportedOperationException("use the eachArrayWithMaxLike(numberExamples) form");
}
@Override
public PactDslJsonArray eachArrayWithMaxLike(Integer size) {
return eachArrayWithMaxLike(1, size);
}
@Override
public PactDslJsonArray eachArrayWithMaxLike(int numberExamples, Integer size) {
if (numberExamples > size) {
throw new IllegalArgumentException(String.format("Number of example %d is more than the maximum size of %d",
numberExamples, size));
}
matchers.put(rootPath + appendArrayIndex(1), matchMax(size));
PactDslJsonArray parent = new PactDslJsonArray(rootPath, "", this, true);
parent.setNumberExamples(numberExamples);
return new PactDslJsonArray("", "", parent);
}
@Override
public PactDslJsonArray eachArrayWithMinLike(String name, Integer size) {
throw new UnsupportedOperationException("use the eachArrayWithMinLike() form");
}
@Override
public PactDslJsonArray eachArrayWithMinLike(String name, int numberExamples, Integer size) {
throw new UnsupportedOperationException("use the eachArrayWithMinLike(numberExamples) form");
}
@Override
public PactDslJsonArray eachArrayWithMinLike(Integer size) {
return eachArrayWithMinLike(size, size);
}
@Override
public PactDslJsonArray eachArrayWithMinLike(int numberExamples, Integer size) {
if (numberExamples < size) {
throw new IllegalArgumentException(String.format("Number of example %d is less than the minimum size of %d",
numberExamples, size));
}
matchers.put(rootPath + appendArrayIndex(1), matchMin(size));
PactDslJsonArray parent = new PactDslJsonArray(rootPath, "", this, true);
parent.setNumberExamples(numberExamples);
return new PactDslJsonArray("", "", parent);
}
/**
* Array of values that are not objects where each item must match the provided example
* @param value Value to use to match each item
*/
public PactDslJsonArray eachLike(PactDslJsonRootValue value) {
return eachLike(value, 1);
}
/**
* Array of values that are not objects where each item must match the provided example
* @param value Value to use to match each item
* @param numberExamples number of examples to generate
*/
public PactDslJsonArray eachLike(PactDslJsonRootValue value, int numberExamples) {
matchers.put(rootPath + appendArrayIndex(1), matchMin(0));
PactDslJsonArray parent = new PactDslJsonArray(rootPath, "", this, true);
parent.setNumberExamples(numberExamples);
parent.putObject(value);
return (PactDslJsonArray) parent.closeArray();
}
/**
* Array of values with a minimum size that are not objects where each item must match the provided example
* @param size minimum size of the array
* @param value Value to use to match each item
*/
public PactDslJsonArray minArrayLike(Integer size, PactDslJsonRootValue value) {
return minArrayLike(size, value, size);
}
/**
* Array of values with a minimum size that are not objects where each item must match the provided example
* @param size minimum size of the array
* @param value Value to use to match each item
* @param numberExamples number of examples to generate
*/
public PactDslJsonArray minArrayLike(Integer size, PactDslJsonRootValue value, int numberExamples) {
if (numberExamples < size) {
throw new IllegalArgumentException(String.format("Number of example %d is less than the minimum size of %d",
numberExamples, size));
}
matchers.put(rootPath + appendArrayIndex(1), matchMin(size));
PactDslJsonArray parent = new PactDslJsonArray(rootPath, "", this, true);
parent.setNumberExamples(numberExamples);
parent.putObject(value);
return (PactDslJsonArray) parent.closeArray();
}
/**
* Array of values with a maximum size that are not objects where each item must match the provided example
* @param size maximum size of the array
* @param value Value to use to match each item
*/
public PactDslJsonArray maxArrayLike(Integer size, PactDslJsonRootValue value) {
return maxArrayLike(size, value, 1);
}
/**
* Array of values with a maximum size that are not objects where each item must match the provided example
* @param size maximum size of the array
* @param value Value to use to match each item
* @param numberExamples number of examples to generate
*/
public PactDslJsonArray maxArrayLike(Integer size, PactDslJsonRootValue value, int numberExamples) {
if (numberExamples > size) {
throw new IllegalArgumentException(String.format("Number of example %d is more than the maximum size of %d",
numberExamples, size));
}
matchers.put(rootPath + appendArrayIndex(1), matchMax(size));
PactDslJsonArray parent = new PactDslJsonArray(rootPath, "", this, true);
parent.setNumberExamples(numberExamples);
parent.putObject(value);
return (PactDslJsonArray) parent.closeArray();
}
}