diff --git a/fineract-client/dependencies.gradle b/fineract-client/dependencies.gradle
index 320dfee5b..02baf8d57 100644
--- a/fineract-client/dependencies.gradle
+++ b/fineract-client/dependencies.gradle
@@ -37,7 +37,8 @@ dependencies {
testImplementation(
'org.junit.jupiter:junit-jupiter-api:5.7.0',
- 'com.google.truth:truth:1.0.1'
+ 'com.google.truth:truth:1.0.1',
+ 'com.google.truth.extensions:truth-java8-extension:1.0.1'
)
testRuntimeOnly(
'org.junit.jupiter:junit-jupiter-engine:5.7.0'
diff --git a/fineract-client/src/main/java/org/apache/fineract/client/util/FineractClient.java b/fineract-client/src/main/java/org/apache/fineract/client/util/FineractClient.java
index 9d4dba168..a7e85395a 100644
--- a/fineract-client/src/main/java/org/apache/fineract/client/util/FineractClient.java
+++ b/fineract-client/src/main/java/org/apache/fineract/client/util/FineractClient.java
@@ -147,6 +147,15 @@ import retrofit2.converter.scalars.ScalarsConverterFactory;
*/
public final class FineractClient {
+ /**
+ * Constant to be used in requests where Fineract's API requires a dateFormat to be given. This matches the format
+ * in which LocalDate instances are serialized. (BTW: In a Java client API, it seems weird to have strong LocalDate
+ * (not String) instances, and then have to specify its format, see
+ * https://issues.apache.org/jira/browse/FINERACT-1233.)
+ */
+ // Matching org.apache.fineract.client.util.JSON.LocalDateTypeAdapter.formatter
+ public static final String DATE_FORMAT = "yyyy-mm-dd";
+
private final Retrofit retrofit;
public final AccountingClosureApi glClosures;
diff --git a/fineract-client/src/main/java/org/apache/fineract/client/util/JSON.java b/fineract-client/src/main/java/org/apache/fineract/client/util/JSON.java
index 034c8b356..c1751bc63 100644
--- a/fineract-client/src/main/java/org/apache/fineract/client/util/JSON.java
+++ b/fineract-client/src/main/java/org/apache/fineract/client/util/JSON.java
@@ -97,22 +97,21 @@ class JSON {
}
/**
- * GSON TypeAdapter for JSR-310 LocalDate type in Fineract API Format ([2009,1,1]).
+ * GSON TypeAdapter for JSR-310 LocalDate type, which Fineract API's RETURNS as JSON array in the
+ * [2009,1,1] format, but EXPECTS as String not Array and with a locale and dateFormat. Weird, but so it is
+ * (see FINERACT-1220 & FINERACT-1233).
*/
public static class LocalDateTypeAdapter extends TypeAdapter {
- // Now unused: private final DateTimeFormatter formatter = DateTimeFormatter.ISO_LOCAL_DATE;
+ // NB this format is referenced from org.apache.fineract.client.util.FineractClient.DATE_FORMAT
+ private final DateTimeFormatter formatter = DateTimeFormatter.ISO_LOCAL_DATE;
@Override
public void write(JsonWriter out, LocalDate date) throws IOException {
if (date == null) {
out.nullValue();
} else {
- out.beginArray();
- out.value(date.getYear());
- out.value(date.getMonthValue());
- out.value(date.getDayOfMonth());
- out.endArray();
+ out.value(formatter.format(date));
}
}
diff --git a/fineract-client/src/test/java/org/apache/fineract/client/testutil/CallSubject.java b/fineract-client/src/test/java/org/apache/fineract/client/testutil/CallSubject.java
new file mode 100644
index 000000000..edd4fae21
--- /dev/null
+++ b/fineract-client/src/test/java/org/apache/fineract/client/testutil/CallSubject.java
@@ -0,0 +1,55 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.fineract.client.testutil;
+
+import com.google.common.truth.FailureMetadata;
+import com.google.common.truth.Subject;
+import com.google.common.truth.Truth;
+import java.io.IOException;
+import javax.annotation.Nullable;
+import retrofit2.Call;
+
+/**
+ * Google Truth Extension for Retrofit Call.
+ *
+ * @author Michael Vorburger.ch
+ */
+public class CallSubject extends Subject {
+
+ // as per https://truth.dev/extension
+
+ public static CallSubject assertThat(@Nullable Call> actual) {
+ return Truth.assertAbout(calls()).that(actual);
+ }
+
+ public static Factory> calls() {
+ return CallSubject::new;
+ }
+
+ private final Call> actual;
+
+ protected CallSubject(FailureMetadata metadata, @Nullable Call> actual) {
+ super(metadata, actual);
+ this.actual = actual;
+ }
+
+ public void hasHttpStatus(int expectedHttpStatus) throws IOException {
+ check("httpStatus").that(actual.execute().code()).isEqualTo(expectedHttpStatus);
+ }
+}
diff --git a/fineract-client/src/test/java/org/apache/fineract/integrationtests/newstyle/IntegrationTest.java b/fineract-client/src/test/java/org/apache/fineract/integrationtests/newstyle/IntegrationTest.java
new file mode 100644
index 000000000..81559eed8
--- /dev/null
+++ b/fineract-client/src/test/java/org/apache/fineract/integrationtests/newstyle/IntegrationTest.java
@@ -0,0 +1,134 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.fineract.integrationtests.newstyle;
+
+import com.google.common.truth.BigDecimalSubject;
+import com.google.common.truth.BooleanSubject;
+import com.google.common.truth.ComparableSubject;
+import com.google.common.truth.DoubleSubject;
+import com.google.common.truth.FloatSubject;
+import com.google.common.truth.IntegerSubject;
+import com.google.common.truth.IterableSubject;
+import com.google.common.truth.LongSubject;
+import com.google.common.truth.OptionalSubject;
+import com.google.common.truth.StringSubject;
+import com.google.common.truth.Subject;
+import com.google.common.truth.Truth;
+import com.google.common.truth.Truth8;
+import java.math.BigDecimal;
+import java.util.Optional;
+import java.util.Random;
+import okhttp3.logging.HttpLoggingInterceptor.Level;
+import org.apache.fineract.client.testutil.CallSubject;
+import org.apache.fineract.client.util.Calls;
+import org.apache.fineract.client.util.FineractClient;
+import org.junit.jupiter.api.MethodOrderer;
+import org.junit.jupiter.api.TestMethodOrder;
+import retrofit2.Call;
+
+/**
+ * Integration Test for /documents API.
+ *
+ * @author Michael Vorburger.ch
+ */
+// TODO Remove @TestMethodOrder when https://github.com/junit-team/junit5/issues/1919 is available
+@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
+public abstract class IntegrationTest {
+
+ private static final Random random = new Random();
+
+ private FineractClient fineract;
+
+ protected FineractClient fineract() {
+ if (fineract == null) {
+ // TODO change from Fineract.dev to https://localhost:8443/fineract-provider/api/v1/ after FINERACT-1209
+ String url = System.getProperty("fineract.it.url", "https://demo.fineract.dev/fineract-provider/api/v1/");
+ fineract = FineractClient.builder().baseURL(url).tenant("default").basicAuth("mifos", "password").insecure(true)
+ .logging(Level.BODY).build();
+ }
+ return fineract;
+ }
+
+ /**
+ * See {@link FineractClient#DATE_FORMAT}.
+ */
+ protected String dateFormat() {
+ return FineractClient.DATE_FORMAT;
+ }
+
+ protected String random() {
+ return Long.toString(random.nextLong());
+ }
+
+ // This method just makes it easier to use Calls.ok() in tests (it avoids having to static import)
+ protected T ok(Call call) {
+ return Calls.ok(call);
+ }
+
+ // as above, avoids import static CallSubject.assertThat
+ protected CallSubject assertThat(Call call) {
+ return CallSubject.assertThat(call);
+ }
+
+ // as above, this avoids issues with e.g. the Eclipse compiler getting confused which assertThat is which
+ public static IterableSubject assertThat(Iterable> actual) {
+ return Truth.assertThat(actual);
+ }
+
+ public static > ComparableSubject assertThat(T actual) {
+ return Truth.assertThat(actual);
+ }
+
+ public static BigDecimalSubject assertThat(BigDecimal actual) {
+ return Truth.assertThat(actual);
+ }
+
+ public static Subject assertThat(Object actual) {
+ return Truth.assertThat(actual);
+ }
+
+ public static LongSubject assertThat(Long actual) {
+ return Truth.assertThat(actual);
+ }
+
+ public static DoubleSubject assertThat(Double actual) {
+ return Truth.assertThat(actual);
+ }
+
+ public static FloatSubject assertThat(Float actual) {
+ return Truth.assertThat(actual);
+ }
+
+ public static IntegerSubject assertThat(Integer actual) {
+ return Truth.assertThat(actual);
+ }
+
+ public static BooleanSubject assertThat(Boolean actual) {
+ return Truth.assertThat(actual);
+ }
+
+ public static StringSubject assertThat(String actual) {
+ return Truth.assertThat(actual);
+ }
+
+ // from truth-java8-extension
+ public static OptionalSubject assertThat(Optional> actual) {
+ return Truth8.assertThat(actual);
+ }
+}
diff --git a/fineract-client/src/test/java/org/apache/fineract/integrationtests/newstyle/OfficeTest.java b/fineract-client/src/test/java/org/apache/fineract/integrationtests/newstyle/OfficeTest.java
new file mode 100644
index 000000000..ddfe5db0e
--- /dev/null
+++ b/fineract-client/src/test/java/org/apache/fineract/integrationtests/newstyle/OfficeTest.java
@@ -0,0 +1,48 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.fineract.integrationtests.newstyle;
+
+import java.time.LocalDate;
+import org.apache.fineract.client.models.PostOfficesRequest;
+import org.junit.jupiter.api.Order;
+import org.junit.jupiter.api.Test;
+
+/**
+ * Integration Test for /offices API.
+ *
+ * @author Michael Vorburger.ch
+ */
+public class OfficeTest extends IntegrationTest {
+
+ @Test
+ @Order(1)
+ void createOne() {
+ // NB parentId(1) always exists (Head Office)
+ // NB name random() because Office Names have to be unique
+ // TODO requiring dateFormat(..).locale(..) is dumb :( see https://issues.apache.org/jira/browse/FINERACT-1233
+ assertThat(ok(fineract().offices.createOffice(new PostOfficesRequest().name("TestOffice-" + random()).parentId(1L)
+ .openingDate(LocalDate.now()).dateFormat(dateFormat()).locale("en_US"))).getOfficeId()).isGreaterThan(0);
+ }
+
+ @Test
+ @Order(2)
+ void retrieveOneExisting() {
+ assertThat(ok(fineract().offices.retrieveOffices(true, null, null)).size()).isAtLeast(2);
+ }
+}