From ae6d282827822ad3c7ad21005d87ef313f4a5be2 Mon Sep 17 00:00:00 2001 From: mpeterss Date: Thu, 19 Sep 2019 14:23:54 +0200 Subject: [PATCH 001/173] Add support for CC-Time and CC-Service-Specific-Units The Used-Service-Unit is changed to be a List. This as we have seen P-GWs that report CC-Time and CC-Service-Specific-Units as separate Used-Service-Unit APVs. --- .../main/kotlin/org/ostelco/at/pgw/OcsTest.kt | 140 +++++++++++++++++- .../org/ostelco/diameter/model/Model.kt | 12 +- .../org/ostelco/diameter/test/TestHelper.kt | 31 +++- .../ProtobufToDiameterConverter.java | 18 ++- .../datasource/local/LocalDataSource.java | 2 +- 5 files changed, 189 insertions(+), 14 deletions(-) diff --git a/acceptance-tests/src/main/kotlin/org/ostelco/at/pgw/OcsTest.kt b/acceptance-tests/src/main/kotlin/org/ostelco/at/pgw/OcsTest.kt index a8576105b..a1aba7699 100644 --- a/acceptance-tests/src/main/kotlin/org/ostelco/at/pgw/OcsTest.kt +++ b/acceptance-tests/src/main/kotlin/org/ostelco/at/pgw/OcsTest.kt @@ -15,6 +15,7 @@ import org.ostelco.diameter.test.TestHelper import org.ostelco.prime.customer.model.Bundle import java.lang.Thread.sleep import kotlin.test.assertEquals +import kotlin.test.assertNull import kotlin.test.fail /** @@ -400,7 +401,6 @@ class OcsTest { } // Next request should deny request and grant no quota - val updateRequest = testClient.createRequest( DEST_REALM, DEST_HOST, @@ -450,7 +450,6 @@ class OcsTest { } // If UE attach again and P-GW tries another CCR-I we should get DIAMETER_CREDIT_LIMIT_REACHED - session = testClient.createSession(object{}.javaClass.enclosingMethod.name + "-2") ?: fail("Failed to create session") request = testClient.createRequest( DEST_REALM, @@ -552,6 +551,143 @@ class OcsTest { } } + + /** + * This test that CCR-I with no requested Service-Units + */ + + @Test + fun creditControlRequestInitNoRSU() { + + val email = "ocs-${randomInt()}@test.com" + createCustomer(name = "Test OCS User", email = email) + + val msisdn = createSubscription(email = email) + + val session = testClient.createSession(object{}.javaClass.enclosingMethod.name) ?: fail("Failed to create session") + val request = testClient.createRequest( + DEST_REALM, + DEST_HOST, + session + ) ?: fail("Failed to create request") + + TestHelper.createInitRequest(request.avps, msisdn) + + testClient.sendNextRequest(request, session) + + waitForAnswer(session.sessionId) + + run { + val result = testClient.getAnswer(session.sessionId) + assertEquals(DIAMETER_SUCCESS, result?.resultCode) + val resultAvps = result?.resultAvps ?: fail("Missing AVPs") + assertEquals(DEST_HOST, resultAvps.getAvp(Avp.ORIGIN_HOST).utF8String) + assertEquals(DEST_REALM, resultAvps.getAvp(Avp.ORIGIN_REALM).utF8String) + assertEquals(RequestType.INITIAL_REQUEST.toLong(), resultAvps.getAvp(Avp.CC_REQUEST_TYPE).integer32.toLong()) + val resultMSCC = resultAvps.getAvp(Avp.MULTIPLE_SERVICES_CREDIT_CONTROL) + assertNull(resultMSCC, "There should not be any MSCC if there is no MSCC in the CCR") + } + } + + + /** + * This test will check that we can handle CCR-U requests that also report CC-Time and CC-Service-Specific-Units + * in separate Used-Service-Units in the MSCC + */ + + @Test + fun creditControlRequestInitUpdateCCTime() { + val email = "ocs-${randomInt()}@test.com" + createCustomer(name = "Test OCS User", email = email) + + val msisdn = createSubscription(email = email) + + val ratingGroup = 10 + val serviceIdentifier = -1 + + val session = testClient.createSession(object{}.javaClass.enclosingMethod.name) ?: fail("Failed to create session") + val initRequest = testClient.createRequest( + DEST_REALM, + DEST_HOST, + session + ) ?: fail("Failed to create request") + + // CCR-I is without any Requested-Service-Unints + TestHelper.createInitRequest(initRequest.avps, msisdn) + + testClient.sendNextRequest(initRequest, session) + + waitForAnswer(session.sessionId) + + run { + val result = testClient.getAnswer(session.sessionId) + assertEquals(DIAMETER_SUCCESS, result?.resultCode) + val resultAvps = result?.resultAvps ?: fail("Missing AVPs") + assertEquals(DEST_HOST, resultAvps.getAvp(Avp.ORIGIN_HOST).utF8String) + assertEquals(DEST_REALM, resultAvps.getAvp(Avp.ORIGIN_REALM).utF8String) + assertEquals(RequestType.INITIAL_REQUEST.toLong(), resultAvps.getAvp(Avp.CC_REQUEST_TYPE).integer32.toLong()) + val resultMSCC = resultAvps.getAvp(Avp.MULTIPLE_SERVICES_CREDIT_CONTROL) + assertNull(resultMSCC, "There should not be any MSCC if there is no MSCC in the CCR") + } + + + // First Update Request with Requested-Service-Units ( no Used-Service-Units ) + val updateRequest1 = testClient.createRequest( + DEST_REALM, + DEST_HOST, + session + ) + + TestHelper.createUpdateRequest(updateRequest1!!.avps, msisdn, 0L, 0L, ratingGroup, serviceIdentifier, 725L, 1L) + + testClient.sendNextRequest(updateRequest1, session) + + waitForAnswer(session.sessionId) + + run { + val result = testClient.getAnswer(session.sessionId) + assertEquals(DIAMETER_SUCCESS, result?.resultCode) + val resultAvps = result?.resultAvps ?: fail("Missing AVPs") + assertEquals(DEST_HOST, resultAvps.getAvp(Avp.ORIGIN_HOST).utF8String) + assertEquals(DEST_REALM, resultAvps.getAvp(Avp.ORIGIN_REALM).utF8String) + assertEquals(RequestType.UPDATE_REQUEST.toLong(), resultAvps.getAvp(Avp.CC_REQUEST_TYPE).integer32.toLong()) + val resultMSCC = resultAvps.getAvp(Avp.MULTIPLE_SERVICES_CREDIT_CONTROL) + assertEquals(DIAMETER_SUCCESS, resultMSCC.grouped.getAvp(Avp.RESULT_CODE).integer32.toLong()) + assertEquals(ratingGroup.toLong(), resultMSCC.grouped.getAvp(Avp.RATING_GROUP).integer32.toLong()) + val granted = resultMSCC.grouped.getAvp(Avp.GRANTED_SERVICE_UNIT) + assertEquals(DEFAULT_REQUESTED_SERVICE_UNIT, granted.grouped.getAvp(Avp.CC_TOTAL_OCTETS).unsigned64) + } + + + // Second Update Request with Requested-Service-Units and Used-Service-Units + val updateRequest2 = testClient.createRequest( + DEST_REALM, + DEST_HOST, + session + ) + + TestHelper.createUpdateRequest(updateRequest2!!.avps, msisdn, 0L, DEFAULT_REQUESTED_SERVICE_UNIT, ratingGroup, serviceIdentifier, 725L, 1L) + + testClient.sendNextRequest(updateRequest2, session) + + waitForAnswer(session.sessionId) + + run { + val result = testClient.getAnswer(session.sessionId) + assertEquals(DIAMETER_SUCCESS, result?.resultCode) + val resultAvps = result?.resultAvps ?: fail("Missing AVPs") + assertEquals(DEST_HOST, resultAvps.getAvp(Avp.ORIGIN_HOST).utF8String) + assertEquals(DEST_REALM, resultAvps.getAvp(Avp.ORIGIN_REALM).utF8String) + assertEquals(RequestType.UPDATE_REQUEST.toLong(), resultAvps.getAvp(Avp.CC_REQUEST_TYPE).integer32.toLong()) + val resultMSCC = resultAvps.getAvp(Avp.MULTIPLE_SERVICES_CREDIT_CONTROL) + assertEquals(DIAMETER_SUCCESS, resultMSCC.grouped.getAvp(Avp.RESULT_CODE).integer32.toLong()) + assertEquals(ratingGroup.toLong(), resultMSCC.grouped.getAvp(Avp.RATING_GROUP).integer32.toLong()) + val granted = resultMSCC.grouped.getAvp(Avp.GRANTED_SERVICE_UNIT) + assertEquals(DEFAULT_REQUESTED_SERVICE_UNIT, granted.grouped.getAvp(Avp.CC_TOTAL_OCTETS).unsigned64) + } + + } + @Test fun creditControlRequestInitUpdateAndTerminateNoRequestedServiceUnit() { diff --git a/diameter-stack/src/main/kotlin/org/ostelco/diameter/model/Model.kt b/diameter-stack/src/main/kotlin/org/ostelco/diameter/model/Model.kt index 0b3281da6..282d15b66 100644 --- a/diameter-stack/src/main/kotlin/org/ostelco/diameter/model/Model.kt +++ b/diameter-stack/src/main/kotlin/org/ostelco/diameter/model/Model.kt @@ -82,6 +82,12 @@ class ServiceUnit() { @AvpField(Avp.CC_OUTPUT_OCTETS) var output: Long = 0 + @AvpField(Avp.CC_TIME) + var ccTime: Long = 0 + + @AvpField(Avp.CC_SERVICE_SPECIFIC_UNITS) + var ccServiceSpecificUnits: Long = 0 + @AvpField(Avp.REPORTING_REASON) var reportingReason: ReportingReason? = null @@ -106,8 +112,8 @@ class MultipleServiceCreditControl() { @AvpList(Avp.REQUESTED_SERVICE_UNIT, ServiceUnit::class) var requested: List = emptyList() - @AvpField(Avp.USED_SERVICE_UNIT) - var used = ServiceUnit() + @AvpList(Avp.USED_SERVICE_UNIT, ServiceUnit::class) + var used: List = emptyList() @AvpField(Avp.GRANTED_SERVICE_UNIT) var granted = ServiceUnit() @@ -130,7 +136,7 @@ class MultipleServiceCreditControl() { ratingGroup: Long, serviceIdentifier: Long, requested: List, - used: ServiceUnit, + used: List, granted: ServiceUnit, validityTime: Int, quotaHoldingTime: Long, diff --git a/diameter-test/src/main/kotlin/org/ostelco/diameter/test/TestHelper.kt b/diameter-test/src/main/kotlin/org/ostelco/diameter/test/TestHelper.kt index 71f8a0647..bca5c2a8b 100644 --- a/diameter-test/src/main/kotlin/org/ostelco/diameter/test/TestHelper.kt +++ b/diameter-test/src/main/kotlin/org/ostelco/diameter/test/TestHelper.kt @@ -42,7 +42,7 @@ object TestHelper { } } - private fun addBucketRequest(ccrAvps: AvpSet, ratingGroup: Int, serviceIdentifier: Int, requestedBucketSize: Long, usedBucketSize: Long = 0) { + private fun addBucketRequest(ccrAvps: AvpSet, ratingGroup: Int, serviceIdentifier: Int, requestedBucketSize: Long, usedBucketSize: Long = 0, ccTime: Long = 0, ccServiceSpecificUnits: Long = 0) { set(ccrAvps) { @@ -72,6 +72,20 @@ object TestHelper { avp(Avp.REPORTING_REASON, ReportingReason.QUOTA_EXHAUSTED.ordinal, VENDOR_ID_3GPP, mFlag = true, pFlag = true) } } + + if (ccTime > 0) { + group(Avp.USED_SERVICE_UNIT) { + avp(Avp.CC_TIME, ccTime, pFlag = true) + avp(Avp.REPORTING_REASON, ReportingReason.OTHER_QUOTA_TYPE.ordinal, VENDOR_ID_3GPP, mFlag = true, pFlag = true) + } + } + + if (ccServiceSpecificUnits > 0) { + group(Avp.USED_SERVICE_UNIT) { + avp(Avp.CC_SERVICE_SPECIFIC_UNITS, ccServiceSpecificUnits, pFlag = true) + avp(Avp.REPORTING_REASON, ReportingReason.OTHER_QUOTA_TYPE.ordinal, VENDOR_ID_3GPP, mFlag = true, pFlag = true) + } + } } } } @@ -136,7 +150,12 @@ object TestHelper { } } - + @JvmStatic + fun createInitRequest(ccrAvps: AvpSet, msisdn: String) { + buildBasicRequest(ccrAvps, RequestType.INITIAL_REQUEST, requestNumber = 0) + addUser(ccrAvps, msisdn = msisdn, imsi = IMSI) + addServiceInformation(ccrAvps, apn = APN, sgsnMccMnc = SGSN_MCC_MNC) + } @JvmStatic fun createInitRequest(ccrAvps: AvpSet, msisdn: String, requestedBucketSize: Long, ratingGroup: Int, serviceIdentifier: Int) { @@ -164,6 +183,14 @@ object TestHelper { addServiceInformation(ccrAvps, apn = APN, sgsnMccMnc = SGSN_MCC_MNC) } + @JvmStatic + fun createUpdateRequest(ccrAvps: AvpSet, msisdn: String, requestedBucketSize: Long, usedBucketSize: Long, ratingGroup: Int, serviceIdentifier: Int, ccTime: Long, ccServiceSpecificUnits: Long) { + buildBasicRequest(ccrAvps, RequestType.UPDATE_REQUEST, requestNumber = 1) + addUser(ccrAvps, msisdn = msisdn, imsi = IMSI) + addBucketRequest(ccrAvps, ratingGroup, serviceIdentifier, requestedBucketSize = requestedBucketSize, usedBucketSize = usedBucketSize, ccTime = ccTime, ccServiceSpecificUnits = ccServiceSpecificUnits) + addServiceInformation(ccrAvps, apn = APN, sgsnMccMnc = SGSN_MCC_MNC) + } + @JvmStatic fun createUpdateRequestFinal(ccrAvps: AvpSet, msisdn: String, usedBucketSize: Long, ratingGroup: Int, serviceIdentifier: Int) { buildBasicRequest(ccrAvps, RequestType.UPDATE_REQUEST, requestNumber = 1) diff --git a/ocsgw/src/main/java/org/ostelco/ocsgw/converter/ProtobufToDiameterConverter.java b/ocsgw/src/main/java/org/ostelco/ocsgw/converter/ProtobufToDiameterConverter.java index 0e8ae8ac4..8618092a3 100644 --- a/ocsgw/src/main/java/org/ostelco/ocsgw/converter/ProtobufToDiameterConverter.java +++ b/ocsgw/src/main/java/org/ostelco/ocsgw/converter/ProtobufToDiameterConverter.java @@ -25,7 +25,9 @@ public static MultipleServiceCreditControl convertMSCC(org.ostelco.ocs.api.Multi return new MultipleServiceCreditControl( msccGRPC.getRatingGroup(), (int) msccGRPC.getServiceIdentifier(), - Collections.singletonList(new ServiceUnit()), new ServiceUnit(), new ServiceUnit(msccGRPC.getGranted().getTotalOctets(), 0, 0), + Collections.singletonList(new ServiceUnit()), + Collections.singletonList(new ServiceUnit()), + new ServiceUnit(msccGRPC.getGranted().getTotalOctets(), 0, 0), msccGRPC.getValidityTime(), msccGRPC.getQuotaHoldingTime(), msccGRPC.getVolumeQuotaThreshold(), @@ -99,12 +101,16 @@ public static CreditControlRequestInfo convertRequestToProtobuf(final CreditCont .setOutputOctets(0L)); } - ServiceUnit used = mscc.getUsed(); + for (ServiceUnit used : mscc.getUsed()) { - protoMscc.setUsed(org.ostelco.ocs.api.ServiceUnit.newBuilder() - .setInputOctets(used.getInput()) - .setOutputOctets(used.getOutput()) - .setTotalOctets(used.getTotal())); + // We do not track CC-Service-Specific-Units or CC-Time + if (used.getTotal() > 0) { + protoMscc.setUsed(org.ostelco.ocs.api.ServiceUnit.newBuilder() + .setInputOctets(used.getInput()) + .setOutputOctets(used.getOutput()) + .setTotalOctets(used.getTotal())); + } + } protoMscc.setRatingGroup(mscc.getRatingGroup()); protoMscc.setServiceIdentifier(mscc.getServiceIdentifier()); diff --git a/ocsgw/src/main/java/org/ostelco/ocsgw/datasource/local/LocalDataSource.java b/ocsgw/src/main/java/org/ostelco/ocsgw/datasource/local/LocalDataSource.java index 79937ac44..13a11f738 100644 --- a/ocsgw/src/main/java/org/ostelco/ocsgw/datasource/local/LocalDataSource.java +++ b/ocsgw/src/main/java/org/ostelco/ocsgw/datasource/local/LocalDataSource.java @@ -75,7 +75,7 @@ private CreditControlAnswer createCreditControlAnswer(CreditControlContext conte mscc.getRatingGroup(), mscc.getServiceIdentifier(), newRequested, - new ServiceUnit(mscc.getUsed().getTotal(), mscc.getUsed().getInput(), mscc.getUsed().getOutput()), + mscc.getUsed(), granted, mscc.getValidityTime(), 7200, From 7ba7c99fab80b18732e92c45789d83be530db3c5 Mon Sep 17 00:00:00 2001 From: mpeterss Date: Thu, 19 Sep 2019 14:35:54 +0200 Subject: [PATCH 002/173] Added short description to all test cases --- .../main/kotlin/org/ostelco/at/pgw/OcsTest.kt | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/acceptance-tests/src/main/kotlin/org/ostelco/at/pgw/OcsTest.kt b/acceptance-tests/src/main/kotlin/org/ostelco/at/pgw/OcsTest.kt index a1aba7699..02fb4d38e 100644 --- a/acceptance-tests/src/main/kotlin/org/ostelco/at/pgw/OcsTest.kt +++ b/acceptance-tests/src/main/kotlin/org/ostelco/at/pgw/OcsTest.kt @@ -110,6 +110,9 @@ class OcsTest { }.first().balance } + /** + * Test that the OCS will correctly handle CCR with Requested-Service-Units for multiple Rating-Groups + */ @Test fun multiRatingGroupsInit() { @@ -357,6 +360,9 @@ class OcsTest { } } + /** + * Test that a users gets correctly denied when the balance on the OCS is used up + */ @Test fun creditControlRequestInitTerminateNoCredit() { @@ -484,6 +490,10 @@ class OcsTest { } + /** + * Test that the OCS will deny service for users not in the system + */ + @Test fun creditControlRequestInitUnknownUser() { @@ -512,6 +522,10 @@ class OcsTest { } } + /** + * Test CCR with Requested-Service-Units for a Rating-Group only ( no Service-Identifier set ) + */ + @Test fun creditControlRequestInitNoServiceId() { @@ -688,6 +702,11 @@ class OcsTest { } + /** + * Test that the default bucket size is used by the OCS when the CCR only contain + * Requested-Service-Unit without specified value. + */ + @Test fun creditControlRequestInitUpdateAndTerminateNoRequestedServiceUnit() { @@ -724,6 +743,9 @@ class OcsTest { } + /** + * Test that the OCS will handle CCR-U that does not contain any Requested-Service-Units only Used-Service-Units + */ @Test fun simpleCreditControlRequestInitUpdateNoRSU() { From 61e066b64892881926aba2c429e841221feedd53 Mon Sep 17 00:00:00 2001 From: mpeterss Date: Thu, 19 Sep 2019 14:36:29 +0200 Subject: [PATCH 003/173] Remove the last test answer after use Since the client only needs to store an answer until it has has been checked by the test it should be removed to avoid mixup --- .../src/main/kotlin/org/ostelco/diameter/test/TestClient.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/diameter-test/src/main/kotlin/org/ostelco/diameter/test/TestClient.kt b/diameter-test/src/main/kotlin/org/ostelco/diameter/test/TestClient.kt index 641bc6eed..c563c0551 100644 --- a/diameter-test/src/main/kotlin/org/ostelco/diameter/test/TestClient.kt +++ b/diameter-test/src/main/kotlin/org/ostelco/diameter/test/TestClient.kt @@ -180,11 +180,11 @@ class TestClient : EventListener { } fun getAnswer(sessionId: String) : Result? { - return answerMap.get(sessionId) + return answerMap.remove(sessionId) } fun getRequest(sessionId: String) : Result? { - return requestMap.get(sessionId) + return requestMap.remove(sessionId) } override fun receivedSuccessMessage(request: Request, answer: Answer) { From d068b0743d4c44f426a1c11559e9f76edff44345 Mon Sep 17 00:00:00 2001 From: mpeterss Date: Thu, 19 Sep 2019 15:01:42 +0200 Subject: [PATCH 004/173] Updated comment --- acceptance-tests/src/main/kotlin/org/ostelco/at/pgw/OcsTest.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/acceptance-tests/src/main/kotlin/org/ostelco/at/pgw/OcsTest.kt b/acceptance-tests/src/main/kotlin/org/ostelco/at/pgw/OcsTest.kt index 02fb4d38e..2abba95e8 100644 --- a/acceptance-tests/src/main/kotlin/org/ostelco/at/pgw/OcsTest.kt +++ b/acceptance-tests/src/main/kotlin/org/ostelco/at/pgw/OcsTest.kt @@ -567,7 +567,7 @@ class OcsTest { /** - * This test that CCR-I with no requested Service-Units + * This test CCR-I without any Requested-Service-Units */ @Test From 8fd3c288d46820cd381b180eb2d0e7ba2e020500 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B8rn=20Remseth?= Date: Tue, 24 Sep 2019 08:33:11 +0200 Subject: [PATCH 005/173] More elaborate error logging from what is now an intermittent error --- .../org/ostelco/simcards/admin/SmdpPlusHealthceck.kt | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/sim-administration/simmanager/src/main/kotlin/org/ostelco/simcards/admin/SmdpPlusHealthceck.kt b/sim-administration/simmanager/src/main/kotlin/org/ostelco/simcards/admin/SmdpPlusHealthceck.kt index aaab1bbbb..8c3130f4b 100644 --- a/sim-administration/simmanager/src/main/kotlin/org/ostelco/simcards/admin/SmdpPlusHealthceck.kt +++ b/sim-administration/simmanager/src/main/kotlin/org/ostelco/simcards/admin/SmdpPlusHealthceck.kt @@ -125,8 +125,11 @@ class SmdpPlusHealthceck( is Either.Left -> if (pingResult.a.pingOk) { return true } else { - logger.error("Could not reach SM-DP+ via HTTP PING:", pingResult) - return false + pingResult.mapLeft { + error -> + logger.error("Could not reach SM-DP+ via HTTP PING: '${error.description}'. '$error'") + return false + } } is Either.Right -> { return true From 62340f35fc54d47e71d9bbb6416d9746767ac885 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B8rn=20Remseth?= Date: Tue, 24 Sep 2019 08:39:59 +0200 Subject: [PATCH 006/173] Refactor if-branch so that the double exclamation is no longer necessary --- .../profilevendors/ProfileVendorAdapterDatum.kt | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/sim-administration/simmanager/src/main/kotlin/org/ostelco/simcards/profilevendors/ProfileVendorAdapterDatum.kt b/sim-administration/simmanager/src/main/kotlin/org/ostelco/simcards/profilevendors/ProfileVendorAdapterDatum.kt index 170e889f0..c74ccffb7 100644 --- a/sim-administration/simmanager/src/main/kotlin/org/ostelco/simcards/profilevendors/ProfileVendorAdapterDatum.kt +++ b/sim-administration/simmanager/src/main/kotlin/org/ostelco/simcards/profilevendors/ProfileVendorAdapterDatum.kt @@ -138,11 +138,13 @@ data class ProfileVendorAdapter( .flatMap { response -> if (executionWasFailure(status = response.myHeader.functionExecutionStatus)) { logAndReturnNotFoundError("execution getProfileStatusA. iccidList='$iccidList', status=${response.myHeader.functionExecutionStatus}") - } else if (response.profileStatusList == null) { - logAndReturnNotFoundError("Couldn't find any response for query iccidlist='$iccidList'") } else { - val result = response.profileStatusList!! // TODO: Why is this necessary (see if-branch above) - return result.right() + val result = response.profileStatusList + if (result == null) { + logAndReturnNotFoundError("Couldn't find any response for query iccidlist='$iccidList'") + } else { + return result.right() + } } } } @@ -181,7 +183,7 @@ data class ProfileVendorAdapter( return NotUpdatedError("simEntry without id. simEntry=$simEntry").left() } - + return confirmOrderA(iccid = simEntry.iccid, eid = eid, releaseFlag = releaseFlag) .flatMap { response -> @@ -220,6 +222,7 @@ data class ProfileVendorAdapter( .flatMap { it.first().right() } + /** * Downloads the SM-DP+ 'profile status' information for a list of ICCIDs * from a SM-DP+ service. From e3b587d846ccd36535b1c154a68f50eba3943838 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B8rn=20Remseth?= Date: Tue, 24 Sep 2019 08:42:11 +0200 Subject: [PATCH 007/173] Make variable name more self-explanatory --- .../simcards/profilevendors/ProfileVendorAdapterDatum.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sim-administration/simmanager/src/main/kotlin/org/ostelco/simcards/profilevendors/ProfileVendorAdapterDatum.kt b/sim-administration/simmanager/src/main/kotlin/org/ostelco/simcards/profilevendors/ProfileVendorAdapterDatum.kt index c74ccffb7..bfbd61717 100644 --- a/sim-administration/simmanager/src/main/kotlin/org/ostelco/simcards/profilevendors/ProfileVendorAdapterDatum.kt +++ b/sim-administration/simmanager/src/main/kotlin/org/ostelco/simcards/profilevendors/ProfileVendorAdapterDatum.kt @@ -270,12 +270,12 @@ data class ProfileVendorAdapter( * A dummy ICCID. May or may notreturn a valid profile from any HSS or SM-DP+, but is * useful for checking of there is an SM-DP+ in the other end of the connection. */ - val invalidICCID = listOf("8901000000000000001") + val listContainingOnlyInvalidIccid = listOf("8901000000000000001") /** * Contact the ES2+ endpoint of the SM-DP+, and return true if the answer indicates * that it's up. */ fun ping(): Either> = - getProfileStatus(iccidList = invalidICCID, expectSuccess = false) + getProfileStatus(iccidList = listContainingOnlyInvalidIccid, expectSuccess = false) } \ No newline at end of file From 50c50dfcfe8eb408897f0a023a934c8035ac381e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B8rn=20Remseth?= Date: Tue, 24 Sep 2019 11:34:20 +0200 Subject: [PATCH 008/173] Fixing type error --- .../org/ostelco/simcards/admin/SmdpPlusHealthceck.kt | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/sim-administration/simmanager/src/main/kotlin/org/ostelco/simcards/admin/SmdpPlusHealthceck.kt b/sim-administration/simmanager/src/main/kotlin/org/ostelco/simcards/admin/SmdpPlusHealthceck.kt index 8c3130f4b..5ea086b22 100644 --- a/sim-administration/simmanager/src/main/kotlin/org/ostelco/simcards/admin/SmdpPlusHealthceck.kt +++ b/sim-administration/simmanager/src/main/kotlin/org/ostelco/simcards/admin/SmdpPlusHealthceck.kt @@ -121,18 +121,17 @@ class SmdpPlusHealthceck( // the endpoint in the other end actually gave a reasonable answer to a reasonable request, // indicating that the endpoint is answering requests, then continue to loop over next endpoint, // otherwise see if there is an error. - when (pingResult) { + return when (pingResult) { is Either.Left -> if (pingResult.a.pingOk) { - return true + true } else { - pingResult.mapLeft { - error -> + pingResult.mapLeft { error -> logger.error("Could not reach SM-DP+ via HTTP PING: '${error.description}'. '$error'") - return false } + false } is Either.Right -> { - return true + true } } } From 4facdf2157e891d556a3c58dc6c6dfce5f7399ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B8rn=20Remseth?= Date: Fri, 27 Sep 2019 18:15:42 +0200 Subject: [PATCH 009/173] Fixing issue reported in code review --- .../simcards/profilevendors/ProfileVendorAdapterDatum.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sim-administration/simmanager/src/main/kotlin/org/ostelco/simcards/profilevendors/ProfileVendorAdapterDatum.kt b/sim-administration/simmanager/src/main/kotlin/org/ostelco/simcards/profilevendors/ProfileVendorAdapterDatum.kt index bfbd61717..5f2e37879 100644 --- a/sim-administration/simmanager/src/main/kotlin/org/ostelco/simcards/profilevendors/ProfileVendorAdapterDatum.kt +++ b/sim-administration/simmanager/src/main/kotlin/org/ostelco/simcards/profilevendors/ProfileVendorAdapterDatum.kt @@ -143,7 +143,7 @@ data class ProfileVendorAdapter( if (result == null) { logAndReturnNotFoundError("Couldn't find any response for query iccidlist='$iccidList'") } else { - return result.right() + result.right() } } } From e7ea433762eda4c1ee48bf541f730264198e0dc0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B8rn=20Remseth?= Date: Fri, 27 Sep 2019 21:05:16 +0200 Subject: [PATCH 010/173] Whitespace --- .../simcards/profilevendors/ProfileVendorAdapterDatum.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sim-administration/simmanager/src/main/kotlin/org/ostelco/simcards/profilevendors/ProfileVendorAdapterDatum.kt b/sim-administration/simmanager/src/main/kotlin/org/ostelco/simcards/profilevendors/ProfileVendorAdapterDatum.kt index 5f2e37879..8fdc404fe 100644 --- a/sim-administration/simmanager/src/main/kotlin/org/ostelco/simcards/profilevendors/ProfileVendorAdapterDatum.kt +++ b/sim-administration/simmanager/src/main/kotlin/org/ostelco/simcards/profilevendors/ProfileVendorAdapterDatum.kt @@ -143,7 +143,7 @@ data class ProfileVendorAdapter( if (result == null) { logAndReturnNotFoundError("Couldn't find any response for query iccidlist='$iccidList'") } else { - result.right() + result.right() } } } From 24e0eb61371ba24b82a62e8f3c3af8df855343d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B8rn=20Remseth?= Date: Tue, 1 Oct 2019 14:55:20 +0200 Subject: [PATCH 011/173] Making new go.mod file, to make go modules find their home :-) --- go.mod | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 go.mod diff --git a/go.mod b/go.mod new file mode 100644 index 000000000..8ea2428ce --- /dev/null +++ b/go.mod @@ -0,0 +1,9 @@ +module github.com/ostelco/ostelco-core + +go 1.13 + +require ( + github.com/google/go-cmp v0.3.1 // indirect + github.com/pkg/errors v0.8.1 // indirect + gotest.tools v2.2.0+incompatible +) From 96567adc2377c00e1067e46369cfab5e522a8084 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B8rn=20Remseth?= Date: Tue, 1 Oct 2019 15:34:52 +0200 Subject: [PATCH 012/173] Another check for the length of the batch, it is possible to do that wrong, so obvioiusly I did that wrong. --- .../uploader/upload-sim-batch.go | 28 ++++++++++++++++--- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/sim-administration/uploader/upload-sim-batch.go b/sim-administration/uploader/upload-sim-batch.go index 4d916bcde..e231f0f12 100755 --- a/sim-administration/uploader/upload-sim-batch.go +++ b/sim-administration/uploader/upload-sim-batch.go @@ -209,6 +209,10 @@ func parseCommandLine() Batch { firstMsisdn := flag.String("first-msisdn", "Not a valid MSISDN", "First MSISDN in batch") lastMsisdn := flag.String("last-msisdn", "Not a valid MSISDN", "Last MSISDN in batch") profileType := flag.String("profile-type", "Not a valid sim profile type", "SIM profile type") + batchLengthString := flag.String( + "batch-length", + "Not a valid batch-length, must be an integer", + "Number of sim cards in batch") // XXX Legal values are Loltel and M1 at this time, how to configure that // flexibly? Eventually by puttig them in a database and consulting it during @@ -246,6 +250,15 @@ func parseCommandLine() Batch { checkMSISDNSyntax("last-msisdn", *lastMsisdn) checkMSISDNSyntax("first-msisdn", *firstMsisdn) + batchLength, err := Atoi(*batchLengthString) + if err != nil { + log.Fatalf("Not a valid batch length string '%s'.\n", *batchLengthString) + } + + if batchLength <= 0 { + log.Fatalf("Batch length must be positive, but was '%d'", batchLength) + } + uploadUrl := fmt.Sprintf("http://%s:%s/ostelco/sim-inventory/%s/import-batch/profilevendor/%s?initialHssState=%s", *uploadHostname, *uploadPortnumber, *hssVendor, *profileVendor, *initialHlrActivationStatusOfProfiles) @@ -254,8 +267,14 @@ func parseCommandLine() Batch { // Convert to integers, and get lengths - log.Println("firstmsisdn =", *firstMsisdn) - log.Println("lastmsisdn =", *lastMsisdn) + msisdnIncrement := -1 + if *firstMsisdn <= *lastMsisdn { + msisdnIncrement = 1 + } + + log.Println("firstmsisdn = ", *firstMsisdn) + log.Println("lastmsisdn = ", *lastMsisdn) + log.Println("msisdnIncrement = ", msisdnIncrement) var firstMsisdnInt, _ = Atoi(*firstMsisdn) var lastMsisdnInt, _ = Atoi(*lastMsisdn) @@ -274,7 +293,7 @@ func parseCommandLine() Batch { // Validate that lengths of sequences are equal in absolute // values. - if Abs(msisdnLen) != Abs(iccidlen) || Abs(msisdnLen) != Abs(imsiLen) { + if Abs(msisdnLen) != Abs(iccidlen) || Abs(msisdnLen) != Abs(imsiLen) || batchLength != Abs(imsiLen) { log.Printf("msisdnLen = %10d\n", msisdnLen) log.Printf("iccidLen = %10d\n", iccidlen) log.Printf("imsiLen = %10d\n", imsiLen) @@ -286,6 +305,7 @@ func parseCommandLine() Batch { log.Printf("Unknown parameters: %s", flag.Args()) } + // Return a correctly parsed batch return Batch{ profileType: *profileType, @@ -296,6 +316,6 @@ func parseCommandLine() Batch { firstImsi: firstImsiInt, imsiIncrement: Sign(imsiLen), firstMsisdn: firstMsisdnInt, - msisdnIncrement: Sign(msisdnLen), + msisdnIncrement: msisdnIncrement, } } From 8591cd5d13a59bf668e01373a482bb5a2b284831 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B8rn=20Remseth?= Date: Tue, 1 Oct 2019 15:44:12 +0200 Subject: [PATCH 013/173] Update docs --- sim-administration/uploader/README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/sim-administration/uploader/README.md b/sim-administration/uploader/README.md index d535bb6cf..26c815894 100644 --- a/sim-administration/uploader/README.md +++ b/sim-administration/uploader/README.md @@ -1,6 +1,6 @@ # How to upload batch information to prime using the -## Introuduction +## Introduction Prime has REST endpoint for uploading sim batches. This is an interface with little error checking (beyond the bare miniumums) and a low abstraction layer: It requires a CSV file of ICCID/IMSI/MSISDN/PROFILE tuples. @@ -59,6 +59,8 @@ and will cause error messages.) ``` + -batch-length integer + The number of profiles in the batch. Must match with iccid, msisdn and imsi ranges (if present). -first-iccid string An 18 or 19 digit long string. The 19-th digit being a luhn luhnChecksum digit, if present (default "not a valid iccid") -first-imsi string From 176eb8e7741e178aba11ba9fb848d31821bfe390 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B8rn=20Remseth?= Date: Tue, 1 Oct 2019 15:54:11 +0200 Subject: [PATCH 014/173] Add informal TODO list --- sim-administration/uploader/TODO.md | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 sim-administration/uploader/TODO.md diff --git a/sim-administration/uploader/TODO.md b/sim-administration/uploader/TODO.md new file mode 100644 index 000000000..aa3827208 --- /dev/null +++ b/sim-administration/uploader/TODO.md @@ -0,0 +1,21 @@ +An informal TODO list for the sim batch management tool +== + +1. Make an RDBMS that handles sim card workflows. +2. It must by necessity be able to handle free lists of + imsis, msisdns etc. +3. As today, it should be possible to -generate- those lists + from parameters, where that makes sense. In general howeve,r + in particular for production use, this will not be the case + and we need to cater for that. +4. The programme should -initially- be wholly command line + oriented, with a database using sqlite. +5. At some (much) later stage, it may make sense to put it + in some cloud, somewhere. +6. The interfaces to external parties will be + - input/output files for profile generation. + - some kind of file (not yet determind) for msisdn lists. + - HTTP upload commands, either indirectly via curl (as now), or + directly from the script later. In either case + it will be assumed that tunnels are set up out of band, and + tunnel setup is not part of this program. From 1f7f37866440ff175c96ed3d5082047273fe4abc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B8rn=20Remseth?= Date: Wed, 2 Oct 2019 12:36:41 +0200 Subject: [PATCH 015/173] Remove duplicate parseCommandLine function --- .../outfile_to_hss_input_converter.go | 27 +------------------ .../uploader/upload-sim-batch.go | 3 +++ 2 files changed, 4 insertions(+), 26 deletions(-) diff --git a/sim-administration/uploader/outfile_to_hss_input_converter.go b/sim-administration/uploader/outfile_to_hss_input_converter.go index fb5ef5186..319abff3a 100755 --- a/sim-administration/uploader/outfile_to_hss_input_converter.go +++ b/sim-administration/uploader/outfile_to_hss_input_converter.go @@ -55,7 +55,7 @@ func main() { err := WriteHssCsvFile(outputFile, outRecord.entries) if err != nil { - log.Fatal("Couldn't close output file '", outputFilePrefix, "'. Error = '", err, "'") + log.Fatal("Couldn't close output file '", outputFile, "'. Error = '", err, "'") } } @@ -295,31 +295,6 @@ func isComment(s string) bool { return match } -/// XXX Add a main function that -// a) Reads the output file, then produces a HSS input file from that -// b) Later, integrate with the prime input generator, and add a -// database to keep track of the workflow. -// -// - -// -// Set up command line parsing -// -func parseCommandLine() (string, string) { - inputFile := flag.String("input-file", - "not a valid filename", - "path to .out file used as input file") - - outputFile := flag.String("output-file-prefix", - "not a valid filename", - "prefix to path to .csv file used as input file, filename will be autogenerated") - - // - // Parse input according to spec above - // - flag.Parse() - return *inputFile, *outputFile -} // fileExists checks if a file exists and is not a directory before we // try using it to prevent further errors. diff --git a/sim-administration/uploader/upload-sim-batch.go b/sim-administration/uploader/upload-sim-batch.go index e231f0f12..0996befe2 100755 --- a/sim-administration/uploader/upload-sim-batch.go +++ b/sim-administration/uploader/upload-sim-batch.go @@ -193,6 +193,9 @@ func Abs(x int) int { return x } + + + func parseCommandLine() Batch { // From 8355d620c242bdd07ca95ec65e13654dce0dae4a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B8rn=20Remseth?= Date: Wed, 2 Oct 2019 12:45:48 +0200 Subject: [PATCH 016/173] Movenig readme even more --- sim-administration/uploader/README.md | 103 ++++++-------------------- 1 file changed, 21 insertions(+), 82 deletions(-) diff --git a/sim-administration/uploader/README.md b/sim-administration/uploader/README.md index 26c815894..168b65331 100644 --- a/sim-administration/uploader/README.md +++ b/sim-administration/uploader/README.md @@ -1,92 +1,31 @@ -# How to upload batch information to prime using the +# Sim logistics management -## Introduction -Prime has REST endpoint for uploading sim batches. This is an -interface with little error checking (beyond the bare miniumums) -and a low abstraction layer: It requires a CSV file of ICCID/IMSI/MSISDN/PROFILE tuples. +This directory contains code for managing orders of SIM cards, +sending these orders to various actors, and injecting them into +various subsystems. -This is convenient as a starting point, but in practice it has turned -out to be a little too simple, hence the script upload-sim-batch.go. +It is currently in a state of flux, but will evolve in a bottom +up fashion driven by daily demands to perform the tasks the code +in this directory assists in. -This script takes assumes that there is already a way to talk HTTP -(no encryption) to the upload profiles. The default assumption is that -a tunnel has been set up from localhost:8080 to somewhere more -appropriate, but these coordinaters can be tweaked using command line -parameters. +The code in this directory is written in golang. Currently there are two +go programmes here, both designed to be run from the command line. -The basic REST interface assumes an incoming .csv file, but that is -bulky, and the information content is low. In practice we will -more often than not know the ranges of IMSI, ICCID and MSISDN numbers -involved, and obviously also the profile type names. The script can -take these values as parameters and generate a valid CSV file, and -upload it automatically. + * outfile_to_hss_input_converter.go: Will convert output from + a SIM card vendor into input for a HSS vendor. + + * upload-sim-batch.go: Will from command line parameters generate a + bash script that will use curl to upload sim card parameters + to a "prime" instance. + +For both of these programmes, see the source code, in particular the +comments near the top of the files for instructions on how to use them. -The parameters are checked for consistency, so that if there are -more MSISDNs than ICCIDs in the ranges given as parameters, for instance, -then the script will terminate with an error message. -(these are reasonable things to check btw, errors have been made -that justifies adding these checks). +# TODO +* Make a build command that runs tests and reports test coverage (etc), + make it part of the "build-all.go" script. -##Prerequisites - -* Go has to be installed on the system being run. -* Prime needs to be accessible via ssh tunnel or otherwise from the host - where the script is being run. - - -##A typical invocation looks like this: - - -(The parameters below have correct lengths, but are otherwise bogus, -and will cause error messages.) - -``` - ./upload-sim-batch.go \ - -first-iccid 1234567678901234567689 \ - -last-iccid 1234567678901234567689 \ - -first-imsi 12345676789012345 \ - -last-imsi 12345676789012345 \ - -first-msisdn 12345676789012345 \ - -last-msisdn 12345676789012345 \ - -profile-type gargle-blaster-zot \ - -profile-vendor idemalto \ - -upload-hostname localhost \ - -upload-portnumber 8080 -``` - -##The full set of command line options - -``` - - -batch-length integer - The number of profiles in the batch. Must match with iccid, msisdn and imsi ranges (if present). - -first-iccid string - An 18 or 19 digit long string. The 19-th digit being a luhn luhnChecksum digit, if present (default "not a valid iccid") - -first-imsi string - First IMSI in batch (default "Not a valid IMSI") - -first-msisdn string - First MSISDN in batch (default "Not a valid MSISDN") - -hss-vendor string - The HSS vendor (default "M1") - -initial-hlr-activation-status-of-profiles string - Initial hss activation state. Legal values are ACTIVATED and NOT_ACTIVATED. (default "ACTIVATED") - -last-iccid string - An 18 or 19 digit long string. The 19-th digit being a luhn luhnChecksum digit, if present (default "not a valid iccid") - -last-imsi string - Last IMSI in batch (default "Not a valid IMSI") - -last-msisdn string - Last MSISDN in batch (default "Not a valid MSISDN") - -profile-type string - SIM profile type (default "Not a valid sim profile type") - -profile-vendor string - Vendor of SIM profiles (default "Idemia") - -upload-hostname string - host to upload batch to (default "localhost") - -upload-portnumber string - port to upload to (default "8080") - -``` From e6c74f0ada7ca045c0ec3b7cf83ec9b4156a804f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B8rn=20Remseth?= Date: Wed, 2 Oct 2019 12:50:39 +0200 Subject: [PATCH 017/173] Delete the wrong README.md --- sim-administration/README.md | 31 ------------------------------- 1 file changed, 31 deletions(-) delete mode 100644 sim-administration/README.md diff --git a/sim-administration/README.md b/sim-administration/README.md deleted file mode 100644 index 168b65331..000000000 --- a/sim-administration/README.md +++ /dev/null @@ -1,31 +0,0 @@ -# Sim logistics management - -This directory contains code for managing orders of SIM cards, -sending these orders to various actors, and injecting them into -various subsystems. - -It is currently in a state of flux, but will evolve in a bottom -up fashion driven by daily demands to perform the tasks the code -in this directory assists in. - -The code in this directory is written in golang. Currently there are two -go programmes here, both designed to be run from the command line. - - * outfile_to_hss_input_converter.go: Will convert output from - a SIM card vendor into input for a HSS vendor. - - * upload-sim-batch.go: Will from command line parameters generate a - bash script that will use curl to upload sim card parameters - to a "prime" instance. - -For both of these programmes, see the source code, in particular the -comments near the top of the files for instructions on how to use them. - - -# TODO -* Make a build command that runs tests and reports test coverage (etc), - make it part of the "build-all.go" script. - - - - From ed5af3ea5e50bb1173a2928e94b3e023dd951a82 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B8rn=20Remseth?= Date: Wed, 2 Oct 2019 12:55:30 +0200 Subject: [PATCH 018/173] Restore badness introduced by previous commits that removed the parser for output files --- .../outfile_to_hss_input_converter.go | 22 +++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/sim-administration/uploader/outfile_to_hss_input_converter.go b/sim-administration/uploader/outfile_to_hss_input_converter.go index 319abff3a..827c0cca7 100755 --- a/sim-administration/uploader/outfile_to_hss_input_converter.go +++ b/sim-administration/uploader/outfile_to_hss_input_converter.go @@ -44,7 +44,7 @@ import ( /// func main() { - inputFile, outputFilePrefix := parseCommandLine() + inputFile, outputFilePrefix := parseOutputToHssConverterCommandLine() fmt.Println("inputFile = ", inputFile) fmt.Println("outputFilePrefix = ", outputFilePrefix) @@ -55,7 +55,7 @@ func main() { err := WriteHssCsvFile(outputFile, outRecord.entries) if err != nil { - log.Fatal("Couldn't close output file '", outputFile, "'. Error = '", err, "'") + log.Fatal("Couldn't close output file '", outputFilePrefix, "'. Error = '", err, "'") } } @@ -107,6 +107,23 @@ type ParserState struct { /// Functions /// + +func parseOutputToHssConverterCommandLine() (string, string) { + inputFile := flag.String("input-file", + "not a valid filename", + "path to .out file used as input file") + + outputFile := flag.String("output-file-prefix", + "not a valid filename", + "prefix to path to .csv file used as input file, filename will be autogenerated") + + // + // Parse input according to spec above + // + flag.Parse() + return *inputFile, *outputFile +} + func ParseLineIntoKeyValueMap(line string, theMap map[string]string) { var splitString = strings.Split(line, ":") if len(splitString) != 2 { @@ -296,6 +313,7 @@ func isComment(s string) bool { } + // fileExists checks if a file exists and is not a directory before we // try using it to prevent further errors. func fileExists(filename string) bool { From 7c2b075cc316fd674ef2f4b54eb98ce1f62a91b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B8rn=20Remseth?= Date: Wed, 2 Oct 2019 12:56:27 +0200 Subject: [PATCH 019/173] Change name of directory to more accurately reflect what's going on in it. --- sim-administration/{uploader => sim-batch-management}/.gitignore | 0 sim-administration/{uploader => sim-batch-management}/README.md | 0 sim-administration/{uploader => sim-batch-management}/TODO.md | 0 .../{uploader => sim-batch-management}/luhn_test.go | 0 .../outfile_to_hss_input_converter.go | 0 .../outfile_to_hss_input_converter_test.go | 0 .../sample_out_file_for_testing.out | 0 .../{uploader => sim-batch-management}/upload-sim-batch.go | 0 8 files changed, 0 insertions(+), 0 deletions(-) rename sim-administration/{uploader => sim-batch-management}/.gitignore (100%) rename sim-administration/{uploader => sim-batch-management}/README.md (100%) rename sim-administration/{uploader => sim-batch-management}/TODO.md (100%) rename sim-administration/{uploader => sim-batch-management}/luhn_test.go (100%) rename sim-administration/{uploader => sim-batch-management}/outfile_to_hss_input_converter.go (100%) rename sim-administration/{uploader => sim-batch-management}/outfile_to_hss_input_converter_test.go (100%) rename sim-administration/{uploader => sim-batch-management}/sample_out_file_for_testing.out (100%) rename sim-administration/{uploader => sim-batch-management}/upload-sim-batch.go (100%) diff --git a/sim-administration/uploader/.gitignore b/sim-administration/sim-batch-management/.gitignore similarity index 100% rename from sim-administration/uploader/.gitignore rename to sim-administration/sim-batch-management/.gitignore diff --git a/sim-administration/uploader/README.md b/sim-administration/sim-batch-management/README.md similarity index 100% rename from sim-administration/uploader/README.md rename to sim-administration/sim-batch-management/README.md diff --git a/sim-administration/uploader/TODO.md b/sim-administration/sim-batch-management/TODO.md similarity index 100% rename from sim-administration/uploader/TODO.md rename to sim-administration/sim-batch-management/TODO.md diff --git a/sim-administration/uploader/luhn_test.go b/sim-administration/sim-batch-management/luhn_test.go similarity index 100% rename from sim-administration/uploader/luhn_test.go rename to sim-administration/sim-batch-management/luhn_test.go diff --git a/sim-administration/uploader/outfile_to_hss_input_converter.go b/sim-administration/sim-batch-management/outfile_to_hss_input_converter.go similarity index 100% rename from sim-administration/uploader/outfile_to_hss_input_converter.go rename to sim-administration/sim-batch-management/outfile_to_hss_input_converter.go diff --git a/sim-administration/uploader/outfile_to_hss_input_converter_test.go b/sim-administration/sim-batch-management/outfile_to_hss_input_converter_test.go similarity index 100% rename from sim-administration/uploader/outfile_to_hss_input_converter_test.go rename to sim-administration/sim-batch-management/outfile_to_hss_input_converter_test.go diff --git a/sim-administration/uploader/sample_out_file_for_testing.out b/sim-administration/sim-batch-management/sample_out_file_for_testing.out similarity index 100% rename from sim-administration/uploader/sample_out_file_for_testing.out rename to sim-administration/sim-batch-management/sample_out_file_for_testing.out diff --git a/sim-administration/uploader/upload-sim-batch.go b/sim-administration/sim-batch-management/upload-sim-batch.go similarity index 100% rename from sim-administration/uploader/upload-sim-batch.go rename to sim-administration/sim-batch-management/upload-sim-batch.go From 395f918d99ff59b1212416aade420dad623ad93e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B8rn=20Remseth?= Date: Wed, 2 Oct 2019 13:04:38 +0200 Subject: [PATCH 020/173] Remove duplcate trimSuffix function --- .../sim-batch-management/outfile_to_hss_input_converter.go | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/sim-administration/sim-batch-management/outfile_to_hss_input_converter.go b/sim-administration/sim-batch-management/outfile_to_hss_input_converter.go index 827c0cca7..c37a6d5c4 100755 --- a/sim-administration/sim-batch-management/outfile_to_hss_input_converter.go +++ b/sim-administration/sim-batch-management/outfile_to_hss_input_converter.go @@ -134,9 +134,7 @@ func ParseLineIntoKeyValueMap(line string, theMap map[string]string) { theMap[key] = value } -func trimSuffix(s string, suffixLen int) string { - return s[:len(s)-suffixLen] -} + func ReadOutputFile(filename string) OutputFileRecord { From 99b429f1ab20b9d4052f10ffb86cc6fd45aa5595 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B8rn=20Remseth?= Date: Wed, 2 Oct 2019 13:04:56 +0200 Subject: [PATCH 021/173] Whitespace --- .../sim-batch-management/outfile_to_hss_input_converter.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/sim-administration/sim-batch-management/outfile_to_hss_input_converter.go b/sim-administration/sim-batch-management/outfile_to_hss_input_converter.go index c37a6d5c4..57a93b8a5 100755 --- a/sim-administration/sim-batch-management/outfile_to_hss_input_converter.go +++ b/sim-administration/sim-batch-management/outfile_to_hss_input_converter.go @@ -134,8 +134,6 @@ func ParseLineIntoKeyValueMap(line string, theMap map[string]string) { theMap[key] = value } - - func ReadOutputFile(filename string) OutputFileRecord { _, err := os.Stat(filename) From 0e5d75d07db50e88abfa74c1dbb8a6a7af4fbbe2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B8rn=20Remseth?= Date: Wed, 2 Oct 2019 13:05:15 +0200 Subject: [PATCH 022/173] Adding readme --- .../README-upload-sim-batch.md | 92 +++++++++++++++++++ 1 file changed, 92 insertions(+) create mode 100644 sim-administration/sim-batch-management/README-upload-sim-batch.md diff --git a/sim-administration/sim-batch-management/README-upload-sim-batch.md b/sim-administration/sim-batch-management/README-upload-sim-batch.md new file mode 100644 index 000000000..26c815894 --- /dev/null +++ b/sim-administration/sim-batch-management/README-upload-sim-batch.md @@ -0,0 +1,92 @@ +# How to upload batch information to prime using the + +## Introduction +Prime has REST endpoint for uploading sim batches. This is an +interface with little error checking (beyond the bare miniumums) +and a low abstraction layer: It requires a CSV file of ICCID/IMSI/MSISDN/PROFILE tuples. + +This is convenient as a starting point, but in practice it has turned +out to be a little too simple, hence the script upload-sim-batch.go. + +This script takes assumes that there is already a way to talk HTTP +(no encryption) to the upload profiles. The default assumption is that +a tunnel has been set up from localhost:8080 to somewhere more +appropriate, but these coordinaters can be tweaked using command line +parameters. + +The basic REST interface assumes an incoming .csv file, but that is +bulky, and the information content is low. In practice we will +more often than not know the ranges of IMSI, ICCID and MSISDN numbers +involved, and obviously also the profile type names. The script can +take these values as parameters and generate a valid CSV file, and +upload it automatically. + +The parameters are checked for consistency, so that if there are +more MSISDNs than ICCIDs in the ranges given as parameters, for instance, +then the script will terminate with an error message. + +(these are reasonable things to check btw, errors have been made +that justifies adding these checks). + +##Prerequisites + +* Go has to be installed on the system being run. +* Prime needs to be accessible via ssh tunnel or otherwise from the host + where the script is being run. + + +##A typical invocation looks like this: + + +(The parameters below have correct lengths, but are otherwise bogus, +and will cause error messages.) + +``` + ./upload-sim-batch.go \ + -first-iccid 1234567678901234567689 \ + -last-iccid 1234567678901234567689 \ + -first-imsi 12345676789012345 \ + -last-imsi 12345676789012345 \ + -first-msisdn 12345676789012345 \ + -last-msisdn 12345676789012345 \ + -profile-type gargle-blaster-zot \ + -profile-vendor idemalto \ + -upload-hostname localhost \ + -upload-portnumber 8080 +``` + +##The full set of command line options + +``` + + -batch-length integer + The number of profiles in the batch. Must match with iccid, msisdn and imsi ranges (if present). + -first-iccid string + An 18 or 19 digit long string. The 19-th digit being a luhn luhnChecksum digit, if present (default "not a valid iccid") + -first-imsi string + First IMSI in batch (default "Not a valid IMSI") + -first-msisdn string + First MSISDN in batch (default "Not a valid MSISDN") + -hss-vendor string + The HSS vendor (default "M1") + -initial-hlr-activation-status-of-profiles string + Initial hss activation state. Legal values are ACTIVATED and NOT_ACTIVATED. (default "ACTIVATED") + -last-iccid string + An 18 or 19 digit long string. The 19-th digit being a luhn luhnChecksum digit, if present (default "not a valid iccid") + -last-imsi string + Last IMSI in batch (default "Not a valid IMSI") + -last-msisdn string + Last MSISDN in batch (default "Not a valid MSISDN") + -profile-type string + SIM profile type (default "Not a valid sim profile type") + -profile-vendor string + Vendor of SIM profiles (default "Idemia") + -upload-hostname string + host to upload batch to (default "localhost") + -upload-portnumber string + port to upload to (default "8080") + +``` + + + From 63a29f2a383e95ec2045142707735580fc34a5b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B8rn=20Remseth?= Date: Wed, 2 Oct 2019 13:06:16 +0200 Subject: [PATCH 023/173] No shell scripts in this directory .-) --- sim-administration/sim-batch-management/.gitignore | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sim-administration/sim-batch-management/.gitignore b/sim-administration/sim-batch-management/.gitignore index 129c50361..c97f963b3 100644 --- a/sim-administration/sim-batch-management/.gitignore +++ b/sim-administration/sim-batch-management/.gitignore @@ -1 +1 @@ -generate-digi-profiles.sh +*.sh From 2133f4982ec5eccb01e54eed087215f390c88968 Mon Sep 17 00:00:00 2001 From: Prasanth Ullattil Date: Wed, 2 Oct 2019 15:59:33 +0200 Subject: [PATCH 024/173] Filter out regions from GET /regions API --- .../org/ostelco/prime/model/Entities.kt | 1 + .../ostelco/prime/storage/graph/KtScripts.kt | 7 ++ .../prime/storage/graph/Neo4jModule.kt | 3 +- .../ostelco/prime/storage/graph/Neo4jStore.kt | 67 +++++++++++++++---- .../main/resources/AllowedRegionsService.kts | 16 +++++ .../kotlin/org/ostelco/prime/dsl/DSLTest.kt | 6 ++ .../prime/storage/graph/Neo4jLoadTest.kt | 6 ++ .../prime/storage/graph/Neo4jStoreTest.kt | 15 ++++- .../ostelco/prime/storage/graph/SchemaTest.kt | 6 ++ .../prime/storage/graph/Neo4jStorageTest.kt | 6 ++ 10 files changed, 116 insertions(+), 17 deletions(-) create mode 100644 neo4j-store/src/main/resources/AllowedRegionsService.kts diff --git a/model/src/main/kotlin/org/ostelco/prime/model/Entities.kt b/model/src/main/kotlin/org/ostelco/prime/model/Entities.kt index 27c8e883e..296754f50 100644 --- a/model/src/main/kotlin/org/ostelco/prime/model/Entities.kt +++ b/model/src/main/kotlin/org/ostelco/prime/model/Entities.kt @@ -62,6 +62,7 @@ data class RegionDetails( enum class CustomerRegionStatus { PENDING, // eKYC initiated, but not yet approved APPROVED, // eKYC approved + AVAILABLE // Region is available for provisioning } enum class KycType { diff --git a/neo4j-store/src/main/kotlin/org/ostelco/prime/storage/graph/KtScripts.kt b/neo4j-store/src/main/kotlin/org/ostelco/prime/storage/graph/KtScripts.kt index 763047839..fd1eaefb5 100644 --- a/neo4j-store/src/main/kotlin/org/ostelco/prime/storage/graph/KtScripts.kt +++ b/neo4j-store/src/main/kotlin/org/ostelco/prime/storage/graph/KtScripts.kt @@ -1,6 +1,7 @@ package org.ostelco.prime.storage.graph import arrow.core.Either +import org.ostelco.prime.model.Customer import org.ostelco.prime.model.Identity import org.ostelco.prime.storage.StoreError @@ -12,4 +13,10 @@ interface OnNewCustomerAction { fun apply(identity: Identity, customerId: String, transaction: PrimeTransaction): Either +} + +interface AllowedRegionsService { + fun get(identity: Identity, + customer: Customer, + transaction: PrimeTransaction): Either> } \ No newline at end of file diff --git a/neo4j-store/src/main/kotlin/org/ostelco/prime/storage/graph/Neo4jModule.kt b/neo4j-store/src/main/kotlin/org/ostelco/prime/storage/graph/Neo4jModule.kt index 87ec38394..431bcad03 100644 --- a/neo4j-store/src/main/kotlin/org/ostelco/prime/storage/graph/Neo4jModule.kt +++ b/neo4j-store/src/main/kotlin/org/ostelco/prime/storage/graph/Neo4jModule.kt @@ -41,7 +41,8 @@ data class Config( val host: String, val protocol: String, val hssNameLookupService: KtsServiceFactory, - val onNewCustomerAction: KtsServiceFactory) + val onNewCustomerAction: KtsServiceFactory, + val allowedRegionsService: KtsServiceFactory) object ConfigRegistry { lateinit var config: Config diff --git a/neo4j-store/src/main/kotlin/org/ostelco/prime/storage/graph/Neo4jStore.kt b/neo4j-store/src/main/kotlin/org/ostelco/prime/storage/graph/Neo4jStore.kt index 35b2761d0..a95a63c37 100644 --- a/neo4j-store/src/main/kotlin/org/ostelco/prime/storage/graph/Neo4jStore.kt +++ b/neo4j-store/src/main/kotlin/org/ostelco/prime/storage/graph/Neo4jStore.kt @@ -41,6 +41,7 @@ import org.ostelco.prime.model.ChangeSegment import org.ostelco.prime.model.Customer import org.ostelco.prime.model.CustomerRegionStatus import org.ostelco.prime.model.CustomerRegionStatus.APPROVED +import org.ostelco.prime.model.CustomerRegionStatus.AVAILABLE import org.ostelco.prime.model.CustomerRegionStatus.PENDING import org.ostelco.prime.model.FCMStrings import org.ostelco.prime.model.HasId @@ -103,6 +104,7 @@ import org.ostelco.prime.storage.graph.ConfigRegistry.config import org.ostelco.prime.storage.graph.Graph.read import org.ostelco.prime.storage.graph.Graph.write import org.ostelco.prime.storage.graph.Graph.writeSuspended +import org.ostelco.prime.storage.graph.Neo4jStoreSingleton.getCustomerId import org.ostelco.prime.storage.graph.Relation.BELONG_TO_SEGMENT import org.ostelco.prime.storage.graph.Relation.HAS_BUNDLE import org.ostelco.prime.storage.graph.Relation.HAS_SIM_PROFILE @@ -291,6 +293,7 @@ object Neo4jStoreSingleton : GraphStore { private val hssNameLookup: HssNameLookupService = config.hssNameLookupService.getKtsService() private val onNewCustomerAction: OnNewCustomerAction = config.onNewCustomerAction.getKtsService() + private val allowedRegionsService: AllowedRegionsService = config.allowedRegionsService.getKtsService() // ------------- // Client Store @@ -415,9 +418,14 @@ object Neo4jStoreSingleton : GraphStore { override fun getAllRegionDetails(identity: org.ostelco.prime.model.Identity): Either> = readTransaction { getCustomerId(identity = identity) .flatMap { customerId -> - getRegionDetails( - customerId = customerId, - transaction = transaction).right() + getAllowedRegions(identity, transaction).map { allowedRegions -> + val allRegions = getAvailableRegionDetails(transaction) + val customerRegions = getRegionDetails( + customerId = customerId, + transaction = transaction) + combineRegions(allRegions, customerRegions) + .filter { allowedRegions.contains(it.region.id) } + } } } @@ -427,13 +435,22 @@ object Neo4jStoreSingleton : GraphStore { getCustomerId(identity = identity) .flatMap { customerId -> - getRegionDetails( - customerId = customerId, - regionCode = regionCode, - transaction = transaction) - .singleOrNull() - ?.right() - ?: NotFoundError(type = customerRegionRelation.name, id = "$customerId -> $regionCode").left() + getAllowedRegions(identity, transaction).flatMap { allowedRegions -> + getRegionDetails( + customerId = customerId, + regionCode = regionCode, + transaction = transaction).singleOrNull { allowedRegions.contains(it.region.id) } + ?.right() + ?: NotFoundError(type = customerRegionRelation.name, id = "$customerId -> $regionCode").left() + } + } + } + + private fun getAllowedRegions(identity: org.ostelco.prime.model.Identity, primeTransaction: PrimeTransaction): Either> { + return getCustomer(identity = identity) + .flatMap { customer -> + logger.info("contact email = ${customer.contactEmail}") + allowedRegionsService.get(identity = identity, customer = customer, transaction = primeTransaction) } } @@ -498,6 +515,28 @@ object Neo4jStoreSingleton : GraphStore { } } + private fun getAvailableRegionDetails(transaction: Transaction): Collection { + val query = "MATCH (r:${regionEntity.name}) RETURN r;" + return read(query, transaction) { it -> + it.list { record -> + val region = regionEntity.createEntity(record["r"].asMap()) + RegionDetails( + region = region, + status = AVAILABLE, + kycStatusMap = getKycStatusMapForRegion(region.id.toLowerCase()), + simProfiles = emptyList()) + }.requireNoNulls() + } + } + + private fun combineRegions(allRegions: Collection, customerRegions: Collection): Collection { + var combined = allRegions.associate { it.region.id to it }.toMutableMap() + for (details in customerRegions) { + combined.replace(details.region.id, details) + } + return combined.values + } + // // SIM Profile // @@ -1685,9 +1724,9 @@ object Neo4jStoreSingleton : GraphStore { logger.error("Failed to fetched MyInfo $version using authCode = $authorisationCode", e) null } ?: SystemError( - type = "MyInfo Auth Code", - id = authorisationCode, - message = "Failed to fetched MyInfo $version").left().bind() + type = "MyInfo Auth Code", + id = authorisationCode, + message = "Failed to fetched MyInfo $version").left().bind() // TODO vihang: Should we set status for NRIC_FIN to APPROVED? @@ -1847,7 +1886,7 @@ object Neo4jStoreSingleton : GraphStore { val existingKycStatusMap = existingCustomerRegion.kycStatusMap val existingKycStatus = existingKycStatusMap[kycType] - val newKycStatus = when(existingKycStatus) { + val newKycStatus = when (existingKycStatus) { // APPROVED is end state. No more state change. KycStatus.APPROVED -> KycStatus.APPROVED // REJECTED and PENDING to 'any' is allowed diff --git a/neo4j-store/src/main/resources/AllowedRegionsService.kts b/neo4j-store/src/main/resources/AllowedRegionsService.kts new file mode 100644 index 000000000..0ce8f3789 --- /dev/null +++ b/neo4j-store/src/main/resources/AllowedRegionsService.kts @@ -0,0 +1,16 @@ +import arrow.core.Either +import arrow.core.right +import org.ostelco.prime.model.Customer +import org.ostelco.prime.model.Identity +import org.ostelco.prime.storage.StoreError +import org.ostelco.prime.storage.graph.AllowedRegionsService +import org.ostelco.prime.storage.graph.PrimeTransaction + +object : AllowedRegionsService { + override fun get(identity: Identity, customer: Customer, transaction: PrimeTransaction): Either> { + return if (customer.contactEmail.toLowerCase().endsWith("@bar.com")) + listOf("no", "sg", "us", "my").right() + else + listOf("sg", "us", "my").right() + } +} \ No newline at end of file diff --git a/neo4j-store/src/test/kotlin/org/ostelco/prime/dsl/DSLTest.kt b/neo4j-store/src/test/kotlin/org/ostelco/prime/dsl/DSLTest.kt index dc8383806..d03227982 100644 --- a/neo4j-store/src/test/kotlin/org/ostelco/prime/dsl/DSLTest.kt +++ b/neo4j-store/src/test/kotlin/org/ostelco/prime/dsl/DSLTest.kt @@ -92,6 +92,12 @@ class DSLTest { textReader = ClasspathResourceTextReader( filename = "/OnNewCustomerAction.kts" ) + ), + allowedRegionsService = KtsServiceFactory( + serviceInterface = "org.ostelco.prime.storage.graph.AllowedRegionsService", + textReader = ClasspathResourceTextReader( + filename = "/AllowedRegionsService.kts" + ) ) ) Neo4jClient.start() diff --git a/neo4j-store/src/test/kotlin/org/ostelco/prime/storage/graph/Neo4jLoadTest.kt b/neo4j-store/src/test/kotlin/org/ostelco/prime/storage/graph/Neo4jLoadTest.kt index 707c20b29..8fe987532 100644 --- a/neo4j-store/src/test/kotlin/org/ostelco/prime/storage/graph/Neo4jLoadTest.kt +++ b/neo4j-store/src/test/kotlin/org/ostelco/prime/storage/graph/Neo4jLoadTest.kt @@ -167,6 +167,12 @@ class Neo4jLoadTest { textReader = ClasspathResourceTextReader( filename = "/OnNewCustomerAction.kts" ) + ), + allowedRegionsService = KtsServiceFactory( + serviceInterface = "org.ostelco.prime.storage.graph.AllowedRegionsService", + textReader = ClasspathResourceTextReader( + filename = "/AllowedRegionsService.kts" + ) ) ) Neo4jClient.start() diff --git a/neo4j-store/src/test/kotlin/org/ostelco/prime/storage/graph/Neo4jStoreTest.kt b/neo4j-store/src/test/kotlin/org/ostelco/prime/storage/graph/Neo4jStoreTest.kt index 29f5f8a2b..0f50bca20 100644 --- a/neo4j-store/src/test/kotlin/org/ostelco/prime/storage/graph/Neo4jStoreTest.kt +++ b/neo4j-store/src/test/kotlin/org/ostelco/prime/storage/graph/Neo4jStoreTest.kt @@ -19,6 +19,7 @@ import org.ostelco.prime.kts.engine.reader.ClasspathResourceTextReader import org.ostelco.prime.model.Customer import org.ostelco.prime.model.CustomerRegionStatus.APPROVED import org.ostelco.prime.model.CustomerRegionStatus.PENDING +import org.ostelco.prime.model.CustomerRegionStatus.AVAILABLE import org.ostelco.prime.model.Identity import org.ostelco.prime.model.JumioScanData import org.ostelco.prime.model.KycStatus @@ -706,8 +707,12 @@ class Neo4jStoreTest { // test Neo4jStoreSingleton.getAllRegionDetails(identity = IDENTITY) .bimap( - { fail("Failed to fetch regions empty list") }, - { assert(it.isEmpty()) { "Regions list should be empty" } }) + { fail("Failed to fetch regions list") }, + { + for (region in it) { + assert(region.status == AVAILABLE) { "All regions should be marked available" } + } + }) } @Test @@ -1087,6 +1092,12 @@ class Neo4jStoreTest { textReader = ClasspathResourceTextReader( filename = "/OnNewCustomerAction.kts" ) + ), + allowedRegionsService = KtsServiceFactory( + serviceInterface = "org.ostelco.prime.storage.graph.AllowedRegionsService", + textReader = ClasspathResourceTextReader( + filename = "/AllowedRegionsService.kts" + ) ) ) Neo4jClient.start() diff --git a/neo4j-store/src/test/kotlin/org/ostelco/prime/storage/graph/SchemaTest.kt b/neo4j-store/src/test/kotlin/org/ostelco/prime/storage/graph/SchemaTest.kt index cd9a0fd04..cc8b74448 100644 --- a/neo4j-store/src/test/kotlin/org/ostelco/prime/storage/graph/SchemaTest.kt +++ b/neo4j-store/src/test/kotlin/org/ostelco/prime/storage/graph/SchemaTest.kt @@ -269,6 +269,12 @@ class SchemaTest { textReader = ClasspathResourceTextReader( filename = "/OnNewCustomerAction.kts" ) + ), + allowedRegionsService = KtsServiceFactory( + serviceInterface = "org.ostelco.prime.storage.graph.AllowedRegionsService", + textReader = ClasspathResourceTextReader( + filename = "/AllowedRegionsService.kts" + ) ) ) Neo4jClient.start() diff --git a/prime/src/integration-test/kotlin/org/ostelco/prime/storage/graph/Neo4jStorageTest.kt b/prime/src/integration-test/kotlin/org/ostelco/prime/storage/graph/Neo4jStorageTest.kt index a82db2bf9..307273a19 100644 --- a/prime/src/integration-test/kotlin/org/ostelco/prime/storage/graph/Neo4jStorageTest.kt +++ b/prime/src/integration-test/kotlin/org/ostelco/prime/storage/graph/Neo4jStorageTest.kt @@ -136,6 +136,12 @@ class Neo4jStorageTest { textReader = ClasspathResourceTextReader( filename = "/OnNewCustomerAction.kts" ) + ), + allowedRegionsService = KtsServiceFactory( + serviceInterface = "org.ostelco.prime.storage.graph.AllowedRegionsService", + textReader = ClasspathResourceTextReader( + filename = "/AllowedRegionsService.kts" + ) ) ) From 832f6f28620d727054ac056506c6f7c476f8ecdc Mon Sep 17 00:00:00 2001 From: Prasanth Ullattil Date: Wed, 2 Oct 2019 23:30:49 +0200 Subject: [PATCH 025/173] Minor code cleanup --- .../ostelco/prime/storage/graph/Neo4jStore.kt | 29 ++++++++++--------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/neo4j-store/src/main/kotlin/org/ostelco/prime/storage/graph/Neo4jStore.kt b/neo4j-store/src/main/kotlin/org/ostelco/prime/storage/graph/Neo4jStore.kt index a95a63c37..a40468acf 100644 --- a/neo4j-store/src/main/kotlin/org/ostelco/prime/storage/graph/Neo4jStore.kt +++ b/neo4j-store/src/main/kotlin/org/ostelco/prime/storage/graph/Neo4jStore.kt @@ -104,7 +104,6 @@ import org.ostelco.prime.storage.graph.ConfigRegistry.config import org.ostelco.prime.storage.graph.Graph.read import org.ostelco.prime.storage.graph.Graph.write import org.ostelco.prime.storage.graph.Graph.writeSuspended -import org.ostelco.prime.storage.graph.Neo4jStoreSingleton.getCustomerId import org.ostelco.prime.storage.graph.Relation.BELONG_TO_SEGMENT import org.ostelco.prime.storage.graph.Relation.HAS_BUNDLE import org.ostelco.prime.storage.graph.Relation.HAS_SIM_PROFILE @@ -418,13 +417,13 @@ object Neo4jStoreSingleton : GraphStore { override fun getAllRegionDetails(identity: org.ostelco.prime.model.Identity): Either> = readTransaction { getCustomerId(identity = identity) .flatMap { customerId -> - getAllowedRegions(identity, transaction).map { allowedRegions -> + getAllowedRegionIds(identity, transaction).map { allowedIds -> val allRegions = getAvailableRegionDetails(transaction) val customerRegions = getRegionDetails( customerId = customerId, transaction = transaction) combineRegions(allRegions, customerRegions) - .filter { allowedRegions.contains(it.region.id) } + .filter { allowedIds.contains(it.region.id) } } } } @@ -435,23 +434,22 @@ object Neo4jStoreSingleton : GraphStore { getCustomerId(identity = identity) .flatMap { customerId -> - getAllowedRegions(identity, transaction).flatMap { allowedRegions -> + getAllowedRegionIds(identity, transaction).flatMap { allowedIds -> getRegionDetails( customerId = customerId, regionCode = regionCode, - transaction = transaction).singleOrNull { allowedRegions.contains(it.region.id) } + transaction = transaction).singleOrNull { allowedIds.contains(it.region.id) } ?.right() ?: NotFoundError(type = customerRegionRelation.name, id = "$customerId -> $regionCode").left() } } } - private fun getAllowedRegions(identity: org.ostelco.prime.model.Identity, primeTransaction: PrimeTransaction): Either> { - return getCustomer(identity = identity) - .flatMap { customer -> - logger.info("contact email = ${customer.contactEmail}") - allowedRegionsService.get(identity = identity, customer = customer, transaction = primeTransaction) - } + // Retrieve the list of allowed region Ids from AllowedRegionsService + private fun getAllowedRegionIds( + identity: org.ostelco.prime.model.Identity, + transaction: PrimeTransaction): Either> = getCustomer(identity).flatMap { + allowedRegionsService.get(identity, it, transaction) } private fun getRegionDetails( @@ -516,6 +514,7 @@ object Neo4jStoreSingleton : GraphStore { } private fun getAvailableRegionDetails(transaction: Transaction): Collection { + // Make list of details using regions in present in graphDB with default values val query = "MATCH (r:${regionEntity.name}) RETURN r;" return read(query, transaction) { it -> it.list { record -> @@ -530,9 +529,11 @@ object Neo4jStoreSingleton : GraphStore { } private fun combineRegions(allRegions: Collection, customerRegions: Collection): Collection { - var combined = allRegions.associate { it.region.id to it }.toMutableMap() - for (details in customerRegions) { - combined.replace(details.region.id, details) + // Create a map with default region details + var combined = allRegions.associateBy { it.region.id }.toMutableMap() + // Overwrite default region details with items from actual region-relations for customer + customerRegions.forEach { + combined[it.region.id] = it } return combined.values } From a0093800915bf52077bb27ad47a9da20bdd581ec Mon Sep 17 00:00:00 2001 From: Prasanth Ullattil Date: Thu, 3 Oct 2019 09:39:42 +0200 Subject: [PATCH 026/173] Add domains we test to the list of regions --- neo4j-store/src/main/resources/AllowedRegionsService.kts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/neo4j-store/src/main/resources/AllowedRegionsService.kts b/neo4j-store/src/main/resources/AllowedRegionsService.kts index 0ce8f3789..07a657380 100644 --- a/neo4j-store/src/main/resources/AllowedRegionsService.kts +++ b/neo4j-store/src/main/resources/AllowedRegionsService.kts @@ -8,9 +8,11 @@ import org.ostelco.prime.storage.graph.PrimeTransaction object : AllowedRegionsService { override fun get(identity: Identity, customer: Customer, transaction: PrimeTransaction): Either> { - return if (customer.contactEmail.toLowerCase().endsWith("@bar.com")) + val allowedEmailDomains = listOf("@bar.com", "@redotter.sg", "@test.com") + val matchedDomains = allowedEmailDomains.filter { customer.contactEmail.toLowerCase().endsWith(it) } + return if (matchedDomains.size > 0) listOf("no", "sg", "us", "my").right() else - listOf("sg", "us", "my").right() + listOf("us", "my").right() } } \ No newline at end of file From b63bd70935f8636f24afe45e5523912439b80af9 Mon Sep 17 00:00:00 2001 From: Prasanth Ullattil Date: Thu, 3 Oct 2019 09:40:24 +0200 Subject: [PATCH 027/173] Refer the kts file in prime config --- prime/config/config.yaml | 5 +++++ prime/config/test.yaml | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/prime/config/config.yaml b/prime/config/config.yaml index e13b9a8cd..b935d94d9 100644 --- a/prime/config/config.yaml +++ b/prime/config/config.yaml @@ -70,6 +70,11 @@ modules: textReader: type: file filename: /config-data/OnNewCustomerAction.kts + allowedRegionsService: + serviceInterface: org.ostelco.prime.storage.graph.AllowedRegionsService + textReader: + type: file + filename: /config-data/AllowedRegionsService.kts - type: analytics config: projectId: ${GCP_PROJECT_ID} diff --git a/prime/config/test.yaml b/prime/config/test.yaml index 4c7f9a4de..fbe3c5706 100644 --- a/prime/config/test.yaml +++ b/prime/config/test.yaml @@ -55,6 +55,11 @@ modules: textReader: type: classpathResource filename: /OnNewCustomerAction.kts + allowedRegionsService: + serviceInterface: org.ostelco.prime.storage.graph.AllowedRegionsService + textReader: + type: classpathResource + filename: /AllowedRegionsService.kts - type: analytics config: projectId: ${GCP_PROJECT_ID} From 49a109ae271223548bdaea0b3b8041ea0706f317 Mon Sep 17 00:00:00 2001 From: Prasanth Ullattil Date: Thu, 3 Oct 2019 10:31:17 +0200 Subject: [PATCH 028/173] Add the new enum value --- prime/infra/dev/prime-customer-api.yaml | 2 +- prime/infra/prod/prime-customer-api.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/prime/infra/dev/prime-customer-api.yaml b/prime/infra/dev/prime-customer-api.yaml index ca32a6371..4a01c61c2 100644 --- a/prime/infra/dev/prime-customer-api.yaml +++ b/prime/infra/dev/prime-customer-api.yaml @@ -875,7 +875,7 @@ definitions: status: description: "Customer Status for this region" type: string - enum: [ PENDING, APPROVED ] + enum: [ PENDING, APPROVED, AVAILABLE ] kycStatusMap: description: "Map of status for each KYC" type: object diff --git a/prime/infra/prod/prime-customer-api.yaml b/prime/infra/prod/prime-customer-api.yaml index 410960ea9..54d0df9bd 100644 --- a/prime/infra/prod/prime-customer-api.yaml +++ b/prime/infra/prod/prime-customer-api.yaml @@ -875,7 +875,7 @@ definitions: status: description: "Customer Status for this region" type: string - enum: [ PENDING, APPROVED ] + enum: [ PENDING, APPROVED, AVAILABLE ] kycStatusMap: description: "Map of status for each KYC" type: object From cb9a30bbb5fd8cb2909bc227f52902055ed9d048 Mon Sep 17 00:00:00 2001 From: Prasanth Ullattil Date: Thu, 3 Oct 2019 10:31:32 +0200 Subject: [PATCH 029/173] Update test to allow all regions to be returned. --- .../src/main/kotlin/org/ostelco/at/jersey/Tests.kt | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/acceptance-tests/src/main/kotlin/org/ostelco/at/jersey/Tests.kt b/acceptance-tests/src/main/kotlin/org/ostelco/at/jersey/Tests.kt index 7fed150b1..a7b065aab 100644 --- a/acceptance-tests/src/main/kotlin/org/ostelco/at/jersey/Tests.kt +++ b/acceptance-tests/src/main/kotlin/org/ostelco/at/jersey/Tests.kt @@ -31,6 +31,7 @@ import org.ostelco.prime.customer.model.Region import org.ostelco.prime.customer.model.RegionDetails import org.ostelco.prime.customer.model.RegionDetails.StatusEnum.APPROVED import org.ostelco.prime.customer.model.RegionDetails.StatusEnum.PENDING +import org.ostelco.prime.customer.model.RegionDetails.StatusEnum.AVAILABLE import org.ostelco.prime.customer.model.RegionDetailsList import org.ostelco.prime.customer.model.ScanInformation import org.ostelco.prime.customer.model.SimProfile @@ -192,8 +193,9 @@ class RegionsTest { path = "/regions" this.email = email } - - assertTrue(regionDetailsList.isEmpty(), "RegionDetails list for new customer should be empty") + regionDetailsList.forEach { + assertTrue(it.status == AVAILABLE, "All regions should be in available state") + } } finally { StripePayment.deleteCustomer(customerId = customerId) } From 60c0a1d9065f9b9527e9555175639a6bb2364604 Mon Sep 17 00:00:00 2001 From: mpeterss Date: Thu, 3 Oct 2019 12:06:35 +0200 Subject: [PATCH 030/173] Refactor activation in the PubSub DataSource Making it more usable for MultiDataSource implementation --- .../datasource/protobuf/GrpcDataSource.java | 2 +- .../datasource/protobuf/PubSubDataSource.kt | 49 ++++++++++--------- 2 files changed, 27 insertions(+), 24 deletions(-) diff --git a/ocsgw/src/main/java/org/ostelco/ocsgw/datasource/protobuf/GrpcDataSource.java b/ocsgw/src/main/java/org/ostelco/ocsgw/datasource/protobuf/GrpcDataSource.java index 7fe7194c4..bdd4f5746 100644 --- a/ocsgw/src/main/java/org/ostelco/ocsgw/datasource/protobuf/GrpcDataSource.java +++ b/ocsgw/src/main/java/org/ostelco/ocsgw/datasource/protobuf/GrpcDataSource.java @@ -252,7 +252,7 @@ private void reconnectStreams() { if (!isReconnecting()) { reconnectStreamFuture = executorService.schedule((Callable) () -> { - LOG.debug("Reconnecting GRPC streams"); + LOG.debug("Reconnecting gRPC streams"); setupChannel(); initCreditControlRequestStream(); setupEventConsumer(); diff --git a/ocsgw/src/main/java/org/ostelco/ocsgw/datasource/protobuf/PubSubDataSource.kt b/ocsgw/src/main/java/org/ostelco/ocsgw/datasource/protobuf/PubSubDataSource.kt index a97a14eca..7535626e3 100644 --- a/ocsgw/src/main/java/org/ostelco/ocsgw/datasource/protobuf/PubSubDataSource.kt +++ b/ocsgw/src/main/java/org/ostelco/ocsgw/datasource/protobuf/PubSubDataSource.kt @@ -51,29 +51,11 @@ class PubSubDataSource( pubSubChannelProvider = FixedTransportChannelProvider.create(GrpcTransportChannel.create(channel)) } - // init publisher - logger.info("Setting up Publisher for pubsub Topic: {}", ccrTopicId) publisher = setupPublisherToTopic(projectId, ccrTopicId) + initCcrKeepAlive() + setupCcaReceiver(projectId, ccaSubscriptionId) - // Instantiate an asynchronous message receiver - setupPubSubSubscriber(projectId, ccaSubscriptionId) { message, consumer -> - // handle incoming message, then ack/nack the received message - val ccaInfo = CreditControlAnswerInfo.parseFrom(message) - if (ccaInfo.resultCode != ResultCode.UNKNOWN) { - logger.info("Pubsub received CreditControlAnswer for msisdn {} sessionId [{}]", ccaInfo.msisdn, ccaInfo.requestId) - protobufDataSource.handleCcrAnswer(ccaInfo) - } - consumer.ack() - } - - setupPubSubSubscriber(projectId, activateSubscriptionId) { message, consumer -> - // handle incoming message, then ack/nack the received message - protobufDataSource.handleActivateResponse( - ActivateResponse.parseFrom(message)) - consumer.ack() - } - - initKeepAlive() + setupActivateReceiver(projectId, activateSubscriptionId) } override fun init() { @@ -93,6 +75,27 @@ class PubSubDataSource( override fun isBlocked(msisdn: String): Boolean = protobufDataSource.isBlocked(msisdn) + + private fun setupCcaReceiver(projectId: String, ccaSubscriptionId: String) { + // Instantiate an asynchronous message receiver + setupPubSubSubscriber(projectId, ccaSubscriptionId) { message, consumer -> + val ccaInfo = CreditControlAnswerInfo.parseFrom(message) + if (ccaInfo.resultCode != ResultCode.UNKNOWN) { + logger.info("Pubsub received CreditControlAnswer for msisdn {} sessionId [{}]", ccaInfo.msisdn, ccaInfo.requestId) + protobufDataSource.handleCcrAnswer(ccaInfo) + } + consumer.ack() + } + } + + private fun setupActivateReceiver(projectId: String, activateSubscriptionId: String) { + setupPubSubSubscriber(projectId, activateSubscriptionId) { message, consumer -> + protobufDataSource.handleActivateResponse( + ActivateResponse.parseFrom(message)) + consumer.ack() + } + } + private fun sendRequest(creditControlRequestInfo : CreditControlRequestInfo) { val base64String = Base64.getEncoder().encodeToString( creditControlRequestInfo.toByteArray()) @@ -177,10 +180,10 @@ class PubSubDataSource( } /** - * The keep alive messages are sent so the stream is always active- + * The keep alive messages are sent so the stream is always active * This to keep latency low. */ - private fun initKeepAlive() { + private fun initCcrKeepAlive() { // this is used to keep low latency on the connection singleThreadScheduledExecutor.scheduleWithFixedDelay({ val ccrInfo = CreditControlRequestInfo.newBuilder() From 93d101e7de8f4b5d359747681db6a9ba63735abe Mon Sep 17 00:00:00 2001 From: Prasanth Ullattil Date: Thu, 3 Oct 2019 13:51:55 +0200 Subject: [PATCH 031/173] Fix AT to comply with changes in the /regions API --- .../kotlin/org/ostelco/at/jersey/Tests.kt | 209 +++++++++++------- .../kotlin/org/ostelco/at/okhttp/Tests.kt | 67 ++++-- 2 files changed, 172 insertions(+), 104 deletions(-) diff --git a/acceptance-tests/src/main/kotlin/org/ostelco/at/jersey/Tests.kt b/acceptance-tests/src/main/kotlin/org/ostelco/at/jersey/Tests.kt index a7b065aab..dba284282 100644 --- a/acceptance-tests/src/main/kotlin/org/ostelco/at/jersey/Tests.kt +++ b/acceptance-tests/src/main/kotlin/org/ostelco/at/jersey/Tests.kt @@ -210,12 +210,12 @@ class RegionsTest { customerId = createCustomer(name = "Test Single Region User", email = email).id enableRegion(email = email) - val regionDetailsList: Collection = get { + val regionDetailsList: RegionDetailsList = get { path = "/regions" this.email = email } - - assertEquals(1, regionDetailsList.size, "Customer should have one region") + val noRegionIndex = regionDetailsList.indexOfFirst { it.region.id == "no" } + assertTrue(noRegionIndex != -1, "regionDetailsList should contain 'no' region") val regionDetails = RegionDetails() .region(Region().id("no").name("Norway")) @@ -223,7 +223,7 @@ class RegionsTest { .kycStatusMap(mapOf(KycType.JUMIO.name to KycStatus.APPROVED)) .simProfiles(SimProfileList()) - assertEquals(regionDetails, regionDetailsList.single(), "RegionDetails do not match") + assertEquals(regionDetails, regionDetailsList[noRegionIndex], "RegionDetails do not match") } finally { StripePayment.deleteCustomer(customerId = customerId) } @@ -825,18 +825,21 @@ class JumioKycTest { } assertNotNull(scanInfo.scanId, message = "Failed to get new scanId") - val regionDetails = get { + val regionDetailsList = get { path = "/regions" this.email = email - }.single() + } + + val noRegionIndex = regionDetailsList.indexOfFirst { it.region.id == "no" } + assertTrue(noRegionIndex != -1, "Did not find Norway region") - assertEquals(Region().id("no").name("Norway"), regionDetails.region) - assertEquals(PENDING, regionDetails.status, message = "Wrong State") + assertEquals(Region().id("no").name("Norway"), regionDetailsList[noRegionIndex].region) + assertEquals(PENDING, regionDetailsList[noRegionIndex].status, message = "Wrong State") assertEquals( expected = mapOf( KycType.JUMIO.name to KycStatus.PENDING), - actual = regionDetails.kycStatusMap) + actual = regionDetailsList[noRegionIndex].kycStatusMap) } finally { StripePayment.deleteCustomer(customerId = customerId) @@ -875,18 +878,22 @@ class JumioKycTest { body = dataMap } - val regionDetails = get> { + val regionDetailsList = get { path = "/regions" this.email = email - }.single() + } + - assertEquals(Region().id("no").name("Norway"), regionDetails.region) - assertEquals(PENDING, regionDetails.status, message = "Wrong State") + val noRegionIndex = regionDetailsList.indexOfFirst { it.region.id == "no" } + assertTrue(noRegionIndex != -1, "Did not find Norway region") + + assertEquals(Region().id("no").name("Norway"), regionDetailsList[noRegionIndex].region) + assertEquals(PENDING, regionDetailsList[noRegionIndex].status, message = "Wrong State") assertEquals( expected = mapOf( KycType.JUMIO.name to KycStatus.REJECTED), - actual = regionDetails.kycStatusMap) + actual = regionDetailsList[noRegionIndex].kycStatusMap) } finally { StripePayment.deleteCustomer(customerId = customerId) @@ -927,18 +934,21 @@ class JumioKycTest { body = dataMap } - val regionDetails = get> { + val regionDetailsList = get { path = "/regions" this.email = email - }.single() + } - assertEquals(Region().id("no").name("Norway"), regionDetails.region) - assertEquals(APPROVED, regionDetails.status, message = "Wrong State") + val noRegionIndex = regionDetailsList.indexOfFirst { it.region.id == "no" } + assertTrue(noRegionIndex != -1, "Did not find Norway region") + + assertEquals(Region().id("no").name("Norway"), regionDetailsList[noRegionIndex].region) + assertEquals(APPROVED, regionDetailsList[noRegionIndex].status, message = "Wrong State") assertEquals( expected = mapOf( KycType.JUMIO.name to KycStatus.APPROVED), - actual = regionDetails.kycStatusMap) + actual = regionDetailsList[noRegionIndex].kycStatusMap) } finally { StripePayment.deleteCustomer(customerId = customerId) @@ -979,17 +989,20 @@ class JumioKycTest { body = dataMap } - val regionDetails = get> { + val regionDetailsList = get { path = "/regions" this.email = email - }.single() + } - assertEquals(Region().id("no").name("Norway"), regionDetails.region) - assertEquals(PENDING, regionDetails.status, message = "Wrong State") + val noRegionIndex = regionDetailsList.indexOfFirst { it.region.id == "no" } + assertTrue(noRegionIndex != -1, "Did not find Norway region") + + assertEquals(Region().id("no").name("Norway"), regionDetailsList[noRegionIndex].region) + assertEquals(PENDING, regionDetailsList[noRegionIndex].status, message = "Wrong State") assertEquals( expected = mapOf(KycType.JUMIO.name to KycStatus.REJECTED), - actual = regionDetails.kycStatusMap) + actual = regionDetailsList[noRegionIndex].kycStatusMap) } finally { StripePayment.deleteCustomer(customerId = customerId) @@ -1028,17 +1041,20 @@ class JumioKycTest { body = dataMap } - val regionDetails = get> { + val regionDetailsList = get { path = "/regions" this.email = email - }.single() + } - assertEquals(Region().id("no").name("Norway"), regionDetails.region) - assertEquals(PENDING, regionDetails.status, message = "Wrong State") + val noRegionIndex = regionDetailsList.indexOfFirst { it.region.id == "no" } + assertTrue(noRegionIndex != -1, "Did not find Norway region") + + assertEquals(Region().id("no").name("Norway"), regionDetailsList[noRegionIndex].region) + assertEquals(PENDING, regionDetailsList[noRegionIndex].status, message = "Wrong State") assertEquals( expected = mapOf(KycType.JUMIO.name to KycStatus.PENDING), - actual = regionDetails.kycStatusMap) + actual = regionDetailsList[noRegionIndex].kycStatusMap) } finally { StripePayment.deleteCustomer(customerId = customerId) @@ -1077,16 +1093,19 @@ class JumioKycTest { body = dataMap } - val regionDetails = get> { + val regionDetailsList = get { path = "/regions" this.email = email - }.single() + } - assertEquals(Region().id("no").name("Norway"), regionDetails.region) - assertEquals(PENDING, regionDetails.status, message = "Wrong State") + val noRegionIndex = regionDetailsList.indexOfFirst { it.region.id == "no" } + assertTrue(noRegionIndex != -1, "Did not find Norway region") + + assertEquals(Region().id("no").name("Norway"), regionDetailsList[noRegionIndex].region) + assertEquals(PENDING, regionDetailsList[noRegionIndex].status, message = "Wrong State") assertEquals( expected = mapOf(KycType.JUMIO.name to KycStatus.REJECTED), - actual = regionDetails.kycStatusMap) + actual = regionDetailsList[noRegionIndex].kycStatusMap) } finally { StripePayment.deleteCustomer(customerId = customerId) @@ -1125,16 +1144,19 @@ class JumioKycTest { body = dataMap } - val regionDetails = get> { + val regionDetailsList = get { path = "/regions" this.email = email - }.single() + } - assertEquals(Region().id("no").name("Norway"), regionDetails.region) - assertEquals(PENDING, regionDetails.status, message = "Wrong State") + var noRegionIndex = regionDetailsList.indexOfFirst { it.region.id == "no" } + assertTrue(noRegionIndex != -1, "Did not find Norway region") + + assertEquals(Region().id("no").name("Norway"), regionDetailsList[noRegionIndex].region) + assertEquals(PENDING, regionDetailsList[noRegionIndex].status, message = "Wrong State") assertEquals( expected = mapOf(KycType.JUMIO.name to KycStatus.REJECTED), - actual = regionDetails.kycStatusMap) + actual = regionDetailsList[noRegionIndex].kycStatusMap) val newScanInfo: ScanInformation = post { path = "/regions/no/kyc/jumio/scans" @@ -1166,17 +1188,20 @@ class JumioKycTest { body = dataMap2 } - val newRegionDetails = get> { + val newRegionDetailsList = get { path = "/regions" this.email = email - }.single() + } + + noRegionIndex = regionDetailsList.indexOfFirst { it.region.id == "no" } + assertTrue(noRegionIndex != -1, "Did not find Norway region") - assertEquals(Region().id("no").name("Norway"), newRegionDetails.region) - assertEquals(APPROVED, newRegionDetails.status, message = "Wrong State") + assertEquals(Region().id("no").name("Norway"), newRegionDetailsList[noRegionIndex].region) + assertEquals(APPROVED, newRegionDetailsList[noRegionIndex].status, message = "Wrong State") assertEquals( expected = mapOf(KycType.JUMIO.name to KycStatus.APPROVED), - actual = newRegionDetails.kycStatusMap) + actual = newRegionDetailsList[noRegionIndex].kycStatusMap) } finally { StripePayment.deleteCustomer(customerId = customerId) @@ -1271,17 +1296,20 @@ class JumioKycTest { body = dataMap } - val newRegionDetails = get> { + val regionDetailsList1 = get { path = "/regions" this.email = email - }.single() + } + + var noRegionIndex = regionDetailsList1.indexOfFirst { it.region.id == "no" } + assertTrue(noRegionIndex != -1, "Did not find Norway region") - assertEquals(Region().id("no").name("Norway"), newRegionDetails.region) - assertEquals(PENDING, newRegionDetails.status, message = "Wrong State") + assertEquals(Region().id("no").name("Norway"), regionDetailsList1[noRegionIndex].region) + assertEquals(PENDING, regionDetailsList1[noRegionIndex].status, message = "Wrong State") assertEquals( expected = mapOf(KycType.JUMIO.name to KycStatus.REJECTED), - actual = newRegionDetails.kycStatusMap) + actual = regionDetailsList1[noRegionIndex].kycStatusMap) val newScanInfo: ScanInformation = post { path = "/regions/no/kyc/jumio/scans" @@ -1314,17 +1342,20 @@ class JumioKycTest { body = dataMap2 } - val regionDetails = get> { + val regionDetailsList2 = get { path = "/regions" this.email = email - }.single() + } + + noRegionIndex = regionDetailsList2.indexOfFirst { it.region.id == "no" } + assertTrue(noRegionIndex != -1, "Did not find Norway region") - assertEquals(Region().id("no").name("Norway"), regionDetails.region) - assertEquals(APPROVED, regionDetails.status, message = "Wrong State") + assertEquals(Region().id("no").name("Norway"), regionDetailsList2[noRegionIndex].region) + assertEquals(APPROVED, regionDetailsList2[noRegionIndex].status, message = "Wrong State") assertEquals( expected = mapOf(KycType.JUMIO.name to KycStatus.APPROVED), - actual = regionDetails.kycStatusMap) + actual = regionDetailsList2[noRegionIndex].kycStatusMap) val encodedEmail = URLEncoder.encode(email, StandardCharsets.UTF_8) val scanInformationList = get> { @@ -1376,17 +1407,20 @@ class JumioKycTest { body = dataMap } - val regionDetails = get> { + val regionDetailsList = get { path = "/regions" this.email = email - }.single() + } + val noRegionIndex = regionDetailsList.indexOfFirst { it.region.id == "no" } + assertTrue(noRegionIndex != -1, "Did not find Norway region") + - assertEquals(Region().id("no").name("Norway"), regionDetails.region) - assertEquals(PENDING, regionDetails.status, message = "Wrong State") + assertEquals(Region().id("no").name("Norway"), regionDetailsList[noRegionIndex].region) + assertEquals(PENDING, regionDetailsList[noRegionIndex].status, message = "Wrong State") assertEquals( expected = mapOf(KycType.JUMIO.name to KycStatus.REJECTED), - actual = regionDetails.kycStatusMap) + actual = regionDetailsList[noRegionIndex].kycStatusMap) } finally { StripePayment.deleteCustomer(customerId = customerId) @@ -1462,8 +1496,9 @@ class SingaporeKycTest { path = "/regions" this.email = email } - - assertTrue(regionDetailsList.isEmpty(), "regionDetailsList should be empty") + regionDetailsList.forEach { + assertTrue(it.status == AVAILABLE, "All regions should be in available state") + } } val personData: String = get { @@ -1480,7 +1515,8 @@ class SingaporeKycTest { this.email = email } - assertEquals(1, regionDetailsList.size, "regionDetailsList should have only one entry") + val sgRegionIndex = regionDetailsList.indexOfFirst { it.region.id == "sg" } + assertTrue(sgRegionIndex != -1, "regionDetailsList should contain sg region") val regionDetails = RegionDetails() .region(Region().id("sg").name("Singapore")) @@ -1492,7 +1528,7 @@ class SingaporeKycTest { KycType.NRIC_FIN.name to KycStatus.PENDING)) .simProfiles(SimProfileList()) - assertEquals(regionDetails, regionDetailsList.single(), "RegionDetails do not match") + assertEquals(regionDetails, regionDetailsList[sgRegionIndex], "RegionDetails do not match") } } finally { StripePayment.deleteCustomer(customerId = customerId) @@ -1514,7 +1550,9 @@ class SingaporeKycTest { this.email = email } - assertTrue(regionDetailsList.isEmpty(), "regionDetailsList should be empty") + regionDetailsList.forEach { + assertTrue(it.status == AVAILABLE, "All regions should be in available state") + } } val personData: String = get { @@ -1531,7 +1569,8 @@ class SingaporeKycTest { this.email = email } - assertEquals(1, regionDetailsList.size, "regionDetailsList should have only one entry") + val sgRegionIndex = regionDetailsList.indexOfFirst { it.region.id == "sg" } + assertTrue(sgRegionIndex != -1, "regionDetailsList should contain sg region") val regionDetails = RegionDetails() .region(Region().id("sg").name("Singapore")) @@ -1543,7 +1582,7 @@ class SingaporeKycTest { KycType.NRIC_FIN.name to KycStatus.PENDING)) .simProfiles(SimProfileList()) - assertEquals(regionDetails, regionDetailsList.single(), "RegionDetails do not match") + assertEquals(regionDetails, regionDetailsList[sgRegionIndex], "RegionDetails do not match") } } finally { StripePayment.deleteCustomer(customerId = customerId) @@ -1564,7 +1603,9 @@ class SingaporeKycTest { this.email = email } - assertTrue(regionDetailsList.isEmpty(), "regionDetailsList should be empty") + regionDetailsList.forEach { + assertTrue(it.status == AVAILABLE, "All regions should be in available state") + } } get { @@ -1577,8 +1618,8 @@ class SingaporeKycTest { path = "/regions" this.email = email } - - assertEquals(1, regionDetailsList.size, "regionDetailsList should have only one entry") + val sgRegionIndex = regionDetailsList.indexOfFirst { it.region.id == "sg" } + assertTrue(sgRegionIndex != -1, "regionDetailsList should contain sg region") val regionDetails = RegionDetails() .region(Region().id("sg").name("Singapore")) @@ -1590,7 +1631,7 @@ class SingaporeKycTest { KycType.ADDRESS.name to KycStatus.PENDING)) .simProfiles(SimProfileList()) - assertEquals(regionDetails, regionDetailsList.single(), "RegionDetails do not match") + assertEquals(regionDetails, regionDetailsList[sgRegionIndex], "RegionDetails do not match") } val scanInfo: ScanInformation = post { @@ -1623,12 +1664,13 @@ class SingaporeKycTest { } run { - val regionDetailsList = get> { + val regionDetailsList = get { path = "/regions" this.email = email } - assertEquals(1, regionDetailsList.size, "regionDetailsList should have only one entry") + val sgRegionIndex = regionDetailsList.indexOfFirst { it.region.id == "sg" } + assertTrue(sgRegionIndex != -1, "regionDetailsList should contain sg region") val regionDetails = RegionDetails() .region(Region().id("sg").name("Singapore")) @@ -1640,7 +1682,7 @@ class SingaporeKycTest { KycType.ADDRESS.name to KycStatus.PENDING)) .simProfiles(SimProfileList()) - assertEquals(regionDetails, regionDetailsList.single(), "RegionDetails do not match") + assertEquals(regionDetails, regionDetailsList[sgRegionIndex], "RegionDetails do not match") } put(expectedResultCode = 204) { @@ -1655,7 +1697,8 @@ class SingaporeKycTest { this.email = email } - assertEquals(1, regionDetailsList.size, "regionDetailsList should have only one entry") + val sgRegionIndex = regionDetailsList.indexOfFirst { it.region.id == "sg" } + assertTrue(sgRegionIndex != -1, "regionDetailsList should contain sg region") val regionDetails = RegionDetails() .region(Region().id("sg").name("Singapore")) @@ -1667,7 +1710,7 @@ class SingaporeKycTest { KycType.NRIC_FIN.name to KycStatus.APPROVED)) .simProfiles(SimProfileList()) - assertEquals(regionDetails, regionDetailsList.single(), "RegionDetails do not match") + assertEquals(regionDetails, regionDetailsList[sgRegionIndex], "RegionDetails do not match") } } finally { StripePayment.deleteCustomer(customerId = customerId) @@ -1688,7 +1731,9 @@ class SingaporeKycTest { this.email = email } - assertTrue(regionDetailsList.isEmpty(), "regionDetailsList should be empty") + regionDetailsList.forEach { + assertTrue(it.status == AVAILABLE, "All regions should be in available state") + } } val scanInfo: ScanInformation = post { @@ -1721,12 +1766,13 @@ class SingaporeKycTest { } run { - val regionDetailsList = get> { + val regionDetailsList = get { path = "/regions" this.email = email } - assertEquals(1, regionDetailsList.size, "regionDetailsList should have only one entry") + val sgRegionIndex = regionDetailsList.indexOfFirst { it.region.id == "sg" } + assertTrue(sgRegionIndex != -1, "regionDetailsList should contain sg region") val regionDetails = RegionDetails() .region(Region().id("sg").name("Singapore")) @@ -1738,7 +1784,7 @@ class SingaporeKycTest { KycType.ADDRESS.name to KycStatus.PENDING)) .simProfiles(SimProfileList()) - assertEquals(regionDetails, regionDetailsList.single(), "RegionDetails do not match") + assertEquals(regionDetails, regionDetailsList[sgRegionIndex], "RegionDetails do not match") } put(expectedResultCode = 204) { @@ -1753,7 +1799,8 @@ class SingaporeKycTest { this.email = email } - assertEquals(1, regionDetailsList.size, "regionDetailsList should have only one entry") + val sgRegionIndex = regionDetailsList.indexOfFirst { it.region.id == "sg" } + assertTrue(sgRegionIndex != -1, "regionDetailsList should contain sg region") val regionDetails = RegionDetails() .region(Region().id("sg").name("Singapore")) @@ -1765,7 +1812,7 @@ class SingaporeKycTest { KycType.NRIC_FIN.name to KycStatus.PENDING)) .simProfiles(SimProfileList()) - assertEquals(regionDetails, regionDetailsList.single(), "RegionDetails do not match") + assertEquals(regionDetails, regionDetailsList[sgRegionIndex], "RegionDetails do not match") } } finally { StripePayment.deleteCustomer(customerId = customerId) diff --git a/acceptance-tests/src/main/kotlin/org/ostelco/at/okhttp/Tests.kt b/acceptance-tests/src/main/kotlin/org/ostelco/at/okhttp/Tests.kt index cf7702fbe..55c2a485b 100644 --- a/acceptance-tests/src/main/kotlin/org/ostelco/at/okhttp/Tests.kt +++ b/acceptance-tests/src/main/kotlin/org/ostelco/at/okhttp/Tests.kt @@ -30,6 +30,8 @@ import org.ostelco.prime.customer.model.Region import org.ostelco.prime.customer.model.RegionDetails import org.ostelco.prime.customer.model.RegionDetails.StatusEnum.APPROVED import org.ostelco.prime.customer.model.RegionDetails.StatusEnum.PENDING +import org.ostelco.prime.customer.model.RegionDetails.StatusEnum.AVAILABLE +import org.ostelco.prime.customer.model.RegionDetailsList import org.ostelco.prime.customer.model.ScanInformation import org.ostelco.prime.customer.model.SimProfile import org.ostelco.prime.customer.model.SimProfileList @@ -121,9 +123,12 @@ class RegionsTest { val client = clientForSubject(subject = email) - val regionDetailsList: Collection = client.allRegions + val regionDetailsList: RegionDetailsList = client.allRegions + + regionDetailsList.forEach { + assertTrue(it.status == AVAILABLE, "All regions should be in available state") + } - assertTrue(regionDetailsList.isEmpty(), "RegionDetails list for new customer should be empty") } finally { StripePayment.deleteCustomer(customerId = customerId) } @@ -140,9 +145,10 @@ class RegionsTest { val client = clientForSubject(subject = email) - val regionDetailsList: Collection = client.allRegions + val regionDetailsList: RegionDetailsList = client.allRegions - assertEquals(1, regionDetailsList.size, "Customer should have one region") + val noRegionIndex = regionDetailsList.indexOfFirst { it.region.id == "no" } + assertTrue(noRegionIndex != -1, "regionDetailsList should contain 'no' region") val regionDetails = RegionDetails() .region(Region().id("no").name("Norway")) @@ -150,7 +156,7 @@ class RegionsTest { .kycStatusMap(mapOf(KycType.JUMIO.name to KycStatus.APPROVED)) .simProfiles(SimProfileList()) - assertEquals(regionDetails, regionDetailsList.single(), "RegionDetails do not match") + assertEquals(regionDetails, regionDetailsList[noRegionIndex], "RegionDetails do not match") } finally { StripePayment.deleteCustomer(customerId = customerId) } @@ -663,7 +669,9 @@ class SingaporeKycTest { run { val regionDetailsList = client.allRegions - assertTrue(regionDetailsList.isEmpty(), "regionDetailsList should be empty") + regionDetailsList.forEach { + assertTrue(it.status == AVAILABLE, "All regions should be in available state") + } } val personData: String = jacksonObjectMapper().writeValueAsString(client.getCustomerMyInfoV2Data("authCode")) @@ -674,7 +682,8 @@ class SingaporeKycTest { run { val regionDetailsList = client.allRegions - assertEquals(1, regionDetailsList.size, "regionDetailsList should have only one entry") + val sgRegionIndex = regionDetailsList.indexOfFirst { it.region.id == "sg" } + assertTrue(sgRegionIndex != -1, "regionDetailsList should contain sg region") val regionDetails = RegionDetails() .region(Region().id("sg").name("Singapore")) @@ -686,7 +695,7 @@ class SingaporeKycTest { KycType.NRIC_FIN.name to KycStatus.PENDING)) .simProfiles(SimProfileList()) - assertEquals(regionDetails, regionDetailsList.single(), "RegionDetails do not match") + assertEquals(regionDetails, regionDetailsList[sgRegionIndex], "RegionDetails do not match") } } finally { StripePayment.deleteCustomer(customerId = customerId) @@ -707,7 +716,9 @@ class SingaporeKycTest { run { val regionDetailsList = client.allRegions - assertTrue(regionDetailsList.isEmpty(), "regionDetailsList should be empty") + regionDetailsList.forEach { + assertTrue(it.status == AVAILABLE, "All regions should be in available state") + } } val personData: String = jacksonObjectMapper().writeValueAsString(client.getCustomerMyInfoV3Data("authCode")) @@ -718,7 +729,8 @@ class SingaporeKycTest { run { val regionDetailsList = client.allRegions - assertEquals(1, regionDetailsList.size, "regionDetailsList should have only one entry") + val sgRegionIndex = regionDetailsList.indexOfFirst { it.region.id == "sg" } + assertTrue(sgRegionIndex != -1, "regionDetailsList should contain sg region") val regionDetails = RegionDetails() .region(Region().id("sg").name("Singapore")) @@ -730,7 +742,7 @@ class SingaporeKycTest { KycType.NRIC_FIN.name to KycStatus.PENDING)) .simProfiles(SimProfileList()) - assertEquals(regionDetails, regionDetailsList.single(), "RegionDetails do not match") + assertEquals(regionDetails, regionDetailsList[sgRegionIndex], "RegionDetails do not match") } } finally { StripePayment.deleteCustomer(customerId = customerId) @@ -750,7 +762,9 @@ class SingaporeKycTest { run { val regionDetailsList = client.allRegions - assertTrue(regionDetailsList.isEmpty(), "regionDetailsList should be empty") + regionDetailsList.forEach { + assertTrue(it.status == AVAILABLE, "All regions should be in available state") + } } client.checkNricFinId("S7808018C") @@ -758,7 +772,8 @@ class SingaporeKycTest { run { val regionDetailsList = client.allRegions - assertEquals(1, regionDetailsList.size, "regionDetailsList should have only one entry") + val sgRegionIndex = regionDetailsList.indexOfFirst { it.region.id == "sg" } + assertTrue(sgRegionIndex != -1, "regionDetailsList should contain sg region") val regionDetails = RegionDetails() .region(Region().id("sg").name("Singapore")) @@ -770,7 +785,7 @@ class SingaporeKycTest { KycType.ADDRESS.name to KycStatus.PENDING)) .simProfiles(SimProfileList()) - assertEquals(regionDetails, regionDetailsList.single(), "RegionDetails do not match") + assertEquals(regionDetails, regionDetailsList[sgRegionIndex], "RegionDetails do not match") } val scanInfo: ScanInformation = client.createNewJumioKycScanId("sg") @@ -802,7 +817,8 @@ class SingaporeKycTest { run { val regionDetailsList = client.allRegions - assertEquals(1, regionDetailsList.size, "regionDetailsList should have only one entry") + val sgRegionIndex = regionDetailsList.indexOfFirst { it.region.id == "sg" } + assertTrue(sgRegionIndex != -1, "regionDetailsList should contain sg region") val regionDetails = RegionDetails() .region(Region().id("sg").name("Singapore")) @@ -814,7 +830,7 @@ class SingaporeKycTest { KycType.ADDRESS.name to KycStatus.PENDING)) .simProfiles(SimProfileList()) - assertEquals(regionDetails, regionDetailsList.single(), "RegionDetails do not match") + assertEquals(regionDetails, regionDetailsList[sgRegionIndex], "RegionDetails do not match") } client.updateDetails("Singapore") @@ -822,7 +838,8 @@ class SingaporeKycTest { run { val regionDetailsList = client.allRegions - assertEquals(1, regionDetailsList.size, "regionDetailsList should have only one entry") + val sgRegionIndex = regionDetailsList.indexOfFirst { it.region.id == "sg" } + assertTrue(sgRegionIndex != -1, "regionDetailsList should contain sg region") val regionDetails = RegionDetails() .region(Region().id("sg").name("Singapore")) @@ -834,7 +851,7 @@ class SingaporeKycTest { KycType.NRIC_FIN.name to KycStatus.APPROVED)) .simProfiles(SimProfileList()) - assertEquals(regionDetails, regionDetailsList.single(), "RegionDetails do not match") + assertEquals(regionDetails, regionDetailsList[sgRegionIndex], "RegionDetails do not match") } } finally { StripePayment.deleteCustomer(customerId = customerId) @@ -854,7 +871,9 @@ class SingaporeKycTest { run { val regionDetailsList = client.allRegions - assertTrue(regionDetailsList.isEmpty(), "regionDetailsList should be empty") + regionDetailsList.forEach { + assertTrue(it.status == AVAILABLE, "All regions should be in available state") + } } val scanInfo: ScanInformation = client.createNewJumioKycScanId("sg") @@ -886,7 +905,8 @@ class SingaporeKycTest { run { val regionDetailsList = client.allRegions - assertEquals(1, regionDetailsList.size, "regionDetailsList should have only one entry") + val sgRegionIndex = regionDetailsList.indexOfFirst { it.region.id == "sg" } + assertTrue(sgRegionIndex != -1, "regionDetailsList should contain sg region") val regionDetails = RegionDetails() .region(Region().id("sg").name("Singapore")) @@ -898,7 +918,7 @@ class SingaporeKycTest { KycType.ADDRESS.name to KycStatus.PENDING)) .simProfiles(SimProfileList()) - assertEquals(regionDetails, regionDetailsList.single(), "RegionDetails do not match") + assertEquals(regionDetails, regionDetailsList[sgRegionIndex], "RegionDetails do not match") } client.updateDetails("Singapore") @@ -906,7 +926,8 @@ class SingaporeKycTest { run { val regionDetailsList = client.allRegions - assertEquals(1, regionDetailsList.size, "regionDetailsList should have only one entry") + val sgRegionIndex = regionDetailsList.indexOfFirst { it.region.id == "sg" } + assertTrue(sgRegionIndex != -1, "regionDetailsList should contain sg region") val regionDetails = RegionDetails() .region(Region().id("sg").name("Singapore")) @@ -918,7 +939,7 @@ class SingaporeKycTest { KycType.NRIC_FIN.name to KycStatus.PENDING)) .simProfiles(SimProfileList()) - assertEquals(regionDetails, regionDetailsList.single(), "RegionDetails do not match") + assertEquals(regionDetails, regionDetailsList[sgRegionIndex], "RegionDetails do not match") } } finally { StripePayment.deleteCustomer(customerId = customerId) From 3facb8d00e5e8b2286e118460dd0f456e1cb765d Mon Sep 17 00:00:00 2001 From: Prasanth Ullattil Date: Fri, 4 Oct 2019 10:12:12 +0200 Subject: [PATCH 032/173] Sim profile list in default region list should be empty --- acceptance-tests/src/main/kotlin/org/ostelco/at/jersey/Tests.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/acceptance-tests/src/main/kotlin/org/ostelco/at/jersey/Tests.kt b/acceptance-tests/src/main/kotlin/org/ostelco/at/jersey/Tests.kt index dba284282..511de3c99 100644 --- a/acceptance-tests/src/main/kotlin/org/ostelco/at/jersey/Tests.kt +++ b/acceptance-tests/src/main/kotlin/org/ostelco/at/jersey/Tests.kt @@ -1498,6 +1498,7 @@ class SingaporeKycTest { } regionDetailsList.forEach { assertTrue(it.status == AVAILABLE, "All regions should be in available state") + assertTrue(it.simProfiles.isEmpty(), "All regions should have empty Sim profile list") } } From 9ee2900902d856e7329d48d84fc7b73e88c4a24b Mon Sep 17 00:00:00 2001 From: Prasanth Ullattil Date: Fri, 4 Oct 2019 10:21:23 +0200 Subject: [PATCH 033/173] List should contain only one region in a state other than available --- .../src/main/kotlin/org/ostelco/at/jersey/Tests.kt | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/acceptance-tests/src/main/kotlin/org/ostelco/at/jersey/Tests.kt b/acceptance-tests/src/main/kotlin/org/ostelco/at/jersey/Tests.kt index 511de3c99..9c4dfc01d 100644 --- a/acceptance-tests/src/main/kotlin/org/ostelco/at/jersey/Tests.kt +++ b/acceptance-tests/src/main/kotlin/org/ostelco/at/jersey/Tests.kt @@ -214,6 +214,11 @@ class RegionsTest { path = "/regions" this.email = email } + + assertTrue( + regionDetailsList.singleOrNull { it.status != AVAILABLE } != null, + "List should contain only one region in a state other than available") + val noRegionIndex = regionDetailsList.indexOfFirst { it.region.id == "no" } assertTrue(noRegionIndex != -1, "regionDetailsList should contain 'no' region") From 2b89d94c259f387bda4feebfcddf491c2ae38874 Mon Sep 17 00:00:00 2001 From: Prasanth Ullattil Date: Fri, 4 Oct 2019 10:43:30 +0200 Subject: [PATCH 034/173] Simplify tests --- .../kotlin/org/ostelco/at/jersey/Tests.kt | 29 ++++++++++--------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/acceptance-tests/src/main/kotlin/org/ostelco/at/jersey/Tests.kt b/acceptance-tests/src/main/kotlin/org/ostelco/at/jersey/Tests.kt index 9c4dfc01d..71bbde5b5 100644 --- a/acceptance-tests/src/main/kotlin/org/ostelco/at/jersey/Tests.kt +++ b/acceptance-tests/src/main/kotlin/org/ostelco/at/jersey/Tests.kt @@ -55,7 +55,7 @@ class CustomerTest { @Test fun `jersey test - encoded email GET and PUT customer`() { - val email ="customer-${randomInt()}+test@test.com" + val email = "customer-${randomInt()}+test@test.com" val nickname = "Test Customer" var customerId = "" try { @@ -83,7 +83,7 @@ class CustomerTest { assertEquals(createdCustomer.referralId, customer.referralId, "Incorrect 'referralId' in fetched customer") val newName = "New name: Test Customer" - val email2 ="customer-${randomInt()}.abc+test@test.com" + val email2 = "customer-${randomInt()}.abc+test@test.com" val updatedCustomer: Customer = put { path = "/customer" @@ -103,7 +103,7 @@ class CustomerTest { @Test fun `jersey test - GET and PUT customer`() { - val email ="customer-${randomInt()}+test@test.com" + val email = "customer-${randomInt()}+test@test.com" val nickname = "Test Customer" var customerId = "" try { @@ -215,12 +215,14 @@ class RegionsTest { this.email = email } - assertTrue( - regionDetailsList.singleOrNull { it.status != AVAILABLE } != null, - "List should contain only one region in a state other than available") + val naRegionDetails = regionDetailsList.singleOrNull { it.status != AVAILABLE } + assertTrue(naRegionDetails != null, "List should contain only one region in a state other than available") + + val noRegionDetails = regionDetailsList.singleOrNull { it.region.id == "no" } + assertTrue(noRegionDetails != null, "List should contain contain 'no' region") + + assertEquals(naRegionDetails, noRegionDetails, "RegionDetails do not match") - val noRegionIndex = regionDetailsList.indexOfFirst { it.region.id == "no" } - assertTrue(noRegionIndex != -1, "regionDetailsList should contain 'no' region") val regionDetails = RegionDetails() .region(Region().id("no").name("Norway")) @@ -228,7 +230,7 @@ class RegionsTest { .kycStatusMap(mapOf(KycType.JUMIO.name to KycStatus.APPROVED)) .simProfiles(SimProfileList()) - assertEquals(regionDetails, regionDetailsList[noRegionIndex], "RegionDetails do not match") + assertEquals(regionDetails, noRegionDetails, "RegionDetails do not match") } finally { StripePayment.deleteCustomer(customerId = customerId) } @@ -835,16 +837,15 @@ class JumioKycTest { this.email = email } - val noRegionIndex = regionDetailsList.indexOfFirst { it.region.id == "no" } - assertTrue(noRegionIndex != -1, "Did not find Norway region") + val noRegionDetails = regionDetailsList.singleOrNull { it.region.id == "no" } + assertTrue(noRegionDetails != null, "Did not find Norway region") - assertEquals(Region().id("no").name("Norway"), regionDetailsList[noRegionIndex].region) - assertEquals(PENDING, regionDetailsList[noRegionIndex].status, message = "Wrong State") + assertEquals(PENDING, noRegionDetails.status, message = "Wrong State") assertEquals( expected = mapOf( KycType.JUMIO.name to KycStatus.PENDING), - actual = regionDetailsList[noRegionIndex].kycStatusMap) + actual = noRegionDetails.kycStatusMap) } finally { StripePayment.deleteCustomer(customerId = customerId) From 88654384d5055c47603fd9a44ebab88d24e3854e Mon Sep 17 00:00:00 2001 From: Prasanth Ullattil Date: Fri, 4 Oct 2019 10:56:16 +0200 Subject: [PATCH 035/173] Update all 'no' region tests --- .../kotlin/org/ostelco/at/jersey/Tests.kt | 100 +++++++++--------- 1 file changed, 50 insertions(+), 50 deletions(-) diff --git a/acceptance-tests/src/main/kotlin/org/ostelco/at/jersey/Tests.kt b/acceptance-tests/src/main/kotlin/org/ostelco/at/jersey/Tests.kt index 71bbde5b5..ff6c2201d 100644 --- a/acceptance-tests/src/main/kotlin/org/ostelco/at/jersey/Tests.kt +++ b/acceptance-tests/src/main/kotlin/org/ostelco/at/jersey/Tests.kt @@ -890,16 +890,16 @@ class JumioKycTest { } - val noRegionIndex = regionDetailsList.indexOfFirst { it.region.id == "no" } - assertTrue(noRegionIndex != -1, "Did not find Norway region") + val noRegionDetails = regionDetailsList.singleOrNull { it.region.id == "no" } + assertTrue(noRegionDetails != null, "Did not find Norway region") - assertEquals(Region().id("no").name("Norway"), regionDetailsList[noRegionIndex].region) - assertEquals(PENDING, regionDetailsList[noRegionIndex].status, message = "Wrong State") + assertEquals(Region().id("no").name("Norway"), noRegionDetails.region) + assertEquals(PENDING, noRegionDetails.status, message = "Wrong State") assertEquals( expected = mapOf( KycType.JUMIO.name to KycStatus.REJECTED), - actual = regionDetailsList[noRegionIndex].kycStatusMap) + actual = noRegionDetails.kycStatusMap) } finally { StripePayment.deleteCustomer(customerId = customerId) @@ -945,16 +945,16 @@ class JumioKycTest { this.email = email } - val noRegionIndex = regionDetailsList.indexOfFirst { it.region.id == "no" } - assertTrue(noRegionIndex != -1, "Did not find Norway region") + val noRegionDetails = regionDetailsList.singleOrNull { it.region.id == "no" } + assertTrue(noRegionDetails != null, "Did not find Norway region") - assertEquals(Region().id("no").name("Norway"), regionDetailsList[noRegionIndex].region) - assertEquals(APPROVED, regionDetailsList[noRegionIndex].status, message = "Wrong State") + assertEquals(Region().id("no").name("Norway"), noRegionDetails.region) + assertEquals(APPROVED, noRegionDetails.status, message = "Wrong State") assertEquals( expected = mapOf( KycType.JUMIO.name to KycStatus.APPROVED), - actual = regionDetailsList[noRegionIndex].kycStatusMap) + actual = noRegionDetails.kycStatusMap) } finally { StripePayment.deleteCustomer(customerId = customerId) @@ -1000,15 +1000,15 @@ class JumioKycTest { this.email = email } - val noRegionIndex = regionDetailsList.indexOfFirst { it.region.id == "no" } - assertTrue(noRegionIndex != -1, "Did not find Norway region") + val noRegionDetails = regionDetailsList.singleOrNull { it.region.id == "no" } + assertTrue(noRegionDetails != null, "Did not find Norway region") - assertEquals(Region().id("no").name("Norway"), regionDetailsList[noRegionIndex].region) - assertEquals(PENDING, regionDetailsList[noRegionIndex].status, message = "Wrong State") + assertEquals(Region().id("no").name("Norway"), noRegionDetails.region) + assertEquals(PENDING, noRegionDetails.status, message = "Wrong State") assertEquals( expected = mapOf(KycType.JUMIO.name to KycStatus.REJECTED), - actual = regionDetailsList[noRegionIndex].kycStatusMap) + actual = noRegionDetails.kycStatusMap) } finally { StripePayment.deleteCustomer(customerId = customerId) @@ -1052,15 +1052,15 @@ class JumioKycTest { this.email = email } - val noRegionIndex = regionDetailsList.indexOfFirst { it.region.id == "no" } - assertTrue(noRegionIndex != -1, "Did not find Norway region") + val noRegionDetails = regionDetailsList.singleOrNull { it.region.id == "no" } + assertTrue(noRegionDetails != null, "Did not find Norway region") - assertEquals(Region().id("no").name("Norway"), regionDetailsList[noRegionIndex].region) - assertEquals(PENDING, regionDetailsList[noRegionIndex].status, message = "Wrong State") + assertEquals(Region().id("no").name("Norway"), noRegionDetails.region) + assertEquals(PENDING, noRegionDetails.status, message = "Wrong State") assertEquals( expected = mapOf(KycType.JUMIO.name to KycStatus.PENDING), - actual = regionDetailsList[noRegionIndex].kycStatusMap) + actual = noRegionDetails.kycStatusMap) } finally { StripePayment.deleteCustomer(customerId = customerId) @@ -1104,14 +1104,14 @@ class JumioKycTest { this.email = email } - val noRegionIndex = regionDetailsList.indexOfFirst { it.region.id == "no" } - assertTrue(noRegionIndex != -1, "Did not find Norway region") + val noRegionDetails = regionDetailsList.singleOrNull { it.region.id == "no" } + assertTrue(noRegionDetails != null, "Did not find Norway region") - assertEquals(Region().id("no").name("Norway"), regionDetailsList[noRegionIndex].region) - assertEquals(PENDING, regionDetailsList[noRegionIndex].status, message = "Wrong State") + assertEquals(Region().id("no").name("Norway"), noRegionDetails.region) + assertEquals(PENDING, noRegionDetails.status, message = "Wrong State") assertEquals( expected = mapOf(KycType.JUMIO.name to KycStatus.REJECTED), - actual = regionDetailsList[noRegionIndex].kycStatusMap) + actual = noRegionDetails.kycStatusMap) } finally { StripePayment.deleteCustomer(customerId = customerId) @@ -1155,14 +1155,14 @@ class JumioKycTest { this.email = email } - var noRegionIndex = regionDetailsList.indexOfFirst { it.region.id == "no" } - assertTrue(noRegionIndex != -1, "Did not find Norway region") + var noRegionDetails = regionDetailsList.singleOrNull { it.region.id == "no" } + assertTrue(noRegionDetails != null, "Did not find Norway region") - assertEquals(Region().id("no").name("Norway"), regionDetailsList[noRegionIndex].region) - assertEquals(PENDING, regionDetailsList[noRegionIndex].status, message = "Wrong State") + assertEquals(Region().id("no").name("Norway"), noRegionDetails.region) + assertEquals(PENDING, noRegionDetails.status, message = "Wrong State") assertEquals( expected = mapOf(KycType.JUMIO.name to KycStatus.REJECTED), - actual = regionDetailsList[noRegionIndex].kycStatusMap) + actual = noRegionDetails.kycStatusMap) val newScanInfo: ScanInformation = post { path = "/regions/no/kyc/jumio/scans" @@ -1199,15 +1199,15 @@ class JumioKycTest { this.email = email } - noRegionIndex = regionDetailsList.indexOfFirst { it.region.id == "no" } - assertTrue(noRegionIndex != -1, "Did not find Norway region") + noRegionDetails = regionDetailsList.singleOrNull { it.region.id == "no" } + assertTrue(noRegionDetails != null, "Did not find Norway region") - assertEquals(Region().id("no").name("Norway"), newRegionDetailsList[noRegionIndex].region) - assertEquals(APPROVED, newRegionDetailsList[noRegionIndex].status, message = "Wrong State") + assertEquals(Region().id("no").name("Norway"), noRegionDetails.region) + assertEquals(APPROVED, noRegionDetails.status, message = "Wrong State") assertEquals( expected = mapOf(KycType.JUMIO.name to KycStatus.APPROVED), - actual = newRegionDetailsList[noRegionIndex].kycStatusMap) + actual = noRegionDetails.kycStatusMap) } finally { StripePayment.deleteCustomer(customerId = customerId) @@ -1307,15 +1307,15 @@ class JumioKycTest { this.email = email } - var noRegionIndex = regionDetailsList1.indexOfFirst { it.region.id == "no" } - assertTrue(noRegionIndex != -1, "Did not find Norway region") + var noRegionDetails = regionDetailsList1.singleOrNull { it.region.id == "no" } + assertTrue(noRegionDetails != null, "Did not find Norway region") - assertEquals(Region().id("no").name("Norway"), regionDetailsList1[noRegionIndex].region) - assertEquals(PENDING, regionDetailsList1[noRegionIndex].status, message = "Wrong State") + assertEquals(Region().id("no").name("Norway"), noRegionDetails.region) + assertEquals(PENDING, noRegionDetails.status, message = "Wrong State") assertEquals( expected = mapOf(KycType.JUMIO.name to KycStatus.REJECTED), - actual = regionDetailsList1[noRegionIndex].kycStatusMap) + actual = noRegionDetails.kycStatusMap) val newScanInfo: ScanInformation = post { path = "/regions/no/kyc/jumio/scans" @@ -1353,15 +1353,15 @@ class JumioKycTest { this.email = email } - noRegionIndex = regionDetailsList2.indexOfFirst { it.region.id == "no" } - assertTrue(noRegionIndex != -1, "Did not find Norway region") + noRegionDetails = regionDetailsList2.singleOrNull { it.region.id == "no" } + assertTrue(noRegionDetails != null, "Did not find Norway region") - assertEquals(Region().id("no").name("Norway"), regionDetailsList2[noRegionIndex].region) - assertEquals(APPROVED, regionDetailsList2[noRegionIndex].status, message = "Wrong State") + assertEquals(Region().id("no").name("Norway"), noRegionDetails.region) + assertEquals(APPROVED, noRegionDetails.status, message = "Wrong State") assertEquals( expected = mapOf(KycType.JUMIO.name to KycStatus.APPROVED), - actual = regionDetailsList2[noRegionIndex].kycStatusMap) + actual = noRegionDetails.kycStatusMap) val encodedEmail = URLEncoder.encode(email, StandardCharsets.UTF_8) val scanInformationList = get> { @@ -1417,16 +1417,16 @@ class JumioKycTest { path = "/regions" this.email = email } - val noRegionIndex = regionDetailsList.indexOfFirst { it.region.id == "no" } - assertTrue(noRegionIndex != -1, "Did not find Norway region") + val noRegionDetails = regionDetailsList.singleOrNull { it.region.id == "no" } + assertTrue(noRegionDetails != null, "Did not find Norway region") - assertEquals(Region().id("no").name("Norway"), regionDetailsList[noRegionIndex].region) - assertEquals(PENDING, regionDetailsList[noRegionIndex].status, message = "Wrong State") + assertEquals(Region().id("no").name("Norway"), noRegionDetails.region) + assertEquals(PENDING, noRegionDetails.status, message = "Wrong State") assertEquals( expected = mapOf(KycType.JUMIO.name to KycStatus.REJECTED), - actual = regionDetailsList[noRegionIndex].kycStatusMap) + actual = noRegionDetails.kycStatusMap) } finally { StripePayment.deleteCustomer(customerId = customerId) From 0e6e977a8075740d4af108104771b35cc54d100f Mon Sep 17 00:00:00 2001 From: Prasanth Ullattil Date: Fri, 4 Oct 2019 11:06:46 +0200 Subject: [PATCH 036/173] Fix a mistake in the AT --- acceptance-tests/src/main/kotlin/org/ostelco/at/jersey/Tests.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/acceptance-tests/src/main/kotlin/org/ostelco/at/jersey/Tests.kt b/acceptance-tests/src/main/kotlin/org/ostelco/at/jersey/Tests.kt index ff6c2201d..2aa48ffa8 100644 --- a/acceptance-tests/src/main/kotlin/org/ostelco/at/jersey/Tests.kt +++ b/acceptance-tests/src/main/kotlin/org/ostelco/at/jersey/Tests.kt @@ -1199,7 +1199,7 @@ class JumioKycTest { this.email = email } - noRegionDetails = regionDetailsList.singleOrNull { it.region.id == "no" } + noRegionDetails = newRegionDetailsList.singleOrNull { it.region.id == "no" } assertTrue(noRegionDetails != null, "Did not find Norway region") assertEquals(Region().id("no").name("Norway"), noRegionDetails.region) From eb6a26eb3afcd65cba451f2e5a4bb4ddc48452ec Mon Sep 17 00:00:00 2001 From: Prasanth Ullattil Date: Fri, 4 Oct 2019 11:25:22 +0200 Subject: [PATCH 037/173] Simplify 'sg' tests --- .../kotlin/org/ostelco/at/jersey/Tests.kt | 42 +++++++++---------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/acceptance-tests/src/main/kotlin/org/ostelco/at/jersey/Tests.kt b/acceptance-tests/src/main/kotlin/org/ostelco/at/jersey/Tests.kt index 2aa48ffa8..0f0d7928d 100644 --- a/acceptance-tests/src/main/kotlin/org/ostelco/at/jersey/Tests.kt +++ b/acceptance-tests/src/main/kotlin/org/ostelco/at/jersey/Tests.kt @@ -1522,8 +1522,8 @@ class SingaporeKycTest { this.email = email } - val sgRegionIndex = regionDetailsList.indexOfFirst { it.region.id == "sg" } - assertTrue(sgRegionIndex != -1, "regionDetailsList should contain sg region") + val sgRegionDetails = regionDetailsList.singleOrNull { it.region.id == "sg" } + assertTrue(sgRegionDetails != null, "regionDetailsList should contain sg region") val regionDetails = RegionDetails() .region(Region().id("sg").name("Singapore")) @@ -1535,7 +1535,7 @@ class SingaporeKycTest { KycType.NRIC_FIN.name to KycStatus.PENDING)) .simProfiles(SimProfileList()) - assertEquals(regionDetails, regionDetailsList[sgRegionIndex], "RegionDetails do not match") + assertEquals(regionDetails, sgRegionDetails, "RegionDetails do not match") } } finally { StripePayment.deleteCustomer(customerId = customerId) @@ -1576,8 +1576,8 @@ class SingaporeKycTest { this.email = email } - val sgRegionIndex = regionDetailsList.indexOfFirst { it.region.id == "sg" } - assertTrue(sgRegionIndex != -1, "regionDetailsList should contain sg region") + val newRegionDetailsList = regionDetailsList.singleOrNull { it.region.id == "sg" } + assertTrue(newRegionDetailsList != null, "regionDetailsList should contain sg region") val regionDetails = RegionDetails() .region(Region().id("sg").name("Singapore")) @@ -1589,7 +1589,7 @@ class SingaporeKycTest { KycType.NRIC_FIN.name to KycStatus.PENDING)) .simProfiles(SimProfileList()) - assertEquals(regionDetails, regionDetailsList[sgRegionIndex], "RegionDetails do not match") + assertEquals(regionDetails, newRegionDetailsList, "RegionDetails do not match") } } finally { StripePayment.deleteCustomer(customerId = customerId) @@ -1625,8 +1625,8 @@ class SingaporeKycTest { path = "/regions" this.email = email } - val sgRegionIndex = regionDetailsList.indexOfFirst { it.region.id == "sg" } - assertTrue(sgRegionIndex != -1, "regionDetailsList should contain sg region") + val sgRegionDetails = regionDetailsList.singleOrNull { it.region.id == "sg" } + assertTrue(sgRegionDetails != null, "regionDetailsList should contain sg region") val regionDetails = RegionDetails() .region(Region().id("sg").name("Singapore")) @@ -1638,7 +1638,7 @@ class SingaporeKycTest { KycType.ADDRESS.name to KycStatus.PENDING)) .simProfiles(SimProfileList()) - assertEquals(regionDetails, regionDetailsList[sgRegionIndex], "RegionDetails do not match") + assertEquals(regionDetails, sgRegionDetails, "RegionDetails do not match") } val scanInfo: ScanInformation = post { @@ -1676,8 +1676,8 @@ class SingaporeKycTest { this.email = email } - val sgRegionIndex = regionDetailsList.indexOfFirst { it.region.id == "sg" } - assertTrue(sgRegionIndex != -1, "regionDetailsList should contain sg region") + val sgRegionDetails = regionDetailsList.singleOrNull { it.region.id == "sg" } + assertTrue(sgRegionDetails != null, "regionDetailsList should contain sg region") val regionDetails = RegionDetails() .region(Region().id("sg").name("Singapore")) @@ -1689,7 +1689,7 @@ class SingaporeKycTest { KycType.ADDRESS.name to KycStatus.PENDING)) .simProfiles(SimProfileList()) - assertEquals(regionDetails, regionDetailsList[sgRegionIndex], "RegionDetails do not match") + assertEquals(regionDetails, sgRegionDetails, "RegionDetails do not match") } put(expectedResultCode = 204) { @@ -1704,8 +1704,8 @@ class SingaporeKycTest { this.email = email } - val sgRegionIndex = regionDetailsList.indexOfFirst { it.region.id == "sg" } - assertTrue(sgRegionIndex != -1, "regionDetailsList should contain sg region") + val sgRegionDetails = regionDetailsList.singleOrNull { it.region.id == "sg" } + assertTrue(sgRegionDetails != null, "regionDetailsList should contain sg region") val regionDetails = RegionDetails() .region(Region().id("sg").name("Singapore")) @@ -1717,7 +1717,7 @@ class SingaporeKycTest { KycType.NRIC_FIN.name to KycStatus.APPROVED)) .simProfiles(SimProfileList()) - assertEquals(regionDetails, regionDetailsList[sgRegionIndex], "RegionDetails do not match") + assertEquals(regionDetails, sgRegionDetails, "RegionDetails do not match") } } finally { StripePayment.deleteCustomer(customerId = customerId) @@ -1778,8 +1778,8 @@ class SingaporeKycTest { this.email = email } - val sgRegionIndex = regionDetailsList.indexOfFirst { it.region.id == "sg" } - assertTrue(sgRegionIndex != -1, "regionDetailsList should contain sg region") + val sgRegionDetails = regionDetailsList.singleOrNull { it.region.id == "sg" } + assertTrue(sgRegionDetails != null, "regionDetailsList should contain sg region") val regionDetails = RegionDetails() .region(Region().id("sg").name("Singapore")) @@ -1791,7 +1791,7 @@ class SingaporeKycTest { KycType.ADDRESS.name to KycStatus.PENDING)) .simProfiles(SimProfileList()) - assertEquals(regionDetails, regionDetailsList[sgRegionIndex], "RegionDetails do not match") + assertEquals(regionDetails, sgRegionDetails, "RegionDetails do not match") } put(expectedResultCode = 204) { @@ -1806,8 +1806,8 @@ class SingaporeKycTest { this.email = email } - val sgRegionIndex = regionDetailsList.indexOfFirst { it.region.id == "sg" } - assertTrue(sgRegionIndex != -1, "regionDetailsList should contain sg region") + val sgRegionDetails = regionDetailsList.singleOrNull { it.region.id == "sg" } + assertTrue(sgRegionDetails != null, "regionDetailsList should contain sg region") val regionDetails = RegionDetails() .region(Region().id("sg").name("Singapore")) @@ -1819,7 +1819,7 @@ class SingaporeKycTest { KycType.NRIC_FIN.name to KycStatus.PENDING)) .simProfiles(SimProfileList()) - assertEquals(regionDetails, regionDetailsList[sgRegionIndex], "RegionDetails do not match") + assertEquals(regionDetails, sgRegionDetails, "RegionDetails do not match") } } finally { StripePayment.deleteCustomer(customerId = customerId) From 317f8abfc6fa16197d0156939c1ee603c79092aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B8rn=20Remseth?= Date: Mon, 7 Oct 2019 11:50:02 +0200 Subject: [PATCH 038/173] In the middle of a refactoring of the go modules. This is not a viable solution, but submitting to branch to share with collaborators. --- .gitignore | 1 + github.com/go.mod | 3 -- .../loltelutils/loltelutils.go | 24 +++++++++ .../outfile_to_hss_input_converter.go | 50 ++++++++++++++++++ .../outfile_to_hss_input_converter_lib.go} | 51 +------------------ ...utfile_to_hss_input_converter_lib_test.go} | 4 +- .../{ => uploadtoprime}/luhn_test.go | 2 +- .../{ => uploadtoprime}/upload-sim-batch.go | 30 ++--------- 8 files changed, 83 insertions(+), 82 deletions(-) delete mode 100644 github.com/go.mod create mode 100644 sim-administration/sim-batch-management/loltelutils/loltelutils.go create mode 100755 sim-administration/sim-batch-management/outfileconversion/outfile_to_hss_input_converter.go rename sim-administration/sim-batch-management/{outfile_to_hss_input_converter.go => outfileconversion/outfile_to_hss_input_converter_lib.go} (81%) mode change 100755 => 100644 rename sim-administration/sim-batch-management/{outfile_to_hss_input_converter_test.go => outfileconversion/outfile_to_hss_input_converter_lib_test.go} (93%) rename sim-administration/sim-batch-management/{ => uploadtoprime}/luhn_test.go (97%) rename sim-administration/sim-batch-management/{ => uploadtoprime}/upload-sim-batch.go (96%) diff --git a/.gitignore b/.gitignore index 1f09c6d5a..9edbae017 100644 --- a/.gitignore +++ b/.gitignore @@ -45,3 +45,4 @@ metrics_descriptor.pb # Python virtual env venv prime-service-account.json +go.sum diff --git a/github.com/go.mod b/github.com/go.mod deleted file mode 100644 index 3ba5b2de9..000000000 --- a/github.com/go.mod +++ /dev/null @@ -1,3 +0,0 @@ -module github.com/ostelco-core - -go 1.12 diff --git a/sim-administration/sim-batch-management/loltelutils/loltelutils.go b/sim-administration/sim-batch-management/loltelutils/loltelutils.go new file mode 100644 index 000000000..c8b91d204 --- /dev/null +++ b/sim-administration/sim-batch-management/loltelutils/loltelutils.go @@ -0,0 +1,24 @@ +package loltelutils + + +func TrimSuffix(s string, suffixLen int) string { + return s[:len(s)-suffixLen] +} + +func Sign(x int) int { + if x < 0 { + return -1 + } else if x > 0 { + return 1 + } else { + return 0 + } +} + +// Abs returns the absolute value of x. +func Abs(x int) int { + if x < 0 { + return -x + } + return x +} diff --git a/sim-administration/sim-batch-management/outfileconversion/outfile_to_hss_input_converter.go b/sim-administration/sim-batch-management/outfileconversion/outfile_to_hss_input_converter.go new file mode 100755 index 000000000..ef965ce35 --- /dev/null +++ b/sim-administration/sim-batch-management/outfileconversion/outfile_to_hss_input_converter.go @@ -0,0 +1,50 @@ +//usr/bin/env go run "$0" "$@"; exit "$?" +/** + * This program is intended to be used from the command line, and will convert an + * output file from a sim card vendor into an input file for a HSS. The assumptions + * necessary for this to work are: + * + * * The SIM card vendor produces output files similar to the example .out file + * found in the same source directory as this program + * + * * The HSS accepts input as a CSV file, with header line 'ICCID, IMSI, KI' and subsequent + * lines containing ICCID/IMSI/Ki fields, all separated by commas. + * + * Needless to say, the outmost care should be taken when handling Ki values and + * this program must, as a matter of course, be considered a security risk, as + * must all software that touch SIM values. + * + * With that caveat in place, the usage of this program typically looks like + * this: + * + * ./outfile_to_hss_input_converter.go \ + * -input-file sample_out_file_for_testing.out + * -output-file-prefix ./hss-input-for- + * + * (followed by cryptographically strong erasure of the .out file, + * encapsulation of the .csv file in strong cryptography etc., none + * of which are handled by this script). + */ + +package outfileconversion + +import ( + "fmt" + "log" +) + +func main() { + inputFile, outputFilePrefix := parseOutputToHssConverterCommandLine() + + fmt.Println("inputFile = ", inputFile) + fmt.Println("outputFilePrefix = ", outputFilePrefix) + + outRecord := ReadOutputFile(inputFile) + outputFile := outputFilePrefix + outRecord.outputFileName + ".csv" + fmt.Println("outputFile = ", outputFile) + + err := WriteHssCsvFile(outputFile, outRecord.entries) + if err != nil { + log.Fatal("Couldn't close output file '", outputFilePrefix, "'. Error = '", err, "'") + } +} diff --git a/sim-administration/sim-batch-management/outfile_to_hss_input_converter.go b/sim-administration/sim-batch-management/outfileconversion/outfile_to_hss_input_converter_lib.go old mode 100755 new mode 100644 similarity index 81% rename from sim-administration/sim-batch-management/outfile_to_hss_input_converter.go rename to sim-administration/sim-batch-management/outfileconversion/outfile_to_hss_input_converter_lib.go index 57a93b8a5..b0460f72f --- a/sim-administration/sim-batch-management/outfile_to_hss_input_converter.go +++ b/sim-administration/sim-batch-management/outfileconversion/outfile_to_hss_input_converter_lib.go @@ -1,32 +1,4 @@ -//usr/bin/env go run "$0" "$@"; exit "$?" -/** - * This program is intended to be used from the command line, and will convert an - * output file from a sim card vendor into an input file for a HSS. The assumptions - * necessary for this to work are: - * - * * The SIM card vendor produces output files similar to the example .out file - * found in the same source directory as this program - * - * * The HSS accepts input as a CSV file, with header line 'ICCID, IMSI, KI' and subsequent - * lines containing ICCID/IMSI/Ki fields, all separated by commas. - * - * Needless to say, the outmost care should be taken when handling Ki values and - * this program must, as a matter of course, be considered a security risk, as - * must all software that touch SIM values. - * - * With that caveat in place, the usage of this program typically looks like - * this: - * - * ./outfile_to_hss_input_converter.go \ - * -input-file sample_out_file_for_testing.out - * -output-file-prefix ./hss-input-for- - * - * (followed by cryptographically strong erasure of the .out file, - * encapsulation of the .csv file in strong cryptography etc., none - * of which are handled by this script). - */ - -package main +package outfileconversion import ( "bufio" @@ -39,26 +11,6 @@ import ( "strings" ) -/// -/// Main. -/// - -func main() { - inputFile, outputFilePrefix := parseOutputToHssConverterCommandLine() - - fmt.Println("inputFile = ", inputFile) - fmt.Println("outputFilePrefix = ", outputFilePrefix) - - outRecord := ReadOutputFile(inputFile) - outputFile := outputFilePrefix + outRecord.outputFileName + ".csv" - fmt.Println("outputFile = ", outputFile) - - err := WriteHssCsvFile(outputFile, outRecord.entries) - if err != nil { - log.Fatal("Couldn't close output file '", outputFilePrefix, "'. Error = '", err, "'") - } -} - /// /// Data structures /// @@ -349,3 +301,4 @@ func WriteHssCsvFile(filename string, entries []SimEntry) error { fmt.Println("Successfully written ", max, " sim card records.") return f.Close() } + diff --git a/sim-administration/sim-batch-management/outfile_to_hss_input_converter_test.go b/sim-administration/sim-batch-management/outfileconversion/outfile_to_hss_input_converter_lib_test.go similarity index 93% rename from sim-administration/sim-batch-management/outfile_to_hss_input_converter_test.go rename to sim-administration/sim-batch-management/outfileconversion/outfile_to_hss_input_converter_lib_test.go index df1e4fad9..9e47ada3c 100644 --- a/sim-administration/sim-batch-management/outfile_to_hss_input_converter_test.go +++ b/sim-administration/sim-batch-management/outfileconversion/outfile_to_hss_input_converter_lib_test.go @@ -1,4 +1,4 @@ -package main +package outfileconversion import ( "gotest.tools/assert" @@ -14,7 +14,7 @@ func testKeywordValueParser(t *testing.T) { func testReadOutputFile(t *testing.T) { sample_output_file_name := "sample_out_file_for_testing.out" - record, _ := ReadOutputFile(sample_output_file_name) + record := ReadOutputFile(sample_output_file_name) // First parameter to check assert.Equal(t, sample_output_file_name, record.Filename) diff --git a/sim-administration/sim-batch-management/luhn_test.go b/sim-administration/sim-batch-management/uploadtoprime/luhn_test.go similarity index 97% rename from sim-administration/sim-batch-management/luhn_test.go rename to sim-administration/sim-batch-management/uploadtoprime/luhn_test.go index da3055935..524771436 100644 --- a/sim-administration/sim-batch-management/luhn_test.go +++ b/sim-administration/sim-batch-management/uploadtoprime/luhn_test.go @@ -1,4 +1,4 @@ -package main +package uploadtoprime import ( diff --git a/sim-administration/sim-batch-management/upload-sim-batch.go b/sim-administration/sim-batch-management/uploadtoprime/upload-sim-batch.go similarity index 96% rename from sim-administration/sim-batch-management/upload-sim-batch.go rename to sim-administration/sim-batch-management/uploadtoprime/upload-sim-batch.go index 0996befe2..4d7cdbdf9 100755 --- a/sim-administration/sim-batch-management/upload-sim-batch.go +++ b/sim-administration/sim-batch-management/uploadtoprime/upload-sim-batch.go @@ -8,7 +8,7 @@ // considered technical debt, and the debt can be paid back e.g. by // internalizing the logic into prime. -package main +package uploadtoprime import ( "flag" @@ -17,6 +17,7 @@ import ( "net/url" "regexp" "strings" + "loltelutils" ) import ( @@ -168,33 +169,9 @@ type Batch struct { } func IccidWithoutLuhnChecksum(s string) string { - return trimSuffix(s, 1) + return TrimSuffix(s, 1) } -func trimSuffix(s string, suffixLen int) string { - return s[:len(s)-suffixLen] -} - -func Sign(x int) int { - if x < 0 { - return -1 - } else if x > 0 { - return 1 - } else { - return 0 - } -} - -// Abs returns the absolute value of x. -func Abs(x int) int { - if x < 0 { - return -x - } - return x -} - - - func parseCommandLine() Batch { @@ -269,7 +246,6 @@ func parseCommandLine() Batch { checkProfileType("profile-type", *profileType) // Convert to integers, and get lengths - msisdnIncrement := -1 if *firstMsisdn <= *lastMsisdn { msisdnIncrement = 1 From 237040cfad863ab8abc1bd63da4a5dd1b1825468 Mon Sep 17 00:00:00 2001 From: Prasanth Ullattil Date: Mon, 7 Oct 2019 12:59:34 +0200 Subject: [PATCH 039/173] Mark the scaninfo-shredder cronjob as disabled --- prime/infra/prime-direct-values.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/prime/infra/prime-direct-values.yaml b/prime/infra/prime-direct-values.yaml index 75a91c84d..46befbe66 100644 --- a/prime/infra/prime-direct-values.yaml +++ b/prime/infra/prime-direct-values.yaml @@ -11,6 +11,8 @@ podAutoscaling: enabled: false cronjobs: + shredder: + enabled: false prime: image: eu.gcr.io/pi-ostelco-dev/prime From 36ba48a2ab19eefd394101116a9c3a0391e42ba5 Mon Sep 17 00:00:00 2001 From: Prasanth Ullattil Date: Thu, 3 Oct 2019 10:31:17 +0200 Subject: [PATCH 040/173] Add the new enum value --- prime/infra/dev/prime-customer-api.yaml | 2 +- prime/infra/prod/prime-customer-api.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/prime/infra/dev/prime-customer-api.yaml b/prime/infra/dev/prime-customer-api.yaml index ca32a6371..4a01c61c2 100644 --- a/prime/infra/dev/prime-customer-api.yaml +++ b/prime/infra/dev/prime-customer-api.yaml @@ -875,7 +875,7 @@ definitions: status: description: "Customer Status for this region" type: string - enum: [ PENDING, APPROVED ] + enum: [ PENDING, APPROVED, AVAILABLE ] kycStatusMap: description: "Map of status for each KYC" type: object diff --git a/prime/infra/prod/prime-customer-api.yaml b/prime/infra/prod/prime-customer-api.yaml index 410960ea9..54d0df9bd 100644 --- a/prime/infra/prod/prime-customer-api.yaml +++ b/prime/infra/prod/prime-customer-api.yaml @@ -875,7 +875,7 @@ definitions: status: description: "Customer Status for this region" type: string - enum: [ PENDING, APPROVED ] + enum: [ PENDING, APPROVED, AVAILABLE ] kycStatusMap: description: "Map of status for each KYC" type: object From 73b4e323cdeafbf9fe94fa17a873825ddbd19f6e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B8rn=20Remseth?= Date: Mon, 7 Oct 2019 13:50:18 +0200 Subject: [PATCH 041/173] Attempt to add loltelutils, partially succesful. --- .../outfileconversion/outfile_to_hss_input_converter_lib.go | 2 +- .../sim-batch-management/uploadtoprime/upload-sim-batch.go | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/sim-administration/sim-batch-management/outfileconversion/outfile_to_hss_input_converter_lib.go b/sim-administration/sim-batch-management/outfileconversion/outfile_to_hss_input_converter_lib.go index b0460f72f..8761a68e9 100644 --- a/sim-administration/sim-batch-management/outfileconversion/outfile_to_hss_input_converter_lib.go +++ b/sim-administration/sim-batch-management/outfileconversion/outfile_to_hss_input_converter_lib.go @@ -158,7 +158,7 @@ func ReadOutputFile(filename string) OutputFileRecord { iccidWithChecksum := rawIccid if strings.HasSuffix(rawIccid, "F") { - iccidWithChecksum = trimSuffix(rawIccid, 1) + iccidWithChecksum = loltelutils.TrimSuffix(rawIccid, 1) } var iccidWithoutChecksum = trimSuffix(iccidWithChecksum, 1) diff --git a/sim-administration/sim-batch-management/uploadtoprime/upload-sim-batch.go b/sim-administration/sim-batch-management/uploadtoprime/upload-sim-batch.go index 4d7cdbdf9..4a62eb0e0 100755 --- a/sim-administration/sim-batch-management/uploadtoprime/upload-sim-batch.go +++ b/sim-administration/sim-batch-management/uploadtoprime/upload-sim-batch.go @@ -17,8 +17,8 @@ import ( "net/url" "regexp" "strings" - "loltelutils" -) + . "github.com/ostelco/ostelco-core/loltelutils" + ) import ( . "strconv" From 266e68718a963f556fdab28eabd784e543083dff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B8rn=20Remseth?= Date: Mon, 7 Oct 2019 13:51:30 +0200 Subject: [PATCH 042/173] Removing dot, same error message. --- .../sim-batch-management/uploadtoprime/upload-sim-batch.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sim-administration/sim-batch-management/uploadtoprime/upload-sim-batch.go b/sim-administration/sim-batch-management/uploadtoprime/upload-sim-batch.go index 4a62eb0e0..695be17ad 100755 --- a/sim-administration/sim-batch-management/uploadtoprime/upload-sim-batch.go +++ b/sim-administration/sim-batch-management/uploadtoprime/upload-sim-batch.go @@ -17,7 +17,7 @@ import ( "net/url" "regexp" "strings" - . "github.com/ostelco/ostelco-core/loltelutils" + "github.com/ostelco/ostelco-core/loltelutils" ) import ( From 00b59e5571092c9ad83bf78394afac6f0f381c64 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B8rn=20Remseth?= Date: Mon, 7 Oct 2019 13:55:51 +0200 Subject: [PATCH 043/173] Adding some imports, still observing failures --- .../outfile_to_hss_input_converter_lib.go | 2 ++ .../uploadtoprime/upload-sim-batch.go | 8 ++++---- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/sim-administration/sim-batch-management/outfileconversion/outfile_to_hss_input_converter_lib.go b/sim-administration/sim-batch-management/outfileconversion/outfile_to_hss_input_converter_lib.go index 8761a68e9..bc06cd202 100644 --- a/sim-administration/sim-batch-management/outfileconversion/outfile_to_hss_input_converter_lib.go +++ b/sim-administration/sim-batch-management/outfileconversion/outfile_to_hss_input_converter_lib.go @@ -9,6 +9,8 @@ import ( "regexp" "strconv" "strings" + "github.com/ostelco/ostelco-core/loltelutils" + ) /// diff --git a/sim-administration/sim-batch-management/uploadtoprime/upload-sim-batch.go b/sim-administration/sim-batch-management/uploadtoprime/upload-sim-batch.go index 695be17ad..93df32e9b 100755 --- a/sim-administration/sim-batch-management/uploadtoprime/upload-sim-batch.go +++ b/sim-administration/sim-batch-management/uploadtoprime/upload-sim-batch.go @@ -13,12 +13,12 @@ package uploadtoprime import ( "flag" "fmt" + "github.com/ostelco/ostelco-core/loltelutils" "log" "net/url" "regexp" "strings" - "github.com/ostelco/ostelco-core/loltelutils" - ) +) import ( . "strconv" @@ -26,7 +26,7 @@ import ( func main() { batch := parseCommandLine() - var csvPayload string = generateCsvPayload(batch) + var csvPayload = generateCsvPayload(batch) generatePostingCurlscript(batch.url, csvPayload) } @@ -36,7 +36,7 @@ func generatePostingCurlscript(url string, payload string) { fmt.Printf("curl -H 'Content-Type: text/plain' -X PUT --data-binary @- %s < Date: Mon, 7 Oct 2019 14:04:25 +0200 Subject: [PATCH 044/173] Whitespace --- .../outfileconversion/outfile_to_hss_input_converter_lib.go | 1 - 1 file changed, 1 deletion(-) diff --git a/sim-administration/sim-batch-management/outfileconversion/outfile_to_hss_input_converter_lib.go b/sim-administration/sim-batch-management/outfileconversion/outfile_to_hss_input_converter_lib.go index bc06cd202..ba80c7a76 100644 --- a/sim-administration/sim-batch-management/outfileconversion/outfile_to_hss_input_converter_lib.go +++ b/sim-administration/sim-batch-management/outfileconversion/outfile_to_hss_input_converter_lib.go @@ -10,7 +10,6 @@ import ( "strconv" "strings" "github.com/ostelco/ostelco-core/loltelutils" - ) /// From 289cc9a108be1d43fca7da88612e115d00fb5624 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B8rn=20Remseth?= Date: Mon, 7 Oct 2019 14:24:54 +0200 Subject: [PATCH 045/173] All compiling, all tests passing --- .../outfile_to_hss_input_converter_lib.go | 4 +-- .../uploadtoprime/upload-sim-batch.go | 36 +++++++++---------- 2 files changed, 19 insertions(+), 21 deletions(-) diff --git a/sim-administration/sim-batch-management/outfileconversion/outfile_to_hss_input_converter_lib.go b/sim-administration/sim-batch-management/outfileconversion/outfile_to_hss_input_converter_lib.go index ba80c7a76..258775c41 100644 --- a/sim-administration/sim-batch-management/outfileconversion/outfile_to_hss_input_converter_lib.go +++ b/sim-administration/sim-batch-management/outfileconversion/outfile_to_hss_input_converter_lib.go @@ -9,7 +9,7 @@ import ( "regexp" "strconv" "strings" - "github.com/ostelco/ostelco-core/loltelutils" + "github.com/ostelco/ostelco-core/sim-administration/sim-batch-management/loltelutils" ) /// @@ -162,7 +162,7 @@ func ReadOutputFile(filename string) OutputFileRecord { iccidWithChecksum = loltelutils.TrimSuffix(rawIccid, 1) } - var iccidWithoutChecksum = trimSuffix(iccidWithChecksum, 1) + var iccidWithoutChecksum = loltelutils.TrimSuffix(iccidWithChecksum, 1) // TODO: Enable this!! checkICCIDSyntax(iccidWithChecksum) entry := SimEntry{ rawIccid: rawIccid, diff --git a/sim-administration/sim-batch-management/uploadtoprime/upload-sim-batch.go b/sim-administration/sim-batch-management/uploadtoprime/upload-sim-batch.go index 93df32e9b..4ea93c0c8 100755 --- a/sim-administration/sim-batch-management/uploadtoprime/upload-sim-batch.go +++ b/sim-administration/sim-batch-management/uploadtoprime/upload-sim-batch.go @@ -13,17 +13,14 @@ package uploadtoprime import ( "flag" "fmt" - "github.com/ostelco/ostelco-core/loltelutils" + "github.com/ostelco/ostelco-core/sim-administration/sim-batch-management/loltelutils" "log" "net/url" "regexp" + "strconv" "strings" ) -import ( - . "strconv" -) - func main() { batch := parseCommandLine() var csvPayload = generateCsvPayload(batch) @@ -54,7 +51,7 @@ func calculateChecksum(luhnString string, double bool) int { checksum := 0 for i := len(source) - 1; i > -1; i-- { - t, _ := ParseInt(source[i], 10, 8) + t, _ := strconv.ParseInt(source[i], 10, 8) n := int(t) if double { @@ -73,7 +70,7 @@ func calculateChecksum(luhnString string, double bool) int { } func LuhnChecksum(number int) int { - return generateControlDigit(Itoa(number)) + return generateControlDigit(strconv.Itoa(number)) } func generateCsvPayload(batch Batch) string { @@ -169,7 +166,7 @@ type Batch struct { } func IccidWithoutLuhnChecksum(s string) string { - return TrimSuffix(s, 1) + return loltelutils.TrimSuffix(s, 1) } @@ -230,7 +227,7 @@ func parseCommandLine() Batch { checkMSISDNSyntax("last-msisdn", *lastMsisdn) checkMSISDNSyntax("first-msisdn", *firstMsisdn) - batchLength, err := Atoi(*batchLengthString) + batchLength, err := strconv.Atoi(*batchLengthString) if err != nil { log.Fatalf("Not a valid batch length string '%s'.\n", *batchLengthString) } @@ -255,24 +252,25 @@ func parseCommandLine() Batch { log.Println("lastmsisdn = ", *lastMsisdn) log.Println("msisdnIncrement = ", msisdnIncrement) - var firstMsisdnInt, _ = Atoi(*firstMsisdn) - var lastMsisdnInt, _ = Atoi(*lastMsisdn) + var firstMsisdnInt, _ = strconv.Atoi(*firstMsisdn) + var lastMsisdnInt, _ = strconv.Atoi(*lastMsisdn) var msisdnLen = lastMsisdnInt - firstMsisdnInt + 1 if msisdnLen < 0 { msisdnLen = -msisdnLen } - var firstImsiInt, _ = Atoi(*firstIMSI) - var lastImsiInt, _ = Atoi(*lastIMSI) + var firstImsiInt, _ = strconv.Atoi(*firstIMSI) + var lastImsiInt, _ = strconv.Atoi(*lastIMSI) var imsiLen = lastImsiInt - firstImsiInt + 1 - var firstIccidInt, _ = Atoi(IccidWithoutLuhnChecksum(*firstIccid)) - var lastIccidInt, _ = Atoi(IccidWithoutLuhnChecksum(*lastIccid)) + var firstIccidInt, _ = strconv.Atoi(IccidWithoutLuhnChecksum(*firstIccid)) + var lastIccidInt, _ = strconv.Atoi(IccidWithoutLuhnChecksum(*lastIccid)) var iccidlen = lastIccidInt - firstIccidInt + 1 // Validate that lengths of sequences are equal in absolute // values. - if Abs(msisdnLen) != Abs(iccidlen) || Abs(msisdnLen) != Abs(imsiLen) || batchLength != Abs(imsiLen) { + // TODO: Perhaps use some varargs trick of some sort here? + if loltelutils.Abs(msisdnLen) != loltelutils.Abs(iccidlen) || loltelutils.Abs(msisdnLen) != loltelutils.Abs(imsiLen) || batchLength != loltelutils.Abs(imsiLen) { log.Printf("msisdnLen = %10d\n", msisdnLen) log.Printf("iccidLen = %10d\n", iccidlen) log.Printf("imsiLen = %10d\n", imsiLen) @@ -289,11 +287,11 @@ func parseCommandLine() Batch { return Batch{ profileType: *profileType, url: uploadUrl, - length: Abs(iccidlen), + length: loltelutils.Abs(iccidlen), firstIccid: firstIccidInt, - iccidIncrement: Sign(iccidlen), + iccidIncrement: loltelutils.Sign(iccidlen), firstImsi: firstImsiInt, - imsiIncrement: Sign(imsiLen), + imsiIncrement: loltelutils.Sign(imsiLen), firstMsisdn: firstMsisdnInt, msisdnIncrement: msisdnIncrement, } From 81e8581b367e87282709f7c5a2978367a631935a Mon Sep 17 00:00:00 2001 From: Prasanth Ullattil Date: Mon, 7 Oct 2019 14:40:48 +0200 Subject: [PATCH 046/173] Add 'AVAILABLE' enum value to CustomerRegionStatus --- prime/config/customer.graphqls | 1 + 1 file changed, 1 insertion(+) diff --git a/prime/config/customer.graphqls b/prime/config/customer.graphqls index 162cd3548..081dec19c 100644 --- a/prime/config/customer.graphqls +++ b/prime/config/customer.graphqls @@ -42,6 +42,7 @@ type Region { enum CustomerRegionStatus { PENDING APPROVED + AVAILABLE } type KycStatusMap { From f7ee1460adef9bbac67539f40f50a9e52e7ad5e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B8rn=20Remseth?= Date: Mon, 7 Oct 2019 14:46:40 +0200 Subject: [PATCH 047/173] Refactoring the generic 'batch' to 'OutputBatch', since it's the result of parsing an output file --- .../uploadtoprime/upload-sim-batch.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/sim-administration/sim-batch-management/uploadtoprime/upload-sim-batch.go b/sim-administration/sim-batch-management/uploadtoprime/upload-sim-batch.go index 4ea93c0c8..9b7d55625 100755 --- a/sim-administration/sim-batch-management/uploadtoprime/upload-sim-batch.go +++ b/sim-administration/sim-batch-management/uploadtoprime/upload-sim-batch.go @@ -22,7 +22,7 @@ import ( ) func main() { - batch := parseCommandLine() + batch := parseUploadFileGeneratorCommmandline() var csvPayload = generateCsvPayload(batch) generatePostingCurlscript(batch.url, csvPayload) @@ -73,7 +73,7 @@ func LuhnChecksum(number int) int { return generateControlDigit(strconv.Itoa(number)) } -func generateCsvPayload(batch Batch) string { +func generateCsvPayload(batch OutputBatch) string { var sb strings.Builder sb.WriteString("ICCID, IMSI, MSISDN, PIN1, PIN2, PUK1, PUK2, PROFILE\n") @@ -153,7 +153,7 @@ func checkProfileType(name string, potentialProfileName string) { } } -type Batch struct { +type OutputBatch struct { profileType string url string length int @@ -170,7 +170,7 @@ func IccidWithoutLuhnChecksum(s string) string { } -func parseCommandLine() Batch { +func parseUploadFileGeneratorCommmandline() OutputBatch { // // Set up command line parsing @@ -233,7 +233,7 @@ func parseCommandLine() Batch { } if batchLength <= 0 { - log.Fatalf("Batch length must be positive, but was '%d'", batchLength) + log.Fatalf("OutputBatch length must be positive, but was '%d'", batchLength) } uploadUrl := fmt.Sprintf("http://%s:%s/ostelco/sim-inventory/%s/import-batch/profilevendor/%s?initialHssState=%s", @@ -284,7 +284,7 @@ func parseCommandLine() Batch { // Return a correctly parsed batch - return Batch{ + return OutputBatch{ profileType: *profileType, url: uploadUrl, length: loltelutils.Abs(iccidlen), From dc991f7a82a09a5c9b9905166481233fbd72035a Mon Sep 17 00:00:00 2001 From: Prasanth Ullattil Date: Mon, 7 Oct 2019 15:04:49 +0200 Subject: [PATCH 048/173] Add AVAILABLE flag to tests --- graphql/src/test/resources/customer.graphqls | 1 + 1 file changed, 1 insertion(+) diff --git a/graphql/src/test/resources/customer.graphqls b/graphql/src/test/resources/customer.graphqls index 162cd3548..081dec19c 100644 --- a/graphql/src/test/resources/customer.graphqls +++ b/graphql/src/test/resources/customer.graphqls @@ -42,6 +42,7 @@ type Region { enum CustomerRegionStatus { PENDING APPROVED + AVAILABLE } type KycStatusMap { From 274e459aceb6814be8391e55cc14cb80ea027f25 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B8rn=20Remseth?= Date: Mon, 7 Oct 2019 15:08:03 +0200 Subject: [PATCH 049/173] Trivial unit test. --- .../outfile_to_hss_input_converter_lib.go | 2 +- .../sim-batch-management/upload-sim-batch.go | 8 +++++ .../uploadtoprime/input_batch_test.go | 19 ++++++++++++ ...d-sim-batch.go => upload-sim-batch-lib.go} | 30 ++++++++++++++----- 4 files changed, 50 insertions(+), 9 deletions(-) create mode 100644 sim-administration/sim-batch-management/upload-sim-batch.go create mode 100644 sim-administration/sim-batch-management/uploadtoprime/input_batch_test.go rename sim-administration/sim-batch-management/uploadtoprime/{upload-sim-batch.go => upload-sim-batch-lib.go} (94%) diff --git a/sim-administration/sim-batch-management/outfileconversion/outfile_to_hss_input_converter_lib.go b/sim-administration/sim-batch-management/outfileconversion/outfile_to_hss_input_converter_lib.go index 258775c41..ad3d59c30 100644 --- a/sim-administration/sim-batch-management/outfileconversion/outfile_to_hss_input_converter_lib.go +++ b/sim-administration/sim-batch-management/outfileconversion/outfile_to_hss_input_converter_lib.go @@ -4,12 +4,12 @@ import ( "bufio" "flag" "fmt" + "github.com/ostelco/ostelco-core/sim-administration/sim-batch-management/loltelutils" "log" "os" "regexp" "strconv" "strings" - "github.com/ostelco/ostelco-core/sim-administration/sim-batch-management/loltelutils" ) /// diff --git a/sim-administration/sim-batch-management/upload-sim-batch.go b/sim-administration/sim-batch-management/upload-sim-batch.go new file mode 100644 index 000000000..7b28ac76f --- /dev/null +++ b/sim-administration/sim-batch-management/upload-sim-batch.go @@ -0,0 +1,8 @@ +package sim_batch_management + +func main() { + batch := ParseUploadFileGeneratorCommmandline() + var csvPayload = generateCsvPayload(batch) + + GeneratePostingCurlscript(batch.url, csvPayload) +} \ No newline at end of file diff --git a/sim-administration/sim-batch-management/uploadtoprime/input_batch_test.go b/sim-administration/sim-batch-management/uploadtoprime/input_batch_test.go new file mode 100644 index 000000000..e3a8d8991 --- /dev/null +++ b/sim-administration/sim-batch-management/uploadtoprime/input_batch_test.go @@ -0,0 +1,19 @@ +package uploadtoprime + +import ( + "gotest.tools/assert" + "testing" +) + +func TestParseInputFileGeneratorCommmandline(t *testing.T) { + parsedBatch := ParseInputFileGeneratorCommmandline() + assert.Equal(t, "Foo", parsedBatch.customer) + assert.Equal(t, "Bar", parsedBatch.profileType) + assert.Equal(t, "baz", parsedBatch.orderDate) + assert.Equal(t, "gazonk", parsedBatch.batchNo) + assert.Equal(t, 5, parsedBatch.length) + assert.Equal(t, 0, parsedBatch.firstIccid) + assert.Equal(t, 2, parsedBatch.firstImsi) + + return InputBatch{customer: "Foo", profileType: "Bar", orderDate: "baz", batchNo: "gazonk", length: 5, firstIccid: 0, firstImsi:2} +} diff --git a/sim-administration/sim-batch-management/uploadtoprime/upload-sim-batch.go b/sim-administration/sim-batch-management/uploadtoprime/upload-sim-batch-lib.go similarity index 94% rename from sim-administration/sim-batch-management/uploadtoprime/upload-sim-batch.go rename to sim-administration/sim-batch-management/uploadtoprime/upload-sim-batch-lib.go index 9b7d55625..9840e0277 100755 --- a/sim-administration/sim-batch-management/uploadtoprime/upload-sim-batch.go +++ b/sim-administration/sim-batch-management/uploadtoprime/upload-sim-batch-lib.go @@ -21,14 +21,8 @@ import ( "strings" ) -func main() { - batch := parseUploadFileGeneratorCommmandline() - var csvPayload = generateCsvPayload(batch) - generatePostingCurlscript(batch.url, csvPayload) -} - -func generatePostingCurlscript(url string, payload string) { +func GeneratePostingCurlscript(url string, payload string) { fmt.Printf("#!/bin/bash\n") fmt.Printf("curl -H 'Content-Type: text/plain' -X PUT --data-binary @- %s < Date: Mon, 7 Oct 2019 15:42:07 +0200 Subject: [PATCH 050/173] Sketch out how to getting a batch from the command line (eventually) --- .../uploadtoprime/input_batch_test.go | 17 ++++++++--------- .../uploadtoprime/upload-sim-batch-lib.go | 5 ++++- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/sim-administration/sim-batch-management/uploadtoprime/input_batch_test.go b/sim-administration/sim-batch-management/uploadtoprime/input_batch_test.go index e3a8d8991..14dd37e9c 100644 --- a/sim-administration/sim-batch-management/uploadtoprime/input_batch_test.go +++ b/sim-administration/sim-batch-management/uploadtoprime/input_batch_test.go @@ -6,14 +6,13 @@ import ( ) func TestParseInputFileGeneratorCommmandline(t *testing.T) { - parsedBatch := ParseInputFileGeneratorCommmandline() - assert.Equal(t, "Foo", parsedBatch.customer) - assert.Equal(t, "Bar", parsedBatch.profileType) - assert.Equal(t, "baz", parsedBatch.orderDate) - assert.Equal(t, "gazonk", parsedBatch.batchNo) - assert.Equal(t, 5, parsedBatch.length) - assert.Equal(t, 0, parsedBatch.firstIccid) - assert.Equal(t, 2, parsedBatch.firstImsi) - return InputBatch{customer: "Foo", profileType: "Bar", orderDate: "baz", batchNo: "gazonk", length: 5, firstIccid: 0, firstImsi:2} + parsedBatch := ParseInputFileGeneratorCommmandline() + assert.Equal(t, "Loltel", parsedBatch.customer) + assert.Equal(t, "OYA_LOLTEL_ACB", parsedBatch.profileType) + assert.Equal(t, "20191007", parsedBatch.orderDate) + assert.Equal(t, "2019100701", parsedBatch.batchNo) + assert.Equal(t, 10, parsedBatch.length) + assert.Equal(t, 894700000000002214, parsedBatch.firstIccid) + assert.Equal(t, 242017100012213, parsedBatch.firstImsi) } diff --git a/sim-administration/sim-batch-management/uploadtoprime/upload-sim-batch-lib.go b/sim-administration/sim-batch-management/uploadtoprime/upload-sim-batch-lib.go index 9840e0277..d7c9e0932 100755 --- a/sim-administration/sim-batch-management/uploadtoprime/upload-sim-batch-lib.go +++ b/sim-administration/sim-batch-management/uploadtoprime/upload-sim-batch-lib.go @@ -308,5 +308,8 @@ type InputBatch struct { } func ParseInputFileGeneratorCommmandline() InputBatch { - return InputBatch{customer: "Foo", profileType: "Bar", orderDate: "baz", batchNo: "gazonk", length: 5, firstIccid: 0, firstImsi:2} + // TODO: This is what we want to happen, but for some reason it isn't happening, so + // we need to up our Go-Fu before we can make flag.Parse(arguments) work + + return InputBatch{customer: "Loltel", profileType: "OYA_LOLTEL_ACB", orderDate: "20191007", batchNo: "2019100701", length: 10, firstIccid: 894700000000002214, firstImsi:242017100012213} } \ No newline at end of file From d5511370c5c4dcb235f4eb9e3f5f0be3193836b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A5vard=20Nor=C3=A9n?= Date: Tue, 8 Oct 2019 21:17:46 +0200 Subject: [PATCH 051/173] Synchronize client and server schema --- prime/config/customer.graphqls | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/prime/config/customer.graphqls b/prime/config/customer.graphqls index 162cd3548..bcc177412 100644 --- a/prime/config/customer.graphqls +++ b/prime/config/customer.graphqls @@ -9,8 +9,8 @@ type QueryType { type Context { customer: Customer! bundles: [Bundle!] - regions(regionCode: String): [RegionDetails!] - products: [Product!] + regions(regionCode: String): [RegionDetails!]! + products: [Product!]! purchases: [Purchase!] } @@ -29,8 +29,8 @@ type Bundle { type RegionDetails { region: Region! - status: CustomerRegionStatus - kycStatusMap: KycStatusMap + status: CustomerRegionStatus! + kycStatusMap: KycStatusMap! simProfiles: [SimProfile!] } @@ -75,8 +75,8 @@ enum SimProfileStatus { type Product { sku: String! price: Price! - properties: Properties - presentation: Presentation + properties: Properties! + presentation: Presentation! } type Properties { @@ -87,11 +87,11 @@ type Properties { type Presentation { subTotal: String payeeLabel: String - priceLabel: String + priceLabel: String! taxLabel: String tax: String subTotalLabel: String - productLabel: String + productLabel: String! label: String } @@ -104,4 +104,4 @@ type Purchase { id: String! product: Product! timestamp: Long! -} \ No newline at end of file +} From 7f578d0e68dd07f2f98fd963eb513513ab3bdebd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A5vard=20Nor=C3=A9n?= Date: Tue, 8 Oct 2019 21:19:54 +0200 Subject: [PATCH 052/173] Update customer.graphqls --- prime/config/customer.graphqls | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/prime/config/customer.graphqls b/prime/config/customer.graphqls index bcc177412..5bc80b44c 100644 --- a/prime/config/customer.graphqls +++ b/prime/config/customer.graphqls @@ -7,7 +7,7 @@ type QueryType { } type Context { - customer: Customer! + customer: Customer bundles: [Bundle!] regions(regionCode: String): [RegionDetails!]! products: [Product!]! From 0decd607be5f994c37f4642254e8b8a0a3127dbd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A5vard=20Nor=C3=A9n?= Date: Tue, 8 Oct 2019 22:02:53 +0200 Subject: [PATCH 053/173] Update customer.graphqls --- prime/config/customer.graphqls | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/prime/config/customer.graphqls b/prime/config/customer.graphqls index 5bc80b44c..f057b6a45 100644 --- a/prime/config/customer.graphqls +++ b/prime/config/customer.graphqls @@ -8,10 +8,10 @@ type QueryType { type Context { customer: Customer - bundles: [Bundle!] + bundles: [Bundle!]! regions(regionCode: String): [RegionDetails!]! products: [Product!]! - purchases: [Purchase!] + purchases: [Purchase!]! } type Customer { @@ -42,6 +42,7 @@ type Region { enum CustomerRegionStatus { PENDING APPROVED + AVAILABLE } type KycStatusMap { From 17d9a5082c890ba9f07b586721cd741a64551335 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A5vard=20Nor=C3=A9n?= Date: Wed, 9 Oct 2019 09:29:04 +0200 Subject: [PATCH 054/173] Update customer.graphqls --- graphql/src/test/resources/customer.graphqls | 25 ++++++++++---------- 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/graphql/src/test/resources/customer.graphqls b/graphql/src/test/resources/customer.graphqls index 162cd3548..f057b6a45 100644 --- a/graphql/src/test/resources/customer.graphqls +++ b/graphql/src/test/resources/customer.graphqls @@ -7,11 +7,11 @@ type QueryType { } type Context { - customer: Customer! - bundles: [Bundle!] - regions(regionCode: String): [RegionDetails!] - products: [Product!] - purchases: [Purchase!] + customer: Customer + bundles: [Bundle!]! + regions(regionCode: String): [RegionDetails!]! + products: [Product!]! + purchases: [Purchase!]! } type Customer { @@ -29,8 +29,8 @@ type Bundle { type RegionDetails { region: Region! - status: CustomerRegionStatus - kycStatusMap: KycStatusMap + status: CustomerRegionStatus! + kycStatusMap: KycStatusMap! simProfiles: [SimProfile!] } @@ -42,6 +42,7 @@ type Region { enum CustomerRegionStatus { PENDING APPROVED + AVAILABLE } type KycStatusMap { @@ -75,8 +76,8 @@ enum SimProfileStatus { type Product { sku: String! price: Price! - properties: Properties - presentation: Presentation + properties: Properties! + presentation: Presentation! } type Properties { @@ -87,11 +88,11 @@ type Properties { type Presentation { subTotal: String payeeLabel: String - priceLabel: String + priceLabel: String! taxLabel: String tax: String subTotalLabel: String - productLabel: String + productLabel: String! label: String } @@ -104,4 +105,4 @@ type Purchase { id: String! product: Product! timestamp: Long! -} \ No newline at end of file +} From fd8b74f0fb5f09d5723652971d8049b7af82145c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A5vard=20Nor=C3=A9n?= Date: Wed, 9 Oct 2019 12:47:10 +0200 Subject: [PATCH 055/173] Trigger CI From 068ea94ed11fe0fc070c3dc0cb498dbee2c7d2ec Mon Sep 17 00:00:00 2001 From: mpeterss Date: Wed, 9 Oct 2019 19:24:43 +0200 Subject: [PATCH 056/173] Updated rating groups --- prime/config/config.yaml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/prime/config/config.yaml b/prime/config/config.yaml index e13b9a8cd..dc355c910 100644 --- a/prime/config/config.yaml +++ b/prime/config/config.yaml @@ -105,6 +105,9 @@ modules: - serviceId: 1 ratingGroup: 10 rate: Normal + - serviceId: -1 + ratingGroup: 102010001 + rate: Normal - type: api - type: stripe-payment-processor config: From efd26e51843eb94a2fef5b37745a53146bed79d9 Mon Sep 17 00:00:00 2001 From: Prasanth Ullattil Date: Thu, 10 Oct 2019 14:36:35 +0200 Subject: [PATCH 057/173] Remove unused data types in openapi spec for houston --- prime/infra/dev/prime-houston-api.yaml | 15 --------------- prime/infra/prod/prime-houston-api.yaml | 15 --------------- 2 files changed, 30 deletions(-) diff --git a/prime/infra/dev/prime-houston-api.yaml b/prime/infra/dev/prime-houston-api.yaml index da13a8d5e..e1e4c6660 100644 --- a/prime/infra/dev/prime-houston-api.yaml +++ b/prime/infra/dev/prime-houston-api.yaml @@ -409,21 +409,6 @@ paths: type: string definitions: - Pong: - type: object - properties: - timestamp: - type: integer - format: int64 - required: - - timestamp - Uuid: - type: object - properties: - uuid: - type: string - required: - - uuid Context: type: object properties: diff --git a/prime/infra/prod/prime-houston-api.yaml b/prime/infra/prod/prime-houston-api.yaml index 81d2918ca..ad3a3c6bc 100644 --- a/prime/infra/prod/prime-houston-api.yaml +++ b/prime/infra/prod/prime-houston-api.yaml @@ -409,21 +409,6 @@ paths: type: string definitions: - Pong: - type: object - properties: - timestamp: - type: integer - format: int64 - required: - - timestamp - Uuid: - type: object - properties: - uuid: - type: string - required: - - uuid Context: type: object properties: From 29565e68ab912feb4b66613251af2b143365d94d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B8rn=20Remseth?= Date: Thu, 10 Oct 2019 20:18:31 +0200 Subject: [PATCH 058/173] Refactoring --- .../sim-batch-management/upload-sim-batch.go | 2 + .../uploadtoprime/input_batch_test.go | 11 ++++- .../uploadtoprime/upload-sim-batch-lib.go | 47 ++++++++++++++----- 3 files changed, 46 insertions(+), 14 deletions(-) diff --git a/sim-administration/sim-batch-management/upload-sim-batch.go b/sim-administration/sim-batch-management/upload-sim-batch.go index 7b28ac76f..d47347fc4 100644 --- a/sim-administration/sim-batch-management/upload-sim-batch.go +++ b/sim-administration/sim-batch-management/upload-sim-batch.go @@ -1,3 +1,5 @@ +//usr/bin/env go run "$0" "$@"; exit "$?" + package sim_batch_management func main() { diff --git a/sim-administration/sim-batch-management/uploadtoprime/input_batch_test.go b/sim-administration/sim-batch-management/uploadtoprime/input_batch_test.go index 14dd37e9c..cd009e58d 100644 --- a/sim-administration/sim-batch-management/uploadtoprime/input_batch_test.go +++ b/sim-administration/sim-batch-management/uploadtoprime/input_batch_test.go @@ -1,6 +1,7 @@ package uploadtoprime import ( + "fmt" "gotest.tools/assert" "testing" ) @@ -12,7 +13,15 @@ func TestParseInputFileGeneratorCommmandline(t *testing.T) { assert.Equal(t, "OYA_LOLTEL_ACB", parsedBatch.profileType) assert.Equal(t, "20191007", parsedBatch.orderDate) assert.Equal(t, "2019100701", parsedBatch.batchNo) - assert.Equal(t, 10, parsedBatch.length) + assert.Equal(t, 10, parsedBatch.quantity) assert.Equal(t, 894700000000002214, parsedBatch.firstIccid) assert.Equal(t, 242017100012213, parsedBatch.firstImsi) } + + +func TestGenerateInputFile(t *testing.T) { + parsedBatch := ParseInputFileGeneratorCommmandline() + var result = GenerateInputFile(parsedBatch) + fmt.Println(result) +} + diff --git a/sim-administration/sim-batch-management/uploadtoprime/upload-sim-batch-lib.go b/sim-administration/sim-batch-management/uploadtoprime/upload-sim-batch-lib.go index d7c9e0932..cb6ddf8d7 100755 --- a/sim-administration/sim-batch-management/uploadtoprime/upload-sim-batch-lib.go +++ b/sim-administration/sim-batch-management/uploadtoprime/upload-sim-batch-lib.go @@ -182,8 +182,8 @@ func ParseUploadFileGeneratorCommmandline() OutputBatch { lastMsisdn := flag.String("last-msisdn", "Not a valid MSISDN", "Last MSISDN in batch") profileType := flag.String("profile-type", "Not a valid sim profile type", "SIM profile type") batchLengthString := flag.String( - "batch-length", - "Not a valid batch-length, must be an integer", + "batch-quantity", + "Not a valid batch-quantity, must be an integer", "Number of sim cards in batch") // XXX Legal values are Loltel and M1 at this time, how to configure that @@ -224,11 +224,11 @@ func ParseUploadFileGeneratorCommmandline() OutputBatch { batchLength, err := strconv.Atoi(*batchLengthString) if err != nil { - log.Fatalf("Not a valid batch length string '%s'.\n", *batchLengthString) + log.Fatalf("Not a valid batch quantity string '%s'.\n", *batchLengthString) } if batchLength <= 0 { - log.Fatalf("OutputBatch length must be positive, but was '%d'", batchLength) + log.Fatalf("OutputBatch quantity must be positive, but was '%d'", batchLength) } uploadUrl := fmt.Sprintf("http://%s:%s/ostelco/sim-inventory/%s/import-batch/profilevendor/%s?initialHssState=%s", @@ -298,18 +298,39 @@ func ParseUploadFileGeneratorCommmandline() OutputBatch { /// type InputBatch struct { - customer string - profileType string - orderDate string - batchNo string - length int - firstIccid int - firstImsi int + customer string + profileType string + orderDate string + batchNo string + quantity int + firstIccid int + firstImsi int } func ParseInputFileGeneratorCommmandline() InputBatch { - // TODO: This is what we want to happen, but for some reason it isn't happening, so + // TODO: This function should be rewritten to parse a string array and send it to flags. // we need to up our Go-Fu before we can make flag.Parse(arguments) work - return InputBatch{customer: "Loltel", profileType: "OYA_LOLTEL_ACB", orderDate: "20191007", batchNo: "2019100701", length: 10, firstIccid: 894700000000002214, firstImsi:242017100012213} + return InputBatch{customer: "Loltel", profileType: "OYA_LOLTEL_ACB", orderDate: "20191007", batchNo: "2019100701", quantity: 10, firstIccid: 894700000000002214, firstImsi:242017100012213} +} + +func GenerateInputFile(batch InputBatch) string { + result := "*HEADER DESCRIPTION\n" + + "***************************************\n" + + fmt.Sprintf("Customer : Loltel\n") + + fmt.Sprintf("ProfileType : %s\n", batch.profileType) + + fmt.Sprintf("Order Date : %s\n", batch.orderDate) + + fmt.Sprintf("Batch No : %s\n", batch.batchNo) + + fmt.Sprintf("Quantity : %d\n", batch.quantity) + + "***************************************\n" + + "*INPUT VARIABLES\n" + + "***************************************\n" + + "var_In:\n" + + fmt.Sprintf(" ICCID: %d\n", batch.firstIccid) + + fmt.Sprintf("IMSI: %d\n", batch.firstImsi) + + "***************************************\n" + + "*OUTPUT VARIABLES\n" + + "***************************************\n" + + "var_Out: ICCID/IMSI/KI\n"; + return result } \ No newline at end of file From 188e2d428f3ba347d639682dfe1cc1e2624ae3d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B8rn=20Remseth?= Date: Thu, 10 Oct 2019 20:18:58 +0200 Subject: [PATCH 059/173] Make executable --- sim-administration/sim-batch-management/upload-sim-batch.go | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 sim-administration/sim-batch-management/upload-sim-batch.go diff --git a/sim-administration/sim-batch-management/upload-sim-batch.go b/sim-administration/sim-batch-management/upload-sim-batch.go old mode 100644 new mode 100755 From 0a6a6366822ce3e2e3b4d6613fd414f70b5477ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B8rn=20Remseth?= Date: Thu, 10 Oct 2019 20:30:16 +0200 Subject: [PATCH 060/173] A bit of refatoring to make the thing actually compile. --- .../sim-batch-management/upload-sim-batch.go | 12 +++++++----- .../uploadtoprime/upload-sim-batch-lib.go | 6 +++--- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/sim-administration/sim-batch-management/upload-sim-batch.go b/sim-administration/sim-batch-management/upload-sim-batch.go index d47347fc4..81d2ed579 100755 --- a/sim-administration/sim-batch-management/upload-sim-batch.go +++ b/sim-administration/sim-batch-management/upload-sim-batch.go @@ -1,10 +1,12 @@ -//usr/bin/env go run "$0" "$@"; exit "$?" +//usr/bin/env go run "$0" "$@"; exit "$?" -package sim_batch_management +package main + +import "github.com/ostelco/ostelco-core/sim-administration/sim-batch-management/uploadtoprime" func main() { - batch := ParseUploadFileGeneratorCommmandline() - var csvPayload = generateCsvPayload(batch) + batch := uploadtoprime.ParseUploadFileGeneratorCommmandline() + var csvPayload = uploadtoprime.GenerateCsvPayload(batch) - GeneratePostingCurlscript(batch.url, csvPayload) + uploadtoprime.GeneratePostingCurlscript(batch.Url, csvPayload) } \ No newline at end of file diff --git a/sim-administration/sim-batch-management/uploadtoprime/upload-sim-batch-lib.go b/sim-administration/sim-batch-management/uploadtoprime/upload-sim-batch-lib.go index cb6ddf8d7..4296460bd 100755 --- a/sim-administration/sim-batch-management/uploadtoprime/upload-sim-batch-lib.go +++ b/sim-administration/sim-batch-management/uploadtoprime/upload-sim-batch-lib.go @@ -67,7 +67,7 @@ func LuhnChecksum(number int) int { return generateControlDigit(strconv.Itoa(number)) } -func generateCsvPayload(batch OutputBatch) string { +func GenerateCsvPayload(batch OutputBatch) string { var sb strings.Builder sb.WriteString("ICCID, IMSI, MSISDN, PIN1, PIN2, PUK1, PUK2, PROFILE\n") @@ -149,7 +149,7 @@ func checkProfileType(name string, potentialProfileName string) { type OutputBatch struct { profileType string - url string + Url string length int firstMsisdn int msisdnIncrement int @@ -281,7 +281,7 @@ func ParseUploadFileGeneratorCommmandline() OutputBatch { // Return a correctly parsed batch return OutputBatch{ profileType: *profileType, - url: uploadUrl, + Url: uploadUrl, length: loltelutils.Abs(iccidlen), firstIccid: firstIccidInt, iccidIncrement: loltelutils.Sign(iccidlen), From ab4473c8de823f3dd650312c8caf9d8f9946de97 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B8rn=20Remseth?= Date: Thu, 10 Oct 2019 20:31:26 +0200 Subject: [PATCH 061/173] Add TODO --- sim-administration/sim-batch-management/upload-sim-batch.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/sim-administration/sim-batch-management/upload-sim-batch.go b/sim-administration/sim-batch-management/upload-sim-batch.go index 81d2ed579..71e5b93b2 100755 --- a/sim-administration/sim-batch-management/upload-sim-batch.go +++ b/sim-administration/sim-batch-management/upload-sim-batch.go @@ -6,6 +6,9 @@ import "github.com/ostelco/ostelco-core/sim-administration/sim-batch-management/ func main() { batch := uploadtoprime.ParseUploadFileGeneratorCommmandline() + + // TODO: Combine these two into something inside uploadtoprime. + // It's unecessary to break the batch thingy open in this way. var csvPayload = uploadtoprime.GenerateCsvPayload(batch) uploadtoprime.GeneratePostingCurlscript(batch.Url, csvPayload) From 54e00f5373ad3fc62720230f8943aa80f1a2a57c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B8rn=20Remseth?= Date: Thu, 10 Oct 2019 20:43:28 +0200 Subject: [PATCH 062/173] Refactoring to put main function on top --- .../outfile_to_hss_input_converter.go | 11 +++++----- .../outfile_to_hss_input_converter_lib.go | 20 +++++++++---------- ...outfile_to_hss_input_converter_lib_test.go | 2 +- 3 files changed, 17 insertions(+), 16 deletions(-) rename sim-administration/sim-batch-management/{outfileconversion => }/outfile_to_hss_input_converter.go (79%) diff --git a/sim-administration/sim-batch-management/outfileconversion/outfile_to_hss_input_converter.go b/sim-administration/sim-batch-management/outfile_to_hss_input_converter.go similarity index 79% rename from sim-administration/sim-batch-management/outfileconversion/outfile_to_hss_input_converter.go rename to sim-administration/sim-batch-management/outfile_to_hss_input_converter.go index ef965ce35..0a2b45270 100755 --- a/sim-administration/sim-batch-management/outfileconversion/outfile_to_hss_input_converter.go +++ b/sim-administration/sim-batch-management/outfile_to_hss_input_converter.go @@ -26,24 +26,25 @@ * of which are handled by this script). */ -package outfileconversion +package main import ( "fmt" "log" + "github.com/ostelco/ostelco-core/sim-administration/sim-batch-management/outfileconversion" ) func main() { - inputFile, outputFilePrefix := parseOutputToHssConverterCommandLine() + inputFile, outputFilePrefix := outfileconversion.ParseOutputToHssConverterCommandLine() fmt.Println("inputFile = ", inputFile) fmt.Println("outputFilePrefix = ", outputFilePrefix) - outRecord := ReadOutputFile(inputFile) - outputFile := outputFilePrefix + outRecord.outputFileName + ".csv" + outRecord := outfileconversion.ReadOutputFile(inputFile) + outputFile := outputFilePrefix + outRecord.OutputFileName + ".csv" fmt.Println("outputFile = ", outputFile) - err := WriteHssCsvFile(outputFile, outRecord.entries) + err := outfileconversion.WriteHssCsvFile(outputFile, outRecord.Entries) if err != nil { log.Fatal("Couldn't close output file '", outputFilePrefix, "'. Error = '", err, "'") } diff --git a/sim-administration/sim-batch-management/outfileconversion/outfile_to_hss_input_converter_lib.go b/sim-administration/sim-batch-management/outfileconversion/outfile_to_hss_input_converter_lib.go index ad3d59c30..6e53238ff 100644 --- a/sim-administration/sim-batch-management/outfileconversion/outfile_to_hss_input_converter_lib.go +++ b/sim-administration/sim-batch-management/outfileconversion/outfile_to_hss_input_converter_lib.go @@ -20,16 +20,16 @@ type OutputFileRecord struct { Filename string inputVariables map[string]string headerDescription map[string]string - entries []SimEntry - // TODO: As it is today, the noOfEntries is just the number of entries, - // but I may want to change that to be the declared number of entries, - // and then later, dynamically, read in the individual entries + Entries []SimEntry + // TODO: As it is today, the noOfEntries is just the number of Entries, + // but I may want to change that to be the declared number of Entries, + // and then later, dynamically, read in the individual Entries // in a channel that is just piped to the goroutine that writes - // them to file, and fails if the number of declared entries - // differs from the actual number of entries. .... but that is + // them to file, and fails if the number of declared Entries + // differs from the actual number of Entries. .... but that is // for another day. noOfEntries int - outputFileName string + OutputFileName string } const ( @@ -61,7 +61,7 @@ type ParserState struct { /// -func parseOutputToHssConverterCommandLine() (string, string) { +func ParseOutputToHssConverterCommandLine() (string, string) { inputFile := flag.String("input-file", "not a valid filename", "path to .out file used as input file") @@ -201,9 +201,9 @@ func ReadOutputFile(filename string) OutputFileRecord { Filename: filename, inputVariables: state.inputVariables, headerDescription: state.headerDescription, - entries: state.entries, + Entries: state.entries, noOfEntries: declaredNoOfEntities, - outputFileName: getOutputFileName(state), + OutputFileName: getOutputFileName(state), } return result diff --git a/sim-administration/sim-batch-management/outfileconversion/outfile_to_hss_input_converter_lib_test.go b/sim-administration/sim-batch-management/outfileconversion/outfile_to_hss_input_converter_lib_test.go index 9e47ada3c..0d6a9bda7 100644 --- a/sim-administration/sim-batch-management/outfileconversion/outfile_to_hss_input_converter_lib_test.go +++ b/sim-administration/sim-batch-management/outfileconversion/outfile_to_hss_input_converter_lib_test.go @@ -31,6 +31,6 @@ func testReadOutputFile(t *testing.T) { assert.Equal(t, record.inputVariables["IMSI"], "242017100011213") // Check that the output entry set looks legit. - assert.Equal(t, 3, len(record.entries)) + assert.Equal(t, 3, len(record.Entries)) assert.Equal(t, 3, record.noOfEntries) } From 666dfcfa07f71ea8ccca5ce65e1eb33722dd0046 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B8rn=20Remseth?= Date: Fri, 11 Oct 2019 10:07:29 +0200 Subject: [PATCH 063/173] Fix fencepost error. --- .../sim-batch-management/uploadtoprime/upload-sim-batch-lib.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sim-administration/sim-batch-management/uploadtoprime/upload-sim-batch-lib.go b/sim-administration/sim-batch-management/uploadtoprime/upload-sim-batch-lib.go index 4296460bd..ef33d1db8 100755 --- a/sim-administration/sim-batch-management/uploadtoprime/upload-sim-batch-lib.go +++ b/sim-administration/sim-batch-management/uploadtoprime/upload-sim-batch-lib.go @@ -75,7 +75,7 @@ func GenerateCsvPayload(batch OutputBatch) string { var imsi = batch.firstImsi var msisdn = batch.firstMsisdn - for i := 0; i <= batch.length; i++ { + for i := 0; i < batch.length; i++ { iccid := fmt.Sprintf("%d%1d", iccidWithoutLuhnChecksum, LuhnChecksum(iccidWithoutLuhnChecksum)) line := fmt.Sprintf("%s, %d, %d,,,,,%s\n", iccid, imsi, msisdn, batch.profileType) From 993c0e2f16f3b1b9e0a80cffa98e95b6c67a5e97 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B8rn=20Remseth?= Date: Fri, 11 Oct 2019 10:11:52 +0200 Subject: [PATCH 064/173] Renaming to match lib and test filenames --- .../{input_batch_test.go => upload-sim-batch-lib-test.go} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename sim-administration/sim-batch-management/uploadtoprime/{input_batch_test.go => upload-sim-batch-lib-test.go} (100%) diff --git a/sim-administration/sim-batch-management/uploadtoprime/input_batch_test.go b/sim-administration/sim-batch-management/uploadtoprime/upload-sim-batch-lib-test.go similarity index 100% rename from sim-administration/sim-batch-management/uploadtoprime/input_batch_test.go rename to sim-administration/sim-batch-management/uploadtoprime/upload-sim-batch-lib-test.go From cd18976e99a96698dcd85ec69df6d3028c31e269 Mon Sep 17 00:00:00 2001 From: mpeterss Date: Fri, 11 Oct 2019 14:04:31 +0200 Subject: [PATCH 065/173] Support CCR-I without MSCC --- .../main/kotlin/org/ostelco/at/pgw/OcsTest.kt | 33 ++++++++++++ .../ostelco/prime/ocs/core/OnlineCharging.kt | 52 +++++++++++-------- 2 files changed, 64 insertions(+), 21 deletions(-) diff --git a/acceptance-tests/src/main/kotlin/org/ostelco/at/pgw/OcsTest.kt b/acceptance-tests/src/main/kotlin/org/ostelco/at/pgw/OcsTest.kt index 2abba95e8..90aa91aff 100644 --- a/acceptance-tests/src/main/kotlin/org/ostelco/at/pgw/OcsTest.kt +++ b/acceptance-tests/src/main/kotlin/org/ostelco/at/pgw/OcsTest.kt @@ -603,6 +603,39 @@ class OcsTest { } } + /** + * This test CCR-I without any Requested-Service-Units + */ + + @Test + fun creditControlRequestInitNoRsuUnknownUser() { + + val session = testClient.createSession(object{}.javaClass.enclosingMethod.name) ?: fail("Failed to create session") + val request = testClient.createRequest( + DEST_REALM, + DEST_HOST, + session + ) ?: fail("Failed to create request") + + TestHelper.createInitRequest(request.avps, "1337") + + testClient.sendNextRequest(request, session) + + waitForAnswer(session.sessionId) + + run { + val result = testClient.getAnswer(session.sessionId) + assertEquals(DIAMETER_USER_UNKNOWN, result?.resultCode) + val resultAvps = result?.resultAvps ?: fail("Missing AVPs") + assertEquals(DEST_HOST, resultAvps.getAvp(Avp.ORIGIN_HOST).utF8String) + assertEquals(DEST_REALM, resultAvps.getAvp(Avp.ORIGIN_REALM).utF8String) + assertEquals(RequestType.INITIAL_REQUEST.toLong(), resultAvps.getAvp(Avp.CC_REQUEST_TYPE).integer32.toLong()) + val resultMSCC = resultAvps.getAvp(Avp.MULTIPLE_SERVICES_CREDIT_CONTROL) + assertNull(resultMSCC, "There should not be any MSCC if there is no MSCC in the CCR") + } + } + + /** * This test will check that we can handle CCR-U requests that also report CC-Time and CC-Service-Specific-Units diff --git a/ocs-ktc/src/main/kotlin/org/ostelco/prime/ocs/core/OnlineCharging.kt b/ocs-ktc/src/main/kotlin/org/ostelco/prime/ocs/core/OnlineCharging.kt index 834e3d089..2b72bf522 100644 --- a/ocs-ktc/src/main/kotlin/org/ostelco/prime/ocs/core/OnlineCharging.kt +++ b/ocs-ktc/src/main/kotlin/org/ostelco/prime/ocs/core/OnlineCharging.kt @@ -58,30 +58,40 @@ object OnlineCharging : OcsAsyncRequestConsumer { val doneSignal = CountDownLatch(request.msccList.size) - request.msccList.forEach { mscc -> - - val requested = mscc.requested?.totalOctets ?: 0 - if (requested > 0) { - charge(msisdn, mscc, request.serviceInformation.psInformation.sgsnMccMnc) { storeResult -> - storeResult.fold( - { - // FixMe - responseBuilder.resultCode = ResultCode.DIAMETER_USER_UNKNOWN - doneSignal.countDown() - }, - { consumptionResult -> - addGrantedQuota(consumptionResult.granted, mscc, responseBuilder) - addInfo(consumptionResult.balance, mscc, responseBuilder) - reportAnalytics(consumptionResult, request) - Notifications.lowBalanceAlert(msisdn, consumptionResult.granted, consumptionResult.balance) - doneSignal.countDown() - } - ) + if (request.msccCount == 0) { + storage.consume(msisdn, 0L, 0L) { + storeResult -> storeResult.fold( + {responseBuilder.resultCode = ResultCode.DIAMETER_USER_UNKNOWN}, + {responseBuilder.resultCode = ResultCode.DIAMETER_SUCCESS}) + } + } else { + + request.msccList.forEach { mscc -> + + val requested = mscc.requested?.totalOctets ?: 0 + if (requested > 0) { + charge(msisdn, mscc, request.serviceInformation.psInformation.sgsnMccMnc) { storeResult -> + storeResult.fold( + { + // FixMe + responseBuilder.resultCode = ResultCode.DIAMETER_USER_UNKNOWN + doneSignal.countDown() + }, + { consumptionResult -> + addGrantedQuota(consumptionResult.granted, mscc, responseBuilder) + addInfo(consumptionResult.balance, mscc, responseBuilder) + reportAnalytics(consumptionResult, request) + Notifications.lowBalanceAlert(msisdn, consumptionResult.granted, consumptionResult.balance) + doneSignal.countDown() + } + ) + } + } else { + doneSignal.countDown() } - } else { - doneSignal.countDown() } } + doneSignal.await(2, TimeUnit.SECONDS) if (responseBuilder.msccCount == 0) { From 324df34aec2f7feb48f7ece746d943cfa88e133a Mon Sep 17 00:00:00 2001 From: mpeterss Date: Fri, 11 Oct 2019 14:15:36 +0200 Subject: [PATCH 066/173] Refactor moved countdownlatch to the correct if --- .../org/ostelco/prime/ocs/core/OnlineCharging.kt | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/ocs-ktc/src/main/kotlin/org/ostelco/prime/ocs/core/OnlineCharging.kt b/ocs-ktc/src/main/kotlin/org/ostelco/prime/ocs/core/OnlineCharging.kt index 2b72bf522..1d35bf67f 100644 --- a/ocs-ktc/src/main/kotlin/org/ostelco/prime/ocs/core/OnlineCharging.kt +++ b/ocs-ktc/src/main/kotlin/org/ostelco/prime/ocs/core/OnlineCharging.kt @@ -56,16 +56,15 @@ object OnlineCharging : OcsAsyncRequestConsumer { responseBuilder.setRequestId(request.requestId) .setMsisdn(msisdn).setResultCode(ResultCode.DIAMETER_SUCCESS) - val doneSignal = CountDownLatch(request.msccList.size) - if (request.msccCount == 0) { + responseBuilder.validityTime = 86400 storage.consume(msisdn, 0L, 0L) { storeResult -> storeResult.fold( {responseBuilder.resultCode = ResultCode.DIAMETER_USER_UNKNOWN}, {responseBuilder.resultCode = ResultCode.DIAMETER_SUCCESS}) } } else { - + val doneSignal = CountDownLatch(request.msccList.size) request.msccList.forEach { mscc -> val requested = mscc.requested?.totalOctets ?: 0 @@ -90,12 +89,7 @@ object OnlineCharging : OcsAsyncRequestConsumer { doneSignal.countDown() } } - } - - doneSignal.await(2, TimeUnit.SECONDS) - - if (responseBuilder.msccCount == 0) { - responseBuilder.validityTime = 86400 + doneSignal.await(2, TimeUnit.SECONDS) } synchronized(OnlineCharging) { From 389627ffae7d8c6320e18b79642d5382ceb5de14 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B8rn=20Remseth?= Date: Fri, 11 Oct 2019 19:18:55 +0200 Subject: [PATCH 067/173] Lol->Foo --- .../outfile_to_hss_input_converter.go | 2 +- .../uploadtoprime/upload-sim-batch-lib-test.go | 9 ++++++--- .../uploadtoprime/upload-sim-batch-lib.go | 13 ++++--------- 3 files changed, 11 insertions(+), 13 deletions(-) diff --git a/sim-administration/sim-batch-management/outfile_to_hss_input_converter.go b/sim-administration/sim-batch-management/outfile_to_hss_input_converter.go index 0a2b45270..f94fc345c 100755 --- a/sim-administration/sim-batch-management/outfile_to_hss_input_converter.go +++ b/sim-administration/sim-batch-management/outfile_to_hss_input_converter.go @@ -30,8 +30,8 @@ package main import ( "fmt" - "log" "github.com/ostelco/ostelco-core/sim-administration/sim-batch-management/outfileconversion" + "log" ) func main() { diff --git a/sim-administration/sim-batch-management/uploadtoprime/upload-sim-batch-lib-test.go b/sim-administration/sim-batch-management/uploadtoprime/upload-sim-batch-lib-test.go index cd009e58d..e1d249b45 100644 --- a/sim-administration/sim-batch-management/uploadtoprime/upload-sim-batch-lib-test.go +++ b/sim-administration/sim-batch-management/uploadtoprime/upload-sim-batch-lib-test.go @@ -9,8 +9,8 @@ import ( func TestParseInputFileGeneratorCommmandline(t *testing.T) { parsedBatch := ParseInputFileGeneratorCommmandline() - assert.Equal(t, "Loltel", parsedBatch.customer) - assert.Equal(t, "OYA_LOLTEL_ACB", parsedBatch.profileType) + assert.Equal(t, "Footel", parsedBatch.customer) + assert.Equal(t, "BAR_FOOTEL_STD", parsedBatch.profileType) assert.Equal(t, "20191007", parsedBatch.orderDate) assert.Equal(t, "2019100701", parsedBatch.batchNo) assert.Equal(t, 10, parsedBatch.quantity) @@ -18,10 +18,13 @@ func TestParseInputFileGeneratorCommmandline(t *testing.T) { assert.Equal(t, 242017100012213, parsedBatch.firstImsi) } +// TODO: Make a test that checks that the correct number of things are made, +// and also that the right things are made. Already had one fencepost error +// on this stuff. func TestGenerateInputFile(t *testing.T) { parsedBatch := ParseInputFileGeneratorCommmandline() var result = GenerateInputFile(parsedBatch) + fmt.Println(result) } - diff --git a/sim-administration/sim-batch-management/uploadtoprime/upload-sim-batch-lib.go b/sim-administration/sim-batch-management/uploadtoprime/upload-sim-batch-lib.go index ef33d1db8..bdb069639 100755 --- a/sim-administration/sim-batch-management/uploadtoprime/upload-sim-batch-lib.go +++ b/sim-administration/sim-batch-management/uploadtoprime/upload-sim-batch-lib.go @@ -21,7 +21,6 @@ import ( "strings" ) - func GeneratePostingCurlscript(url string, payload string) { fmt.Printf("#!/bin/bash\n") @@ -163,8 +162,6 @@ func IccidWithoutLuhnChecksum(s string) string { return loltelutils.TrimSuffix(s, 1) } - - func ParseUploadFileGeneratorCommmandline() OutputBatch { // @@ -277,7 +274,6 @@ func ParseUploadFileGeneratorCommmandline() OutputBatch { log.Printf("Unknown parameters: %s", flag.Args()) } - // Return a correctly parsed batch return OutputBatch{ profileType: *profileType, @@ -292,7 +288,6 @@ func ParseUploadFileGeneratorCommmandline() OutputBatch { } } - /// /// Input batch management /// @@ -311,13 +306,13 @@ func ParseInputFileGeneratorCommmandline() InputBatch { // TODO: This function should be rewritten to parse a string array and send it to flags. // we need to up our Go-Fu before we can make flag.Parse(arguments) work - return InputBatch{customer: "Loltel", profileType: "OYA_LOLTEL_ACB", orderDate: "20191007", batchNo: "2019100701", quantity: 10, firstIccid: 894700000000002214, firstImsi:242017100012213} + return InputBatch{customer: "Footel", profileType: "BAR_FOOTEL_STD", orderDate: "20191007", batchNo: "2019100701", quantity: 10, firstIccid: 894700000000002214, firstImsi: 242017100012213} } func GenerateInputFile(batch InputBatch) string { result := "*HEADER DESCRIPTION\n" + "***************************************\n" + - fmt.Sprintf("Customer : Loltel\n") + + fmt.Sprintf("Customer :%s\n", batch.customer) + fmt.Sprintf("ProfileType : %s\n", batch.profileType) + fmt.Sprintf("Order Date : %s\n", batch.orderDate) + fmt.Sprintf("Batch No : %s\n", batch.batchNo) + @@ -331,6 +326,6 @@ func GenerateInputFile(batch InputBatch) string { "***************************************\n" + "*OUTPUT VARIABLES\n" + "***************************************\n" + - "var_Out: ICCID/IMSI/KI\n"; + "var_Out: ICCID/IMSI/KI\n" return result -} \ No newline at end of file +} From 1e3a95c7d111c69b09580ce2506d04216d32ae71 Mon Sep 17 00:00:00 2001 From: Vihang Patil Date: Sat, 12 Oct 2019 00:37:23 +0200 Subject: [PATCH 068/173] Added batch provisioning to tools/prime-admin --- tools/prime-admin/config/config.yaml | 5 ++ .../org/ostelco/tools/prime/admin/Main.kt | 87 ++++++++++++++++--- .../tools/prime/admin/actions/Utils.kt | 7 +- 3 files changed, 85 insertions(+), 14 deletions(-) diff --git a/tools/prime-admin/config/config.yaml b/tools/prime-admin/config/config.yaml index e9aeb6aad..df9082dc7 100644 --- a/tools/prime-admin/config/config.yaml +++ b/tools/prime-admin/config/config.yaml @@ -17,6 +17,11 @@ modules: textReader: type: classpathResource filename: /OnNewCustomerAction.kts + allowedRegionsService: + serviceInterface: org.ostelco.prime.storage.graph.AllowedRegionsService + textReader: + type: classpathResource + filename: /AllowedRegionsService.kts # - type: sim-manager # config: # hlrs: [] diff --git a/tools/prime-admin/src/main/kotlin/org/ostelco/tools/prime/admin/Main.kt b/tools/prime-admin/src/main/kotlin/org/ostelco/tools/prime/admin/Main.kt index 3691b8d07..78fc7e6f2 100644 --- a/tools/prime-admin/src/main/kotlin/org/ostelco/tools/prime/admin/Main.kt +++ b/tools/prime-admin/src/main/kotlin/org/ostelco/tools/prime/admin/Main.kt @@ -1,7 +1,15 @@ package org.ostelco.tools.prime.admin import org.ostelco.prime.PrimeApplication +import org.ostelco.tools.prime.admin.actions.approveRegionForCustomer +import org.ostelco.tools.prime.admin.actions.createCustomer +import org.ostelco.tools.prime.admin.actions.createSubscription +import org.ostelco.tools.prime.admin.actions.getAllRegionDetails +import org.ostelco.tools.prime.admin.actions.print +import org.ostelco.tools.prime.admin.actions.printLeft +import org.ostelco.tools.prime.admin.actions.setBalance import org.ostelco.tools.prime.admin.modules.DwEnvModule +import kotlin.math.pow /** * Update `config/config.yaml` to point to valid Neo4j store and Postgres. @@ -27,30 +35,83 @@ fun doActions() { // setup() // index() -// createCustomer(email = "", nickname = "").printLeft() + val email = "" + val nickname = "" + val regionCode = "" + val iccId = "" + val alias = "" + val msisdn = "" + + createCustomer(email = email, nickname = nickname).printLeft() // deleteCustomer(email = "").printLeft() + // set bundle balance + setBalance(email = email, balance = 10 * 2.0.pow(30.0).toLong()).printLeft() + + // check balance + // link to region -// approveRegionForCustomer(email = "", regionCode = "").printLeft() + approveRegionForCustomer(email = email, regionCode = regionCode).printLeft() + // add SimProfile -// createSubscription( -// email = "", -// regionCode = "", -// alias = "", -// msisdn = "").printLeft() + createSubscription( + email = email, + regionCode = regionCode, + alias = alias, + msisdn = msisdn, + iccId = iccId).printLeft() // remove SimProfile + // Get region details +// getRegionDetails(email = email, regionCode = regionCode).print() + + // Get all region details + getAllRegionDetails(email = email).print() + +} + +fun batchProvision() { + + val email = "" + val nickname = "" + + createCustomer(email = email, nickname = nickname).printLeft() + // set bundle balance -// setBalance(email = "", balance = 10 * 2.0.pow(30.0).toLong()).printLeft() + setBalance(email = email, balance = 10 * 2.0.pow(30.0).toLong()).printLeft() - // check bundle balance + // check balance - // Get region details -// getRegionDetails(email = "", regionCode = "").print() + val data = mapOf( + "" to listOf( + SimProfileData(iccId = "", msisdn = "") + ) + ) + + for (regionCode in data.keys) { + + // link to region + approveRegionForCustomer(email = email, regionCode = regionCode).printLeft() + + for (index in 0..9) { + + val (iccId, msisdn) = data[regionCode]?.get(index) ?: throw Exception() + + // add SimProfile + createSubscription( + email = email, + regionCode = regionCode, + alias = "SIM ${index + 1} for $regionCode", + msisdn = msisdn, + iccId = iccId).printLeft() + + } + } // Get all region details -// getAllRegionDetails(email = "").print() + getAllRegionDetails(email = email).print() +} -} \ No newline at end of file +data class SimProfileData(val iccId: String, val msisdn: String) \ No newline at end of file diff --git a/tools/prime-admin/src/main/kotlin/org/ostelco/tools/prime/admin/actions/Utils.kt b/tools/prime-admin/src/main/kotlin/org/ostelco/tools/prime/admin/actions/Utils.kt index c95cdd9e9..f492eae6a 100644 --- a/tools/prime-admin/src/main/kotlin/org/ostelco/tools/prime/admin/actions/Utils.kt +++ b/tools/prime-admin/src/main/kotlin/org/ostelco/tools/prime/admin/actions/Utils.kt @@ -2,6 +2,7 @@ package org.ostelco.tools.prime.admin.actions import arrow.core.Either import org.ostelco.prime.jsonmapper.objectMapper +import org.ostelco.prime.storage.StoreError val formatJson = objectMapper.writerWithDefaultPrettyPrinter()::writeValueAsString @@ -10,7 +11,11 @@ fun formatJson(json: String): String = objectMapper .let(formatJson) fun Either.printLeft() = this.mapLeft { left -> - println(left) + if (left is StoreError) { + println(left.message) + } else { + println(left) + } left } From 4896109d6e1e9b77026a96302dfef3c2728669d4 Mon Sep 17 00:00:00 2001 From: Vihang Patil Date: Wed, 25 Sep 2019 11:50:56 +0200 Subject: [PATCH 069/173] Updated to jdk13. --- .circleci/config.yml | 16 ++++++++-------- .travis.yml | 2 +- acceptance-tests/Dockerfile | 2 +- auth-server/Dockerfile | 2 +- ext-auth-provider/Dockerfile | 2 +- ext-myinfo-emulator/Dockerfile | 2 +- ocsgw/Dockerfile | 2 +- prime/Dockerfile | 2 +- prime/Dockerfile.test | 2 +- prime/cloudbuild.dev.yaml | 2 +- prime/cloudbuild.yaml | 2 +- scaninfo-shredder/Dockerfile | 2 +- scaninfo-shredder/Dockerfile.test | 2 +- .../sm-dp-plus-emulator/Dockerfile | 2 +- 14 files changed, 21 insertions(+), 21 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 74d73980d..af553c156 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -6,7 +6,7 @@ jobs: machine: image: ubuntu-1604:201903-01 environment: - JAVA_HOME: /usr/lib/jvm/zulu12.3.11-ca-jdk12.0.2-linux_x64 + JAVA_HOME: /usr/lib/jvm/zulu13.27.9-ca-jdk13-linux_x64 steps: - checkout @@ -14,8 +14,8 @@ jobs: name: upgrading Java to open-jdk-12 command: | # sudo apt update; sudo apt install -y wget - sudo wget https://cdn.azul.com/zulu/bin/zulu12.3.11-ca-jdk12.0.2-linux_x64.tar.gz -O /tmp/zulu12.3.11-ca-jdk12.0.2-linux_x64.tar.gz - sudo tar -zxf /tmp/zulu12.3.11-ca-jdk12.0.2-linux_x64.tar.gz -C /usr/lib/jvm + sudo wget https://cdn.azul.com/zulu/bin/zulu13.27.9-ca-jdk13-linux_x64.tar.gz -O /tmp/zulu13.27.9-ca-jdk13-linux_x64.tar.gz + sudo tar -zxf /tmp/zulu13.27.9-ca-jdk13-linux_x64.tar.gz -C /usr/lib/jvm - run: # checking for merge conflicts and merging locally if none exist name: merging ${CIRCLE_BRANCH} into develop locally @@ -29,7 +29,7 @@ jobs: - run: name: javac -version command: | - export PATH=/usr/lib/jvm/zulu12.3.11-ca-jdk12.0.2-linux_x64/bin:$PATH + export PATH=/usr/lib/jvm/zulu13.27.9-ca-jdk13-linux_x64/bin:$PATH javac -version - run: name: Pulling Gradle cache @@ -117,7 +117,7 @@ jobs: machine: image: ubuntu-1604:201903-01 environment: - JAVA_HOME: /usr/lib/jvm/zulu12.3.11-ca-jdk12.0.2-linux_x64 + JAVA_HOME: /usr/lib/jvm/zulu13.27.9-ca-jdk13-linux_x64 steps: - checkout @@ -126,8 +126,8 @@ jobs: command: | # sudo apt update; sudo apt install -y wget - sudo wget https://cdn.azul.com/zulu/bin/zulu12.3.11-ca-jdk12.0.2-linux_x64.tar.gz -O /tmp/zulu12.3.11-ca-jdk12.0.2-linux_x64.tar.gz - sudo tar -zxf /tmp/zulu12.3.11-ca-jdk12.0.2-linux_x64.tar.gz -C /usr/lib/jvm + sudo wget https://cdn.azul.com/zulu/bin/zulu13.27.9-ca-jdk13-linux_x64.tar.gz -O /tmp/zulu13.27.9-ca-jdk13-linux_x64.tar.gz + sudo tar -zxf /tmp/zulu13.27.9-ca-jdk13-linux_x64.tar.gz -C /usr/lib/jvm - run: name: Pulling Gradle cache command: | @@ -142,7 +142,7 @@ jobs: - run: name: javac -version command: | - export PATH=/usr/lib/jvm/zulu12.3.11-ca-jdk12.0.2-linux_x64/bin:$PATH + export PATH=/usr/lib/jvm/zulu13.27.9-ca-jdk13-linux_x64/bin:$PATH javac -version - run: name: Gradle Build Prime and ScanInfo Shredder diff --git a/.travis.yml b/.travis.yml index 2125fa561..393cc864f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -10,7 +10,7 @@ cache: install: echo "skip 'gradle assemble' step" -jdk: openjdk12 +jdk: openjdk13 # TODO vihang: fix neo4j-store:test script: diff --git a/acceptance-tests/Dockerfile b/acceptance-tests/Dockerfile index b0c1a8645..bdbbb3dd3 100644 --- a/acceptance-tests/Dockerfile +++ b/acceptance-tests/Dockerfile @@ -1,4 +1,4 @@ -FROM azul/zulu-openjdk:12.0.2 +FROM azul/zulu-openjdk:13 LABEL maintainer="dev@redotter.sg" diff --git a/auth-server/Dockerfile b/auth-server/Dockerfile index d9dbdd240..76cbe7cb6 100644 --- a/auth-server/Dockerfile +++ b/auth-server/Dockerfile @@ -1,4 +1,4 @@ -FROM azul/zulu-openjdk:12.0.2 +FROM azul/zulu-openjdk:13 LABEL maintainer="dev@redotter.sg" diff --git a/ext-auth-provider/Dockerfile b/ext-auth-provider/Dockerfile index 04433f967..ef6da7753 100644 --- a/ext-auth-provider/Dockerfile +++ b/ext-auth-provider/Dockerfile @@ -1,4 +1,4 @@ -FROM azul/zulu-openjdk:12.0.2 +FROM azul/zulu-openjdk:13 LABEL maintainer="dev@redotter.sg" diff --git a/ext-myinfo-emulator/Dockerfile b/ext-myinfo-emulator/Dockerfile index 3a665b845..17be238cd 100644 --- a/ext-myinfo-emulator/Dockerfile +++ b/ext-myinfo-emulator/Dockerfile @@ -1,4 +1,4 @@ -FROM azul/zulu-openjdk:12.0.2 +FROM azul/zulu-openjdk:13 LABEL maintainer="dev@redotter.sg" diff --git a/ocsgw/Dockerfile b/ocsgw/Dockerfile index 78c5471b3..1bca58335 100644 --- a/ocsgw/Dockerfile +++ b/ocsgw/Dockerfile @@ -1,4 +1,4 @@ -FROM azul/zulu-openjdk:12.0.2 +FROM azul/zulu-openjdk:13 LABEL maintainer="dev@redotter.sg" diff --git a/prime/Dockerfile b/prime/Dockerfile index 25a304023..0c5d576a9 100644 --- a/prime/Dockerfile +++ b/prime/Dockerfile @@ -1,4 +1,4 @@ -FROM azul/zulu-openjdk:12.0.2 +FROM azul/zulu-openjdk:13 LABEL maintainer="dev@redotter.sg" diff --git a/prime/Dockerfile.test b/prime/Dockerfile.test index 5351d7467..5f3cc3f03 100644 --- a/prime/Dockerfile.test +++ b/prime/Dockerfile.test @@ -1,6 +1,6 @@ # This Dockerfile is used when running locally using docker-compose for Acceptance Testing. -FROM azul/zulu-openjdk:12.0.2 +FROM azul/zulu-openjdk:13 LABEL maintainer="dev@redotter.sg" diff --git a/prime/cloudbuild.dev.yaml b/prime/cloudbuild.dev.yaml index 497f6e29d..0186972b2 100644 --- a/prime/cloudbuild.dev.yaml +++ b/prime/cloudbuild.dev.yaml @@ -50,7 +50,7 @@ steps: path: /root/out_zip # Build docker images - name: gcr.io/cloud-builders/docker - args: ['build', '--tag=eu.gcr.io/$PROJECT_ID/prime:$SHORT_SHA', '--cache-from', 'azul/zulu-openjdk:12.0.2', 'prime'] + args: ['build', '--tag=eu.gcr.io/$PROJECT_ID/prime:$SHORT_SHA', '--cache-from', 'azul/zulu-openjdk:13', 'prime'] timeout: 120s # Deploy new docker image to Google Kubernetes Engine (GKE) - name: ubuntu diff --git a/prime/cloudbuild.yaml b/prime/cloudbuild.yaml index 6babe6ef8..f802cd79a 100644 --- a/prime/cloudbuild.yaml +++ b/prime/cloudbuild.yaml @@ -50,7 +50,7 @@ steps: path: /root/out_zip # Build docker images - name: gcr.io/cloud-builders/docker - args: ['build', '--tag=eu.gcr.io/$PROJECT_ID/prime:$TAG_NAME', '--cache-from', 'azul/zulu-openjdk:12.0.2', 'prime'] + args: ['build', '--tag=eu.gcr.io/$PROJECT_ID/prime:$TAG_NAME', '--cache-from', 'azul/zulu-openjdk:13', 'prime'] timeout: 120s # Deploy new docker image to Google Kubernetes Engine (GKE) - name: ubuntu diff --git a/scaninfo-shredder/Dockerfile b/scaninfo-shredder/Dockerfile index 7ed4f0288..bd04156a0 100644 --- a/scaninfo-shredder/Dockerfile +++ b/scaninfo-shredder/Dockerfile @@ -1,4 +1,4 @@ -FROM azul/zulu-openjdk:12.0.2 +FROM azul/zulu-openjdk:13 LABEL maintainer="dev@redotter.sg" diff --git a/scaninfo-shredder/Dockerfile.test b/scaninfo-shredder/Dockerfile.test index 2d4c4ad2a..d47fc2f81 100644 --- a/scaninfo-shredder/Dockerfile.test +++ b/scaninfo-shredder/Dockerfile.test @@ -1,4 +1,4 @@ -FROM azul/zulu-openjdk:12.0.2 +FROM azul/zulu-openjdk:13 LABEL maintainer="dev@redotter.sg" diff --git a/sim-administration/sm-dp-plus-emulator/Dockerfile b/sim-administration/sm-dp-plus-emulator/Dockerfile index 4abd863bc..6e139c8fc 100644 --- a/sim-administration/sm-dp-plus-emulator/Dockerfile +++ b/sim-administration/sm-dp-plus-emulator/Dockerfile @@ -1,4 +1,4 @@ -FROM azul/zulu-openjdk:12.0.2 +FROM azul/zulu-openjdk:13 LABEL maintainer="dev@redotter.sg" From f3c7be704b6bd2d57ab8e427ceb242f2fa046a49 Mon Sep 17 00:00:00 2001 From: Vihang Patil Date: Sat, 12 Oct 2019 10:04:53 +0200 Subject: [PATCH 070/173] Updated mockito-koltin library to next generation. --- customer-endpoint/build.gradle.kts | 2 +- .../endpoint/resources/ApplicationTokenResourceTest.kt | 2 +- .../prime/customer/endpoint/resources/CustomerResourceTest.kt | 2 +- .../prime/customer/endpoint/resources/KycResourcesTest.kt | 4 ++-- .../prime/customer/endpoint/resources/ProductsResourceTest.kt | 2 +- .../customer/endpoint/resources/PurchasesResourceTest.kt | 2 +- .../prime/customer/endpoint/resources/RegionsResourceTest.kt | 2 +- .../customer/endpoint/resources/SubscriptionsResourceTest.kt | 2 +- 8 files changed, 9 insertions(+), 9 deletions(-) diff --git a/customer-endpoint/build.gradle.kts b/customer-endpoint/build.gradle.kts index e67e06a93..f8e08f354 100644 --- a/customer-endpoint/build.gradle.kts +++ b/customer-endpoint/build.gradle.kts @@ -16,7 +16,7 @@ dependencies { testRuntimeOnly("io.jsonwebtoken:jjwt-impl:${Version.jjwt}") testRuntimeOnly("io.jsonwebtoken:jjwt-jackson:${Version.jjwt}") - testImplementation("com.nhaarman:mockito-kotlin:1.6.0") + testImplementation("com.nhaarman.mockitokotlin2:mockito-kotlin:${Version.mockitoKotlin}") } apply(from = "../gradle/jacoco.gradle") \ No newline at end of file diff --git a/customer-endpoint/src/test/kotlin/org/ostelco/prime/customer/endpoint/resources/ApplicationTokenResourceTest.kt b/customer-endpoint/src/test/kotlin/org/ostelco/prime/customer/endpoint/resources/ApplicationTokenResourceTest.kt index 8b95593f5..51bf53d94 100644 --- a/customer-endpoint/src/test/kotlin/org/ostelco/prime/customer/endpoint/resources/ApplicationTokenResourceTest.kt +++ b/customer-endpoint/src/test/kotlin/org/ostelco/prime/customer/endpoint/resources/ApplicationTokenResourceTest.kt @@ -2,7 +2,7 @@ package org.ostelco.prime.customer.endpoint.resources import arrow.core.Either import arrow.core.right -import com.nhaarman.mockito_kotlin.argumentCaptor +import com.nhaarman.mockitokotlin2.argumentCaptor import io.dropwizard.auth.AuthDynamicFeature import io.dropwizard.auth.AuthValueFactoryProvider import io.dropwizard.auth.oauth.OAuthCredentialAuthFilter diff --git a/customer-endpoint/src/test/kotlin/org/ostelco/prime/customer/endpoint/resources/CustomerResourceTest.kt b/customer-endpoint/src/test/kotlin/org/ostelco/prime/customer/endpoint/resources/CustomerResourceTest.kt index 5f4e7c831..789a40142 100644 --- a/customer-endpoint/src/test/kotlin/org/ostelco/prime/customer/endpoint/resources/CustomerResourceTest.kt +++ b/customer-endpoint/src/test/kotlin/org/ostelco/prime/customer/endpoint/resources/CustomerResourceTest.kt @@ -1,7 +1,7 @@ package org.ostelco.prime.customer.endpoint.resources import arrow.core.Either -import com.nhaarman.mockito_kotlin.argumentCaptor +import com.nhaarman.mockitokotlin2.argumentCaptor import io.dropwizard.auth.AuthDynamicFeature import io.dropwizard.auth.AuthValueFactoryProvider import io.dropwizard.auth.oauth.OAuthCredentialAuthFilter diff --git a/customer-endpoint/src/test/kotlin/org/ostelco/prime/customer/endpoint/resources/KycResourcesTest.kt b/customer-endpoint/src/test/kotlin/org/ostelco/prime/customer/endpoint/resources/KycResourcesTest.kt index 75b4109c5..5a914deb5 100644 --- a/customer-endpoint/src/test/kotlin/org/ostelco/prime/customer/endpoint/resources/KycResourcesTest.kt +++ b/customer-endpoint/src/test/kotlin/org/ostelco/prime/customer/endpoint/resources/KycResourcesTest.kt @@ -2,8 +2,8 @@ package org.ostelco.prime.customer.endpoint.resources import arrow.core.Either import arrow.core.right -import com.nhaarman.mockito_kotlin.argumentCaptor -import com.nhaarman.mockito_kotlin.eq +import com.nhaarman.mockitokotlin2.argumentCaptor +import com.nhaarman.mockitokotlin2.eq import io.dropwizard.auth.AuthDynamicFeature import io.dropwizard.auth.AuthValueFactoryProvider import io.dropwizard.auth.oauth.OAuthCredentialAuthFilter.Builder diff --git a/customer-endpoint/src/test/kotlin/org/ostelco/prime/customer/endpoint/resources/ProductsResourceTest.kt b/customer-endpoint/src/test/kotlin/org/ostelco/prime/customer/endpoint/resources/ProductsResourceTest.kt index 754505b74..6db9d6115 100644 --- a/customer-endpoint/src/test/kotlin/org/ostelco/prime/customer/endpoint/resources/ProductsResourceTest.kt +++ b/customer-endpoint/src/test/kotlin/org/ostelco/prime/customer/endpoint/resources/ProductsResourceTest.kt @@ -1,7 +1,7 @@ package org.ostelco.prime.customer.endpoint.resources import arrow.core.Either -import com.nhaarman.mockito_kotlin.argumentCaptor +import com.nhaarman.mockitokotlin2.argumentCaptor import io.dropwizard.auth.AuthDynamicFeature import io.dropwizard.auth.AuthValueFactoryProvider import io.dropwizard.auth.oauth.OAuthCredentialAuthFilter diff --git a/customer-endpoint/src/test/kotlin/org/ostelco/prime/customer/endpoint/resources/PurchasesResourceTest.kt b/customer-endpoint/src/test/kotlin/org/ostelco/prime/customer/endpoint/resources/PurchasesResourceTest.kt index 9f246f928..eaedfcaf4 100644 --- a/customer-endpoint/src/test/kotlin/org/ostelco/prime/customer/endpoint/resources/PurchasesResourceTest.kt +++ b/customer-endpoint/src/test/kotlin/org/ostelco/prime/customer/endpoint/resources/PurchasesResourceTest.kt @@ -1,7 +1,7 @@ package org.ostelco.prime.customer.endpoint.resources import arrow.core.Either -import com.nhaarman.mockito_kotlin.argumentCaptor +import com.nhaarman.mockitokotlin2.argumentCaptor import io.dropwizard.auth.AuthDynamicFeature import io.dropwizard.auth.AuthValueFactoryProvider import io.dropwizard.auth.oauth.OAuthCredentialAuthFilter.Builder diff --git a/customer-endpoint/src/test/kotlin/org/ostelco/prime/customer/endpoint/resources/RegionsResourceTest.kt b/customer-endpoint/src/test/kotlin/org/ostelco/prime/customer/endpoint/resources/RegionsResourceTest.kt index 92d12da27..e7a4dc787 100644 --- a/customer-endpoint/src/test/kotlin/org/ostelco/prime/customer/endpoint/resources/RegionsResourceTest.kt +++ b/customer-endpoint/src/test/kotlin/org/ostelco/prime/customer/endpoint/resources/RegionsResourceTest.kt @@ -2,7 +2,7 @@ package org.ostelco.prime.customer.endpoint.resources import arrow.core.Either import arrow.core.right -import com.nhaarman.mockito_kotlin.argumentCaptor +import com.nhaarman.mockitokotlin2.argumentCaptor import io.dropwizard.auth.AuthDynamicFeature import io.dropwizard.auth.AuthValueFactoryProvider import io.dropwizard.auth.oauth.OAuthCredentialAuthFilter.Builder diff --git a/customer-endpoint/src/test/kotlin/org/ostelco/prime/customer/endpoint/resources/SubscriptionsResourceTest.kt b/customer-endpoint/src/test/kotlin/org/ostelco/prime/customer/endpoint/resources/SubscriptionsResourceTest.kt index 8622a3581..a40ebc9b6 100644 --- a/customer-endpoint/src/test/kotlin/org/ostelco/prime/customer/endpoint/resources/SubscriptionsResourceTest.kt +++ b/customer-endpoint/src/test/kotlin/org/ostelco/prime/customer/endpoint/resources/SubscriptionsResourceTest.kt @@ -1,7 +1,7 @@ package org.ostelco.prime.customer.endpoint.resources import arrow.core.Either -import com.nhaarman.mockito_kotlin.argumentCaptor +import com.nhaarman.mockitokotlin2.argumentCaptor import io.dropwizard.auth.AuthDynamicFeature import io.dropwizard.auth.AuthValueFactoryProvider import io.dropwizard.auth.oauth.OAuthCredentialAuthFilter From e03bd3845a4bc8353794cd0f41063e66c6bc58ee Mon Sep 17 00:00:00 2001 From: Vihang Patil Date: Sat, 12 Oct 2019 11:05:26 +0200 Subject: [PATCH 071/173] SimInventory unit tests are ignored because mocking of Koltin Data classes is failing. --- .../org/ostelco/simcards/inventory/SimInventoryUnitTests.kt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sim-administration/simmanager/src/test/kotlin/org/ostelco/simcards/inventory/SimInventoryUnitTests.kt b/sim-administration/simmanager/src/test/kotlin/org/ostelco/simcards/inventory/SimInventoryUnitTests.kt index a6d0926ca..acfb54bec 100644 --- a/sim-administration/simmanager/src/test/kotlin/org/ostelco/simcards/inventory/SimInventoryUnitTests.kt +++ b/sim-administration/simmanager/src/test/kotlin/org/ostelco/simcards/inventory/SimInventoryUnitTests.kt @@ -10,6 +10,7 @@ import org.apache.http.impl.client.CloseableHttpClient import org.junit.AfterClass import org.junit.Before import org.junit.ClassRule +import org.junit.Ignore import org.junit.Test import org.mockito.Mockito import org.mockito.Mockito.mock @@ -26,6 +27,7 @@ import java.util.* import javax.ws.rs.client.Entity import javax.ws.rs.core.MediaType +@Ignore("Fails to Mock config classes which are Kotlin Data classes") class SimInventoryUnitTests { companion object { From 0c5178533dd88e66252d3a5b8550a7a1f373d5a1 Mon Sep 17 00:00:00 2001 From: Vihang Patil Date: Sat, 12 Oct 2019 12:28:03 +0200 Subject: [PATCH 072/173] Used later version of byte-buddy which supports java13 --- .../src/main/kotlin/org/ostelco/prime/gradle/Version.kt | 1 + customer-endpoint/build.gradle.kts | 6 ++++++ graphql/build.gradle.kts | 7 +++++++ sim-administration/simmanager/build.gradle.kts | 6 ++++++ .../ostelco/simcards/inventory/SimInventoryUnitTests.kt | 2 -- 5 files changed, 20 insertions(+), 2 deletions(-) diff --git a/buildSrc/src/main/kotlin/org/ostelco/prime/gradle/Version.kt b/buildSrc/src/main/kotlin/org/ostelco/prime/gradle/Version.kt index ce2eeb67b..71bd9916f 100644 --- a/buildSrc/src/main/kotlin/org/ostelco/prime/gradle/Version.kt +++ b/buildSrc/src/main/kotlin/org/ostelco/prime/gradle/Version.kt @@ -7,6 +7,7 @@ object Version { const val arrowTypeClasses = "0.9.0" const val beam = "2.15.0" + const val byteBuddy = "1.10.1" const val csv = "1.7" const val cxf = "3.3.3" const val dockerComposeJunitRule = "1.3.0" diff --git a/customer-endpoint/build.gradle.kts b/customer-endpoint/build.gradle.kts index f8e08f354..986b3d610 100644 --- a/customer-endpoint/build.gradle.kts +++ b/customer-endpoint/build.gradle.kts @@ -17,6 +17,12 @@ dependencies { testRuntimeOnly("io.jsonwebtoken:jjwt-jackson:${Version.jjwt}") testImplementation("com.nhaarman.mockitokotlin2:mockito-kotlin:${Version.mockitoKotlin}") + testImplementation("net.bytebuddy:byte-buddy:${Version.byteBuddy}") { + because("mockito-kotlin:2.2.0 has byte-buddy:1.9.0 which does not work for java13") + } + testImplementation("net.bytebuddy:byte-buddy-agent:${Version.byteBuddy}") { + because("mockito-kotlin:2.2.0 has byte-buddy:1.9.0 which does not work for java13") + } } apply(from = "../gradle/jacoco.gradle") \ No newline at end of file diff --git a/graphql/build.gradle.kts b/graphql/build.gradle.kts index c078d40f9..3707258ec 100644 --- a/graphql/build.gradle.kts +++ b/graphql/build.gradle.kts @@ -11,7 +11,14 @@ dependencies { implementation("com.graphql-java:graphql-java:13.0") testImplementation("io.dropwizard:dropwizard-testing:${Version.dropwizard}") + testImplementation("org.mockito:mockito-core:${Version.mockito}") + testImplementation("net.bytebuddy:byte-buddy:${Version.byteBuddy}") { + because("mockito-kotlin:2.2.0 has byte-buddy:1.9.0 which does not work for java13") + } + testImplementation("net.bytebuddy:byte-buddy-agent:${Version.byteBuddy}") { + because("mockito-kotlin:2.2.0 has byte-buddy:1.9.0 which does not work for java13") + } testImplementation("io.jsonwebtoken:jjwt-api:${Version.jjwt}") testRuntimeOnly("io.jsonwebtoken:jjwt-impl:${Version.jjwt}") testRuntimeOnly("io.jsonwebtoken:jjwt-jackson:${Version.jjwt}") diff --git a/sim-administration/simmanager/build.gradle.kts b/sim-administration/simmanager/build.gradle.kts index 62d637dd2..2626b4ca3 100644 --- a/sim-administration/simmanager/build.gradle.kts +++ b/sim-administration/simmanager/build.gradle.kts @@ -72,6 +72,12 @@ dependencies { testImplementation("io.dropwizard:dropwizard-testing:${Version.dropwizard}") testImplementation("com.nhaarman.mockitokotlin2:mockito-kotlin:${Version.mockitoKotlin}") + testImplementation("net.bytebuddy:byte-buddy:${Version.byteBuddy}") { + because("mockito-kotlin:2.2.0 has byte-buddy:1.9.0 which does not work for java13") + } + testImplementation("net.bytebuddy:byte-buddy-agent:${Version.byteBuddy}") { + because("mockito-kotlin:2.2.0 has byte-buddy:1.9.0 which does not work for java13") + } testImplementation("org.testcontainers:postgresql:${Version.testcontainers}") testImplementation(project(":sim-administration:sm-dp-plus-emulator")) diff --git a/sim-administration/simmanager/src/test/kotlin/org/ostelco/simcards/inventory/SimInventoryUnitTests.kt b/sim-administration/simmanager/src/test/kotlin/org/ostelco/simcards/inventory/SimInventoryUnitTests.kt index acfb54bec..a6d0926ca 100644 --- a/sim-administration/simmanager/src/test/kotlin/org/ostelco/simcards/inventory/SimInventoryUnitTests.kt +++ b/sim-administration/simmanager/src/test/kotlin/org/ostelco/simcards/inventory/SimInventoryUnitTests.kt @@ -10,7 +10,6 @@ import org.apache.http.impl.client.CloseableHttpClient import org.junit.AfterClass import org.junit.Before import org.junit.ClassRule -import org.junit.Ignore import org.junit.Test import org.mockito.Mockito import org.mockito.Mockito.mock @@ -27,7 +26,6 @@ import java.util.* import javax.ws.rs.client.Entity import javax.ws.rs.core.MediaType -@Ignore("Fails to Mock config classes which are Kotlin Data classes") class SimInventoryUnitTests { companion object { From 235b9df86b0b6a8c8d9f749c00a01ab4ff6964a9 Mon Sep 17 00:00:00 2001 From: Vihang Patil Date: Wed, 25 Sep 2019 13:51:53 +0200 Subject: [PATCH 073/173] Ported jacoco.gradle to kts. --- admin-endpoint/build.gradle.kts | 2 +- appleid-auth-service/build.gradle.kts | 2 +- auth-server/build.gradle.kts | 2 +- build.gradle.kts | 2 +- customer-endpoint/build.gradle.kts | 2 +- customer-support-endpoint/build.gradle.kts | 2 +- data-store/build.gradle.kts | 2 +- dataflow-pipelines/build.gradle.kts | 2 +- diameter-ha/build.gradle.kts | 2 +- diameter-stack/build.gradle.kts | 2 +- document-data-store/build.gradle.kts | 2 +- ekyc/build.gradle.kts | 2 +- email-notifier/build.gradle.kts | 2 +- ext-auth-provider/build.gradle.kts | 2 +- ext-myinfo-emulator/build.gradle.kts | 2 +- gradle/jacoco.gradle | 11 ----------- gradle/jacoco.gradle.kts | 15 +++++++++++++++ graphql/build.gradle.kts | 2 +- imei-lookup/build.gradle.kts | 2 +- jersey/build.gradle.kts | 2 +- kts-engine/build.gradle.kts | 2 +- logging/build.gradle.kts | 2 +- neo4j-store/build.gradle.kts | 2 +- ocs-ktc/build.gradle.kts | 2 +- ocsgw/build.gradle.kts | 2 +- payment-processor/build.gradle.kts | 2 +- prime-modules/build.gradle.kts | 2 +- scaninfo-datastore/build.gradle.kts | 2 +- scaninfo-shredder/build.gradle.kts | 2 +- secure-archive/build.gradle.kts | 2 +- .../es2plus4dropwizard/build.gradle.kts | 2 +- sim-administration/hss-adapter/build.gradle.kts | 2 +- .../jersey-json-schema-validator/build.gradle.kts | 2 +- .../ostelco-dropwizard-utils/build.gradle.kts | 2 +- sim-administration/simcard-utils/build.gradle.kts | 2 +- sim-administration/simmanager/build.gradle.kts | 2 +- .../sm-dp-plus-emulator/build.gradle.kts | 2 +- slack/build.gradle.kts | 2 +- tracing/build.gradle.kts | 2 +- 39 files changed, 52 insertions(+), 48 deletions(-) delete mode 100644 gradle/jacoco.gradle create mode 100644 gradle/jacoco.gradle.kts diff --git a/admin-endpoint/build.gradle.kts b/admin-endpoint/build.gradle.kts index 4a13c6d5d..e16ad95e0 100644 --- a/admin-endpoint/build.gradle.kts +++ b/admin-endpoint/build.gradle.kts @@ -12,4 +12,4 @@ dependencies { testImplementation("io.dropwizard:dropwizard-testing:${Version.dropwizard}") } -apply(from = "../gradle/jacoco.gradle") \ No newline at end of file +apply(from = "../gradle/jacoco.gradle.kts") \ No newline at end of file diff --git a/appleid-auth-service/build.gradle.kts b/appleid-auth-service/build.gradle.kts index 768d49edc..f077d3af7 100644 --- a/appleid-auth-service/build.gradle.kts +++ b/appleid-auth-service/build.gradle.kts @@ -31,4 +31,4 @@ tasks.withType { archiveVersion.set("") } -apply(from = "../gradle/jacoco.gradle") +apply(from = "../gradle/jacoco.gradle.kts") diff --git a/auth-server/build.gradle.kts b/auth-server/build.gradle.kts index 618331bf8..fc02349fb 100644 --- a/auth-server/build.gradle.kts +++ b/auth-server/build.gradle.kts @@ -59,7 +59,7 @@ val integration = tasks.create("integration", Test::class.java) { tasks.build.get().dependsOn(integration) integration.mustRunAfter(tasks.test) -apply(from = "../gradle/jacoco.gradle") +apply(from = "../gradle/jacoco.gradle.kts") idea { module { diff --git a/build.gradle.kts b/build.gradle.kts index 600455f60..5ce6150d4 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -33,7 +33,7 @@ allprojects { } jacoco { - toolVersion = "0.8.2" + toolVersion = "0.8.4" } } diff --git a/customer-endpoint/build.gradle.kts b/customer-endpoint/build.gradle.kts index 986b3d610..7b71ca916 100644 --- a/customer-endpoint/build.gradle.kts +++ b/customer-endpoint/build.gradle.kts @@ -25,4 +25,4 @@ dependencies { } } -apply(from = "../gradle/jacoco.gradle") \ No newline at end of file +apply(from = "../gradle/jacoco.gradle.kts") \ No newline at end of file diff --git a/customer-support-endpoint/build.gradle.kts b/customer-support-endpoint/build.gradle.kts index 4a13c6d5d..e16ad95e0 100644 --- a/customer-support-endpoint/build.gradle.kts +++ b/customer-support-endpoint/build.gradle.kts @@ -12,4 +12,4 @@ dependencies { testImplementation("io.dropwizard:dropwizard-testing:${Version.dropwizard}") } -apply(from = "../gradle/jacoco.gradle") \ No newline at end of file +apply(from = "../gradle/jacoco.gradle.kts") \ No newline at end of file diff --git a/data-store/build.gradle.kts b/data-store/build.gradle.kts index 6635574e0..cb3dc5694 100644 --- a/data-store/build.gradle.kts +++ b/data-store/build.gradle.kts @@ -22,4 +22,4 @@ dependencies { testImplementation("org.mockito:mockito-core:${Version.mockito}") } -apply(from = "../gradle/jacoco.gradle") \ No newline at end of file +apply(from = "../gradle/jacoco.gradle.kts") \ No newline at end of file diff --git a/dataflow-pipelines/build.gradle.kts b/dataflow-pipelines/build.gradle.kts index 3581f69d3..a7cfe120c 100644 --- a/dataflow-pipelines/build.gradle.kts +++ b/dataflow-pipelines/build.gradle.kts @@ -54,4 +54,4 @@ tasks.test { } } -apply(from = "../gradle/jacoco.gradle") \ No newline at end of file +apply(from = "../gradle/jacoco.gradle.kts") \ No newline at end of file diff --git a/diameter-ha/build.gradle.kts b/diameter-ha/build.gradle.kts index 5c9e26b0e..5df14a317 100644 --- a/diameter-ha/build.gradle.kts +++ b/diameter-ha/build.gradle.kts @@ -25,4 +25,4 @@ dependencies { testImplementation("org.mockito:mockito-all:1.10.19") } -apply(from = "../gradle/jacoco.gradle") \ No newline at end of file +apply(from = "../gradle/jacoco.gradle.kts") \ No newline at end of file diff --git a/diameter-stack/build.gradle.kts b/diameter-stack/build.gradle.kts index 97d397e90..bafde004e 100644 --- a/diameter-stack/build.gradle.kts +++ b/diameter-stack/build.gradle.kts @@ -30,4 +30,4 @@ dependencies { testImplementation("org.mockito:mockito-all:1.10.19") } -apply(from = "../gradle/jacoco.gradle") \ No newline at end of file +apply(from = "../gradle/jacoco.gradle.kts") \ No newline at end of file diff --git a/document-data-store/build.gradle.kts b/document-data-store/build.gradle.kts index 2d275e1bd..6c13f3ccd 100644 --- a/document-data-store/build.gradle.kts +++ b/document-data-store/build.gradle.kts @@ -11,4 +11,4 @@ dependencies { testImplementation(kotlin("test-junit")) } -apply(from = "../gradle/jacoco.gradle") \ No newline at end of file +apply(from = "../gradle/jacoco.gradle.kts") \ No newline at end of file diff --git a/ekyc/build.gradle.kts b/ekyc/build.gradle.kts index a4cf59eb6..3d04acc71 100644 --- a/ekyc/build.gradle.kts +++ b/ekyc/build.gradle.kts @@ -37,4 +37,4 @@ tasks.test { } } -apply(from = "../gradle/jacoco.gradle") \ No newline at end of file +apply(from = "../gradle/jacoco.gradle.kts") \ No newline at end of file diff --git a/email-notifier/build.gradle.kts b/email-notifier/build.gradle.kts index f42376003..af10f2800 100644 --- a/email-notifier/build.gradle.kts +++ b/email-notifier/build.gradle.kts @@ -35,4 +35,4 @@ tasks.test { } } -apply(from = "../gradle/jacoco.gradle") \ No newline at end of file +apply(from = "../gradle/jacoco.gradle.kts") \ No newline at end of file diff --git a/ext-auth-provider/build.gradle.kts b/ext-auth-provider/build.gradle.kts index 383623ea8..e74ace227 100644 --- a/ext-auth-provider/build.gradle.kts +++ b/ext-auth-provider/build.gradle.kts @@ -34,4 +34,4 @@ tasks.withType { archiveVersion.set("") } -apply(from = "../gradle/jacoco.gradle") \ No newline at end of file +apply(from = "../gradle/jacoco.gradle.kts") \ No newline at end of file diff --git a/ext-myinfo-emulator/build.gradle.kts b/ext-myinfo-emulator/build.gradle.kts index 84dff7ab6..d4d3b3c33 100644 --- a/ext-myinfo-emulator/build.gradle.kts +++ b/ext-myinfo-emulator/build.gradle.kts @@ -35,4 +35,4 @@ tasks.withType { archiveVersion.set("") } -apply(from = "../gradle/jacoco.gradle") \ No newline at end of file +apply(from = "../gradle/jacoco.gradle.kts") \ No newline at end of file diff --git a/gradle/jacoco.gradle b/gradle/jacoco.gradle deleted file mode 100644 index 1f506a14f..000000000 --- a/gradle/jacoco.gradle +++ /dev/null @@ -1,11 +0,0 @@ -jacocoTestReport { - group = "Reporting" - description = "Generate Jacoco coverage reports after running tests." - getAdditionalSourceDirs().from(files(sourceSets.main.allJava.srcDirs)) - reports { - xml.enabled = true - html.enabled = true - } -} - -check.dependsOn jacocoTestReport \ No newline at end of file diff --git a/gradle/jacoco.gradle.kts b/gradle/jacoco.gradle.kts new file mode 100644 index 000000000..22a95747a --- /dev/null +++ b/gradle/jacoco.gradle.kts @@ -0,0 +1,15 @@ +tasks { + named("jacocoTestReport") { + group = "Reporting" + description = "Generate Jacoco coverage reports after running tests." + additionalSourceDirs.from(files(project.the()["main"].allJava.srcDirs)) + reports { + xml.isEnabled = true + csv.isEnabled = false + html.isEnabled = true + html.destination = file("${buildDir}/jacocoHtml") + } + } +} + +tasks.named("check").get().dependsOn(tasks.named("jacocoTestReport")) \ No newline at end of file diff --git a/graphql/build.gradle.kts b/graphql/build.gradle.kts index 3707258ec..3825d11a6 100644 --- a/graphql/build.gradle.kts +++ b/graphql/build.gradle.kts @@ -24,4 +24,4 @@ dependencies { testRuntimeOnly("io.jsonwebtoken:jjwt-jackson:${Version.jjwt}") } -apply(from = "../gradle/jacoco.gradle") \ No newline at end of file +apply(from = "../gradle/jacoco.gradle.kts") \ No newline at end of file diff --git a/imei-lookup/build.gradle.kts b/imei-lookup/build.gradle.kts index edfc6e0d8..b265a9197 100644 --- a/imei-lookup/build.gradle.kts +++ b/imei-lookup/build.gradle.kts @@ -14,4 +14,4 @@ dependencies { testImplementation(kotlin("test-junit")) } -apply(from = "../gradle/jacoco.gradle") \ No newline at end of file +apply(from = "../gradle/jacoco.gradle.kts") \ No newline at end of file diff --git a/jersey/build.gradle.kts b/jersey/build.gradle.kts index 69d8af8c3..446370f32 100644 --- a/jersey/build.gradle.kts +++ b/jersey/build.gradle.kts @@ -15,4 +15,4 @@ dependencies { testRuntimeOnly("io.jsonwebtoken:jjwt-jackson:${Version.jjwt}") } -apply(from = "../gradle/jacoco.gradle") \ No newline at end of file +apply(from = "../gradle/jacoco.gradle.kts") \ No newline at end of file diff --git a/kts-engine/build.gradle.kts b/kts-engine/build.gradle.kts index ceba37144..fd49560a8 100644 --- a/kts-engine/build.gradle.kts +++ b/kts-engine/build.gradle.kts @@ -24,4 +24,4 @@ dependencies { testImplementation("org.mockito:mockito-core:${Version.mockito}") } -apply(from = "../gradle/jacoco.gradle") \ No newline at end of file +apply(from = "../gradle/jacoco.gradle.kts") \ No newline at end of file diff --git a/logging/build.gradle.kts b/logging/build.gradle.kts index 8d7eaa4e6..963beaf8c 100644 --- a/logging/build.gradle.kts +++ b/logging/build.gradle.kts @@ -14,4 +14,4 @@ dependencies { implementation("com.fasterxml.jackson.core:jackson-databind:${Version.jacksonDatabind}") } -apply(from = "../gradle/jacoco.gradle") \ No newline at end of file +apply(from = "../gradle/jacoco.gradle.kts") \ No newline at end of file diff --git a/neo4j-store/build.gradle.kts b/neo4j-store/build.gradle.kts index fe474adf4..e675bd518 100644 --- a/neo4j-store/build.gradle.kts +++ b/neo4j-store/build.gradle.kts @@ -22,4 +22,4 @@ dependencies { testImplementation("org.mockito:mockito-core:${Version.mockito}") } -apply(from = "../gradle/jacoco.gradle") \ No newline at end of file +apply(from = "../gradle/jacoco.gradle.kts") \ No newline at end of file diff --git a/ocs-ktc/build.gradle.kts b/ocs-ktc/build.gradle.kts index a2fec0b9b..1b6b870ec 100644 --- a/ocs-ktc/build.gradle.kts +++ b/ocs-ktc/build.gradle.kts @@ -19,4 +19,4 @@ dependencies { } -apply(from = "../gradle/jacoco.gradle") \ No newline at end of file +apply(from = "../gradle/jacoco.gradle.kts") \ No newline at end of file diff --git a/ocsgw/build.gradle.kts b/ocsgw/build.gradle.kts index 7e66859d4..06d5a447b 100644 --- a/ocsgw/build.gradle.kts +++ b/ocsgw/build.gradle.kts @@ -63,4 +63,4 @@ tasks.test { } } -apply(from = "../gradle/jacoco.gradle") \ No newline at end of file +apply(from = "../gradle/jacoco.gradle.kts") \ No newline at end of file diff --git a/payment-processor/build.gradle.kts b/payment-processor/build.gradle.kts index d5c9aa66d..4c341fb37 100644 --- a/payment-processor/build.gradle.kts +++ b/payment-processor/build.gradle.kts @@ -42,7 +42,7 @@ configurations.named("integrationImplementation") { tasks.build.get().dependsOn(integration) -apply(from = "../gradle/jacoco.gradle") +apply(from = "../gradle/jacoco.gradle.kts") idea { module { diff --git a/prime-modules/build.gradle.kts b/prime-modules/build.gradle.kts index 176019bb7..b75488bb0 100644 --- a/prime-modules/build.gradle.kts +++ b/prime-modules/build.gradle.kts @@ -34,4 +34,4 @@ dependencies { testImplementation(kotlin("test-junit")) } -apply(from = "../gradle/jacoco.gradle") \ No newline at end of file +apply(from = "../gradle/jacoco.gradle.kts") \ No newline at end of file diff --git a/scaninfo-datastore/build.gradle.kts b/scaninfo-datastore/build.gradle.kts index b516f07bf..b45583f93 100644 --- a/scaninfo-datastore/build.gradle.kts +++ b/scaninfo-datastore/build.gradle.kts @@ -26,4 +26,4 @@ dependencies { testImplementation("org.mockito:mockito-core:${Version.mockito}") } -apply(from = "../gradle/jacoco.gradle") \ No newline at end of file +apply(from = "../gradle/jacoco.gradle.kts") \ No newline at end of file diff --git a/scaninfo-shredder/build.gradle.kts b/scaninfo-shredder/build.gradle.kts index 4593b1668..451fca29c 100644 --- a/scaninfo-shredder/build.gradle.kts +++ b/scaninfo-shredder/build.gradle.kts @@ -53,4 +53,4 @@ tasks.register("version") { } } -apply(from = "../gradle/jacoco.gradle") \ No newline at end of file +apply(from = "../gradle/jacoco.gradle.kts") diff --git a/secure-archive/build.gradle.kts b/secure-archive/build.gradle.kts index 80314f7df..e801c7987 100644 --- a/secure-archive/build.gradle.kts +++ b/secure-archive/build.gradle.kts @@ -19,4 +19,4 @@ dependencies { testImplementation(kotlin("test-junit")) } -apply(from = "../gradle/jacoco.gradle") \ No newline at end of file +apply(from = "../gradle/jacoco.gradle.kts") \ No newline at end of file diff --git a/sim-administration/es2plus4dropwizard/build.gradle.kts b/sim-administration/es2plus4dropwizard/build.gradle.kts index 5310dbf4e..22561d5c5 100644 --- a/sim-administration/es2plus4dropwizard/build.gradle.kts +++ b/sim-administration/es2plus4dropwizard/build.gradle.kts @@ -17,4 +17,4 @@ dependencies { testImplementation("org.mockito:mockito-core:${Version.mockito}") } -apply(from = "../../gradle/jacoco.gradle") \ No newline at end of file +apply(from = "../../gradle/jacoco.gradle.kts") \ No newline at end of file diff --git a/sim-administration/hss-adapter/build.gradle.kts b/sim-administration/hss-adapter/build.gradle.kts index fdfece202..ce45b2637 100644 --- a/sim-administration/hss-adapter/build.gradle.kts +++ b/sim-administration/hss-adapter/build.gradle.kts @@ -100,7 +100,7 @@ protobuf { protobufGeneratedFilesBaseDir = generatedFilesBaseDir } -apply(from = "../../gradle/jacoco.gradle") +apply(from = "../../gradle/jacoco.gradle.kts") idea { module { diff --git a/sim-administration/jersey-json-schema-validator/build.gradle.kts b/sim-administration/jersey-json-schema-validator/build.gradle.kts index f148894e9..439fcce34 100644 --- a/sim-administration/jersey-json-schema-validator/build.gradle.kts +++ b/sim-administration/jersey-json-schema-validator/build.gradle.kts @@ -14,4 +14,4 @@ dependencies { testImplementation("io.dropwizard:dropwizard-testing:${Version.dropwizard}") } -apply(from = "../../gradle/jacoco.gradle") \ No newline at end of file +apply(from = "../../gradle/jacoco.gradle.kts") \ No newline at end of file diff --git a/sim-administration/ostelco-dropwizard-utils/build.gradle.kts b/sim-administration/ostelco-dropwizard-utils/build.gradle.kts index 747437a94..9dc256d31 100644 --- a/sim-administration/ostelco-dropwizard-utils/build.gradle.kts +++ b/sim-administration/ostelco-dropwizard-utils/build.gradle.kts @@ -29,4 +29,4 @@ dependencies { testImplementation("io.dropwizard:dropwizard-testing:${Version.dropwizard}") } -apply(from = "../../gradle/jacoco.gradle") \ No newline at end of file +apply(from = "../../gradle/jacoco.gradle.kts") \ No newline at end of file diff --git a/sim-administration/simcard-utils/build.gradle.kts b/sim-administration/simcard-utils/build.gradle.kts index ccc84137f..01e127e0c 100644 --- a/sim-administration/simcard-utils/build.gradle.kts +++ b/sim-administration/simcard-utils/build.gradle.kts @@ -11,4 +11,4 @@ dependencies { testImplementation("io.dropwizard:dropwizard-testing:${Version.dropwizard}") } -apply(from = "../../gradle/jacoco.gradle") \ No newline at end of file +apply(from = "../../gradle/jacoco.gradle.kts") \ No newline at end of file diff --git a/sim-administration/simmanager/build.gradle.kts b/sim-administration/simmanager/build.gradle.kts index 2626b4ca3..0fcc4ee65 100644 --- a/sim-administration/simmanager/build.gradle.kts +++ b/sim-administration/simmanager/build.gradle.kts @@ -125,7 +125,7 @@ protobuf { protobufGeneratedFilesBaseDir = generatedFilesBaseDir } -apply(from = "../../gradle/jacoco.gradle") +apply(from = "../../gradle/jacoco.gradle.kts") idea { module { diff --git a/sim-administration/sm-dp-plus-emulator/build.gradle.kts b/sim-administration/sm-dp-plus-emulator/build.gradle.kts index d1c858103..eab37890d 100644 --- a/sim-administration/sm-dp-plus-emulator/build.gradle.kts +++ b/sim-administration/sm-dp-plus-emulator/build.gradle.kts @@ -44,4 +44,4 @@ tasks.withType { archiveVersion.set("") } -apply(from = "../../gradle/jacoco.gradle") \ No newline at end of file +apply(from = "../../gradle/jacoco.gradle.kts") \ No newline at end of file diff --git a/slack/build.gradle.kts b/slack/build.gradle.kts index 95ca400ac..bc9b06390 100644 --- a/slack/build.gradle.kts +++ b/slack/build.gradle.kts @@ -35,4 +35,4 @@ tasks.test { } } -apply(from = "../gradle/jacoco.gradle") \ No newline at end of file +apply(from = "../gradle/jacoco.gradle.kts") \ No newline at end of file diff --git a/tracing/build.gradle.kts b/tracing/build.gradle.kts index 08471e6b3..6e8ace845 100644 --- a/tracing/build.gradle.kts +++ b/tracing/build.gradle.kts @@ -31,4 +31,4 @@ tasks.test { } } -apply(from = "../gradle/jacoco.gradle") \ No newline at end of file +apply(from = "../gradle/jacoco.gradle.kts") \ No newline at end of file From 6e6a97a406e10e963680d797f5ff108e7a757133 Mon Sep 17 00:00:00 2001 From: Vihang Patil Date: Mon, 14 Oct 2019 00:40:31 +0200 Subject: [PATCH 074/173] Corrections. --- dataflow-pipelines/build.gradle.kts | 57 ------------------- .../ostelco/prime/storage/graph/Neo4jStore.kt | 4 +- 2 files changed, 2 insertions(+), 59 deletions(-) delete mode 100644 dataflow-pipelines/build.gradle.kts diff --git a/dataflow-pipelines/build.gradle.kts b/dataflow-pipelines/build.gradle.kts deleted file mode 100644 index a7cfe120c..000000000 --- a/dataflow-pipelines/build.gradle.kts +++ /dev/null @@ -1,57 +0,0 @@ -import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar -import org.gradle.api.tasks.testing.logging.TestExceptionFormat.FULL -import org.ostelco.prime.gradle.Version - -plugins { - kotlin("jvm") - application - id("com.github.johnrengelman.shadow") -} - -dependencies { - implementation(kotlin("stdlib-jdk8")) - - implementation(project(":analytics-grpc-api")) - - implementation("com.google.cloud:google-cloud-pubsub:${Version.googleCloudPubSub}") - - implementation("org.apache.beam:beam-sdks-java-core:${Version.beam}") - implementation("org.apache.beam:beam-runners-google-cloud-dataflow-java:${Version.beam}") - - implementation("ch.qos.logback:logback-classic:1.2.3") - - testRuntimeOnly("org.hamcrest:hamcrest-all:1.3") - testRuntimeOnly("org.apache.beam:beam-runners-direct-java:${Version.beam}") - - testImplementation("org.junit.jupiter:junit-jupiter-api:${Version.junit5}") - testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:${Version.junit5}") -} - -application { - mainClassName = "org.ostelco.dataflow.pipelines.DeployPipelineKt" -} - -tasks.withType { - mergeServiceFiles() - archiveClassifier.set("uber") - archiveVersion.set("") -} - -java { - sourceCompatibility = JavaVersion.VERSION_1_8 - targetCompatibility = JavaVersion.VERSION_1_8 -} - -tasks.test { - - // native support to Junit5 in Gradle 4.6+ - useJUnitPlatform { - includeEngines("junit-jupiter") - } - testLogging { - exceptionFormat = FULL - events("PASSED", "FAILED", "SKIPPED") - } -} - -apply(from = "../gradle/jacoco.gradle.kts") \ No newline at end of file diff --git a/neo4j-store/src/main/kotlin/org/ostelco/prime/storage/graph/Neo4jStore.kt b/neo4j-store/src/main/kotlin/org/ostelco/prime/storage/graph/Neo4jStore.kt index a40468acf..b864077ff 100644 --- a/neo4j-store/src/main/kotlin/org/ostelco/prime/storage/graph/Neo4jStore.kt +++ b/neo4j-store/src/main/kotlin/org/ostelco/prime/storage/graph/Neo4jStore.kt @@ -1822,7 +1822,7 @@ object Neo4jStoreSingleton : GraphStore { val customerId = getCustomer(identity = identity).bind().id - // set // apply(from = "../../gradle/jacoco.gradle") KYC Status to Pending + // set ADDRESS KYC Status to Pending setKycStatus( customerId = customerId, regionCode = "sg", @@ -1837,7 +1837,7 @@ object Neo4jStoreSingleton : GraphStore { "address" to address.toByteArray()) ).bind() - // set // apply(from = "../../gradle/jacoco.gradle") KYC Status to Approved + // set ADDRESS KYC Status to Approved setKycStatus( customerId = customerId, regionCode = "sg", From 556d2e78efbbad1e03156af3f75dbe2b85e33561 Mon Sep 17 00:00:00 2001 From: Vihang Patil Date: Mon, 14 Oct 2019 01:12:39 +0200 Subject: [PATCH 075/173] Used older version of jacoco for simmanger tests --- sim-administration/simmanager/build.gradle.kts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/sim-administration/simmanager/build.gradle.kts b/sim-administration/simmanager/build.gradle.kts index 0fcc4ee65..9a143c007 100644 --- a/sim-administration/simmanager/build.gradle.kts +++ b/sim-administration/simmanager/build.gradle.kts @@ -106,6 +106,10 @@ val integration = tasks.create("integration", Test::class.java) { tasks.build.get().dependsOn(integration) +jacoco { + toolVersion = "0.8.3" // because 0.8.4 has a issue - https://github.com/mockito/mockito/issues/1717 +} + var protobufGeneratedFilesBaseDir: String = "" protobuf { @@ -137,4 +141,4 @@ idea { tasks.clean { delete(protobufGeneratedFilesBaseDir) -} +} \ No newline at end of file From a95909d330a4f863161d24e8063e9a739b3788c1 Mon Sep 17 00:00:00 2001 From: mpeterss Date: Fri, 11 Oct 2019 16:12:41 +0200 Subject: [PATCH 076/173] Refactor of the OnlineCharging functionallity --- .../ostelco/prime/ocs/core/OnlineCharging.kt | 116 ++++++++++-------- 1 file changed, 68 insertions(+), 48 deletions(-) diff --git a/ocs-ktc/src/main/kotlin/org/ostelco/prime/ocs/core/OnlineCharging.kt b/ocs-ktc/src/main/kotlin/org/ostelco/prime/ocs/core/OnlineCharging.kt index 1d35bf67f..07d231791 100644 --- a/ocs-ktc/src/main/kotlin/org/ostelco/prime/ocs/core/OnlineCharging.kt +++ b/ocs-ktc/src/main/kotlin/org/ostelco/prime/ocs/core/OnlineCharging.kt @@ -40,64 +40,84 @@ object OnlineCharging : OcsAsyncRequestConsumer { val msisdn = request.msisdn if (msisdn != null) { + if (isKeepAlive(request)) { + handleKeepAlive(request, returnCreditControlAnswer) + } else { + chargeRequest(request, msisdn, returnCreditControlAnswer) + } + } + } - val responseBuilder = CreditControlAnswerInfo.newBuilder() - responseBuilder.setRequestNumber(request.requestNumber) + private fun isKeepAlive(request: CreditControlRequestInfo) : Boolean { + return request.msisdn == "keepalive" + } + + private fun handleKeepAlive(request: CreditControlRequestInfo, returnCreditControlAnswer : (CreditControlAnswerInfo) -> Unit) { + val responseBuilder = CreditControlAnswerInfo.newBuilder() + responseBuilder.requestNumber = request.requestNumber + responseBuilder.setRequestId(request.requestId).setMsisdn("keepalive").resultCode = ResultCode.UNKNOWN + returnCreditControlAnswer(responseBuilder.buildPartial()) + } + + private fun chargeRequest(request: CreditControlRequestInfo, + msisdn: String, + returnCreditControlAnswer: (CreditControlAnswerInfo) -> Unit) { + + CoroutineScope(Dispatchers.Default).launch { - // these are keepalives to keep latency low - if (msisdn == "keepalive") { - responseBuilder.setRequestId(request.requestId).setMsisdn("keepalive").resultCode = ResultCode.UNKNOWN - returnCreditControlAnswer(responseBuilder.buildPartial()) + val responseBuilder = CreditControlAnswerInfo.newBuilder() + responseBuilder.requestNumber = request.requestNumber + responseBuilder.setRequestId(request.requestId) + .setMsisdn(msisdn) + .resultCode = ResultCode.DIAMETER_SUCCESS + + if (request.msccCount == 0) { + responseBuilder.validityTime = 86400 + storage.consume(msisdn, 0L, 0L) { + storeResult -> storeResult.fold( + {responseBuilder.resultCode = ResultCode.DIAMETER_USER_UNKNOWN}, + {responseBuilder.resultCode = ResultCode.DIAMETER_SUCCESS}) + } } else { + chargeMSCCs(request, msisdn, responseBuilder) + } + + synchronized(OnlineCharging) { + returnCreditControlAnswer(responseBuilder.build()) + } + } + } - CoroutineScope(Dispatchers.Default).launch { - - responseBuilder.setRequestId(request.requestId) - .setMsisdn(msisdn).setResultCode(ResultCode.DIAMETER_SUCCESS) - - if (request.msccCount == 0) { - responseBuilder.validityTime = 86400 - storage.consume(msisdn, 0L, 0L) { - storeResult -> storeResult.fold( - {responseBuilder.resultCode = ResultCode.DIAMETER_USER_UNKNOWN}, - {responseBuilder.resultCode = ResultCode.DIAMETER_SUCCESS}) - } - } else { - val doneSignal = CountDownLatch(request.msccList.size) - request.msccList.forEach { mscc -> - - val requested = mscc.requested?.totalOctets ?: 0 - if (requested > 0) { - charge(msisdn, mscc, request.serviceInformation.psInformation.sgsnMccMnc) { storeResult -> - storeResult.fold( - { - // FixMe - responseBuilder.resultCode = ResultCode.DIAMETER_USER_UNKNOWN - doneSignal.countDown() - }, - { consumptionResult -> - addGrantedQuota(consumptionResult.granted, mscc, responseBuilder) - addInfo(consumptionResult.balance, mscc, responseBuilder) - reportAnalytics(consumptionResult, request) - Notifications.lowBalanceAlert(msisdn, consumptionResult.granted, consumptionResult.balance) - doneSignal.countDown() - } - ) - } - } else { + private suspend fun chargeMSCCs(request: CreditControlRequestInfo, + msisdn: String, + responseBuilder : CreditControlAnswerInfo.Builder) { + val doneSignal = CountDownLatch(request.msccList.size) + request.msccList.forEach { mscc -> + + val requested = mscc.requested?.totalOctets ?: 0 + if (requested > 0) { + charge(msisdn, mscc, request.serviceInformation.psInformation.sgsnMccMnc) { storeResult -> + storeResult.fold( + { + // FixMe : should all store errors be unknown user? + responseBuilder.resultCode = ResultCode.DIAMETER_USER_UNKNOWN + doneSignal.countDown() + }, + { consumptionResult -> + addGrantedQuota(consumptionResult.granted, mscc, responseBuilder) + addInfo(consumptionResult.balance, mscc, responseBuilder) + reportAnalytics(consumptionResult, request) + Notifications.lowBalanceAlert(msisdn, consumptionResult.granted, consumptionResult.balance) doneSignal.countDown() } - } - doneSignal.await(2, TimeUnit.SECONDS) - } - - synchronized(OnlineCharging) { - returnCreditControlAnswer(responseBuilder.build()) - } + ) } + } else { + doneSignal.countDown() } } + doneSignal.await(2, TimeUnit.SECONDS) } private fun reportAnalytics(consumptionResult : ConsumptionResult, request: CreditControlRequestInfo) { From c6c6b1a8584061787fac762eb4851a2591e15bcb Mon Sep 17 00:00:00 2001 From: mpeterss Date: Mon, 14 Oct 2019 14:36:09 +0200 Subject: [PATCH 077/173] Refactor --- .../org/ostelco/prime/ocs/core/OnlineCharging.kt | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/ocs-ktc/src/main/kotlin/org/ostelco/prime/ocs/core/OnlineCharging.kt b/ocs-ktc/src/main/kotlin/org/ostelco/prime/ocs/core/OnlineCharging.kt index 07d231791..6545a2946 100644 --- a/ocs-ktc/src/main/kotlin/org/ostelco/prime/ocs/core/OnlineCharging.kt +++ b/ocs-ktc/src/main/kotlin/org/ostelco/prime/ocs/core/OnlineCharging.kt @@ -27,6 +27,7 @@ import java.util.concurrent.TimeUnit object OnlineCharging : OcsAsyncRequestConsumer { var loadUnitTest = false + val keepAliveMsisdn = "keepalive" private val loadAcceptanceTest = System.getenv("LOAD_TESTING") == "true" private val storage: AdminDataSource = getResource() @@ -49,14 +50,15 @@ object OnlineCharging : OcsAsyncRequestConsumer { } - private fun isKeepAlive(request: CreditControlRequestInfo) : Boolean { - return request.msisdn == "keepalive" - } + private fun isKeepAlive(request: CreditControlRequestInfo) : Boolean = request.msisdn == keepAliveMsisdn private fun handleKeepAlive(request: CreditControlRequestInfo, returnCreditControlAnswer : (CreditControlAnswerInfo) -> Unit) { val responseBuilder = CreditControlAnswerInfo.newBuilder() - responseBuilder.requestNumber = request.requestNumber - responseBuilder.setRequestId(request.requestId).setMsisdn("keepalive").resultCode = ResultCode.UNKNOWN + .setRequestNumber(request.requestNumber) + .setRequestId(request.requestId) + .setMsisdn(keepAliveMsisdn) + .setResultCode(ResultCode.UNKNOWN) + returnCreditControlAnswer(responseBuilder.buildPartial()) } From 9f869379ff79c8d734dde417771a31cf6bae4bde Mon Sep 17 00:00:00 2001 From: Vihang Patil Date: Mon, 14 Oct 2019 16:27:06 +0200 Subject: [PATCH 078/173] Formatting --- model/src/main/kotlin/org/ostelco/prime/model/Entities.kt | 3 ++- .../main/kotlin/org/ostelco/prime/storage/graph/Neo4jStore.kt | 3 ++- .../kotlin/org/ostelco/prime/storage/graph/Neo4jStoreTest.kt | 2 +- .../src/main/kotlin/org/ostelco/tools/prime/admin/Main.kt | 1 - 4 files changed, 5 insertions(+), 4 deletions(-) diff --git a/model/src/main/kotlin/org/ostelco/prime/model/Entities.kt b/model/src/main/kotlin/org/ostelco/prime/model/Entities.kt index 296754f50..901627d8e 100644 --- a/model/src/main/kotlin/org/ostelco/prime/model/Entities.kt +++ b/model/src/main/kotlin/org/ostelco/prime/model/Entities.kt @@ -27,7 +27,8 @@ data class Region( data class Offer( val id: String, val segments: Collection = emptyList(), - val products: Collection = emptyList()) + val products: Collection = emptyList() +) data class Segment( val id: String, diff --git a/neo4j-store/src/main/kotlin/org/ostelco/prime/storage/graph/Neo4jStore.kt b/neo4j-store/src/main/kotlin/org/ostelco/prime/storage/graph/Neo4jStore.kt index b864077ff..3ac9b3313 100644 --- a/neo4j-store/src/main/kotlin/org/ostelco/prime/storage/graph/Neo4jStore.kt +++ b/neo4j-store/src/main/kotlin/org/ostelco/prime/storage/graph/Neo4jStore.kt @@ -530,7 +530,7 @@ object Neo4jStoreSingleton : GraphStore { private fun combineRegions(allRegions: Collection, customerRegions: Collection): Collection { // Create a map with default region details - var combined = allRegions.associateBy { it.region.id }.toMutableMap() + val combined = allRegions.associateBy { it.region.id }.toMutableMap() // Overwrite default region details with items from actual region-relations for customer customerRegions.forEach { combined[it.region.id] = it @@ -2249,6 +2249,7 @@ object Neo4jStoreSingleton : GraphStore { createPurchaseRecordRelation(customerId, purchaseRecord) .bind() + // FIXME Moving customer to new segments should be done only based on productClass. /* Offer products to the newly signed up subscriber. */ product.segmentIds.forEach { segmentId -> assignCustomerToSegment( diff --git a/neo4j-store/src/test/kotlin/org/ostelco/prime/storage/graph/Neo4jStoreTest.kt b/neo4j-store/src/test/kotlin/org/ostelco/prime/storage/graph/Neo4jStoreTest.kt index 0f50bca20..dbdd4bbd4 100644 --- a/neo4j-store/src/test/kotlin/org/ostelco/prime/storage/graph/Neo4jStoreTest.kt +++ b/neo4j-store/src/test/kotlin/org/ostelco/prime/storage/graph/Neo4jStoreTest.kt @@ -18,8 +18,8 @@ import org.ostelco.prime.kts.engine.KtsServiceFactory import org.ostelco.prime.kts.engine.reader.ClasspathResourceTextReader import org.ostelco.prime.model.Customer import org.ostelco.prime.model.CustomerRegionStatus.APPROVED -import org.ostelco.prime.model.CustomerRegionStatus.PENDING import org.ostelco.prime.model.CustomerRegionStatus.AVAILABLE +import org.ostelco.prime.model.CustomerRegionStatus.PENDING import org.ostelco.prime.model.Identity import org.ostelco.prime.model.JumioScanData import org.ostelco.prime.model.KycStatus diff --git a/tools/prime-admin/src/main/kotlin/org/ostelco/tools/prime/admin/Main.kt b/tools/prime-admin/src/main/kotlin/org/ostelco/tools/prime/admin/Main.kt index 78fc7e6f2..75f055b19 100644 --- a/tools/prime-admin/src/main/kotlin/org/ostelco/tools/prime/admin/Main.kt +++ b/tools/prime-admin/src/main/kotlin/org/ostelco/tools/prime/admin/Main.kt @@ -53,7 +53,6 @@ fun doActions() { // link to region approveRegionForCustomer(email = email, regionCode = regionCode).printLeft() - // add SimProfile createSubscription( email = email, From 168799d85603dbaf7d164219aad64a085e6f2322 Mon Sep 17 00:00:00 2001 From: Vihang Patil Date: Mon, 14 Oct 2019 16:28:36 +0200 Subject: [PATCH 079/173] Updated DSL by adding Segment entity. --- .../main/kotlin/org/ostelco/prime/dsl/Syntax.kt | 15 ++++++++++++++- .../org/ostelco/prime/storage/graph/Neo4jStore.kt | 3 +-- .../ostelco/prime/storage/graph/model/Model.kt | 4 +++- 3 files changed, 18 insertions(+), 4 deletions(-) diff --git a/neo4j-store/src/main/kotlin/org/ostelco/prime/dsl/Syntax.kt b/neo4j-store/src/main/kotlin/org/ostelco/prime/dsl/Syntax.kt index be46c27f9..4ee3a6a28 100644 --- a/neo4j-store/src/main/kotlin/org/ostelco/prime/dsl/Syntax.kt +++ b/neo4j-store/src/main/kotlin/org/ostelco/prime/dsl/Syntax.kt @@ -10,6 +10,7 @@ import org.ostelco.prime.model.Region import org.ostelco.prime.model.ScanInformation import org.ostelco.prime.model.Subscription import org.ostelco.prime.storage.graph.Neo4jStoreSingleton.customerToBundleRelation +import org.ostelco.prime.storage.graph.Neo4jStoreSingleton.customerToSegmentRelation import org.ostelco.prime.storage.graph.Neo4jStoreSingleton.customerToSimProfileRelation import org.ostelco.prime.storage.graph.Neo4jStoreSingleton.identifiesRelation import org.ostelco.prime.storage.graph.Neo4jStoreSingleton.purchaseRecordRelation @@ -22,6 +23,7 @@ import org.ostelco.prime.storage.graph.Neo4jStoreSingleton.subscriptionSimProfil import org.ostelco.prime.storage.graph.Neo4jStoreSingleton.subscriptionToBundleRelation import org.ostelco.prime.storage.graph.RelationType import org.ostelco.prime.storage.graph.model.Identity +import org.ostelco.prime.storage.graph.model.Segment import org.ostelco.prime.storage.graph.model.SimProfile import kotlin.reflect.KClass @@ -117,6 +119,11 @@ data class CustomerContext(override val id: String) : EntityContext(Cu relationType = purchaseRecordRelation, fromId = id, toId = product.id) + + infix fun belongsToSegment(segment: SegmentContext) = RelationExpression( + relationType = customerToSegmentRelation, + fromId = id, + toId = segment.id) } data class BundleContext(override val id: String) : EntityContext(Bundle::class, id) @@ -146,6 +153,7 @@ data class SubscriptionContext(override val id: String) : EntityContext(ScanInformation::class, id) data class PlanContext(override val id: String) : EntityContext(Plan::class, id) data class ProductContext(override val id: String) : EntityContext(Product::class, id) +data class SegmentContext(override val id: String) : EntityContext(Segment::class, id) // // Identity @@ -286,4 +294,9 @@ infix fun PurchaseRecord.Companion.forPurchasesBy(customer: CustomerContext) = RelationFromClause( relationType = purchaseRecordRelation, fromId = customer.id - ) \ No newline at end of file + ) + +// +// Segment +// +infix fun Segment.Companion.withId(id: String): SegmentContext = SegmentContext(id) \ No newline at end of file diff --git a/neo4j-store/src/main/kotlin/org/ostelco/prime/storage/graph/Neo4jStore.kt b/neo4j-store/src/main/kotlin/org/ostelco/prime/storage/graph/Neo4jStore.kt index 3ac9b3313..e2f7e6cfc 100644 --- a/neo4j-store/src/main/kotlin/org/ostelco/prime/storage/graph/Neo4jStore.kt +++ b/neo4j-store/src/main/kotlin/org/ostelco/prime/storage/graph/Neo4jStore.kt @@ -2448,7 +2448,6 @@ object Neo4jStoreSingleton : GraphStore { private val offerEntity = Offer::class.entityType private val segmentEntity = Segment::class.entityType - private val segmentStore = Segment::class.entityStore private val offerToSegmentRelation = RelationType(OFFERED_TO_SEGMENT, offerEntity, segmentEntity, None::class.java) private val offerToSegmentStore = RelationStore(offerToSegmentRelation) @@ -2456,7 +2455,7 @@ object Neo4jStoreSingleton : GraphStore { private val offerToProductRelation = RelationType(OFFER_HAS_PRODUCT, offerEntity, productEntity, None::class.java) private val offerToProductStore = RelationStore(offerToProductRelation) - private val customerToSegmentRelation = RelationType(BELONG_TO_SEGMENT, customerEntity, segmentEntity, None::class.java) + val customerToSegmentRelation = RelationType(BELONG_TO_SEGMENT, customerEntity, segmentEntity, None::class.java) private val customerToSegmentStore = RelationStore(customerToSegmentRelation) // diff --git a/neo4j-store/src/main/kotlin/org/ostelco/prime/storage/graph/model/Model.kt b/neo4j-store/src/main/kotlin/org/ostelco/prime/storage/graph/model/Model.kt index 858754af2..2a537d718 100644 --- a/neo4j-store/src/main/kotlin/org/ostelco/prime/storage/graph/model/Model.kt +++ b/neo4j-store/src/main/kotlin/org/ostelco/prime/storage/graph/model/Model.kt @@ -33,6 +33,8 @@ data class SimProfile( companion object } -data class Segment(override val id: String) : HasId +data class Segment(override val id: String) : HasId { + companion object +} data class Offer(override val id: String) : HasId \ No newline at end of file From 5dcf68e9ee18084f4f91bec04dc39f1b81dc33e4 Mon Sep 17 00:00:00 2001 From: Vihang Patil Date: Mon, 14 Oct 2019 16:31:54 +0200 Subject: [PATCH 080/173] Added OnRegionApprovedAction --- .../ostelco/prime/storage/graph/KtScripts.kt | 21 ++++++++--- .../prime/storage/graph/Neo4jModule.kt | 6 ++- .../ostelco/prime/storage/graph/Neo4jStore.kt | 21 ++++++----- .../main/resources/OnRegionApprovedAction.kts | 37 +++++++++++++++++++ .../kotlin/org/ostelco/prime/dsl/DSLTest.kt | 18 ++++++--- .../prime/storage/graph/Neo4jLoadTest.kt | 18 ++++++--- .../prime/storage/graph/Neo4jStoreTest.kt | 18 ++++++--- .../ostelco/prime/storage/graph/SchemaTest.kt | 18 ++++++--- prime/config/config.yaml | 15 +++++--- prime/config/test.yaml | 15 +++++--- .../prime/storage/graph/Neo4jStorageTest.kt | 18 ++++++--- 11 files changed, 148 insertions(+), 57 deletions(-) create mode 100644 neo4j-store/src/main/resources/OnRegionApprovedAction.kts diff --git a/neo4j-store/src/main/kotlin/org/ostelco/prime/storage/graph/KtScripts.kt b/neo4j-store/src/main/kotlin/org/ostelco/prime/storage/graph/KtScripts.kt index fd1eaefb5..c59b2e6d1 100644 --- a/neo4j-store/src/main/kotlin/org/ostelco/prime/storage/graph/KtScripts.kt +++ b/neo4j-store/src/main/kotlin/org/ostelco/prime/storage/graph/KtScripts.kt @@ -5,10 +5,6 @@ import org.ostelco.prime.model.Customer import org.ostelco.prime.model.Identity import org.ostelco.prime.storage.StoreError -interface HssNameLookupService { - fun getHssName(regionCode: String): String -} - interface OnNewCustomerAction { fun apply(identity: Identity, customerId: String, @@ -18,5 +14,18 @@ interface OnNewCustomerAction { interface AllowedRegionsService { fun get(identity: Identity, customer: Customer, - transaction: PrimeTransaction): Either> -} \ No newline at end of file + transaction: PrimeTransaction + ): Either> +} + +interface HssNameLookupService { + fun getHssName(regionCode: String): String +} + +interface OnRegionApprovedAction { + fun apply( + customer: Customer, + regionCode: String, + transaction: PrimeTransaction + ): Either +} diff --git a/neo4j-store/src/main/kotlin/org/ostelco/prime/storage/graph/Neo4jModule.kt b/neo4j-store/src/main/kotlin/org/ostelco/prime/storage/graph/Neo4jModule.kt index 431bcad03..ea8080790 100644 --- a/neo4j-store/src/main/kotlin/org/ostelco/prime/storage/graph/Neo4jModule.kt +++ b/neo4j-store/src/main/kotlin/org/ostelco/prime/storage/graph/Neo4jModule.kt @@ -40,9 +40,11 @@ class Neo4jModule : PrimeModule { data class Config( val host: String, val protocol: String, - val hssNameLookupService: KtsServiceFactory, val onNewCustomerAction: KtsServiceFactory, - val allowedRegionsService: KtsServiceFactory) + val allowedRegionsService: KtsServiceFactory, + val onRegionApprovedAction: KtsServiceFactory, + val hssNameLookupService: KtsServiceFactory +) object ConfigRegistry { lateinit var config: Config diff --git a/neo4j-store/src/main/kotlin/org/ostelco/prime/storage/graph/Neo4jStore.kt b/neo4j-store/src/main/kotlin/org/ostelco/prime/storage/graph/Neo4jStore.kt index e2f7e6cfc..5a9722c3f 100644 --- a/neo4j-store/src/main/kotlin/org/ostelco/prime/storage/graph/Neo4jStore.kt +++ b/neo4j-store/src/main/kotlin/org/ostelco/prime/storage/graph/Neo4jStore.kt @@ -290,9 +290,10 @@ object Neo4jStoreSingleton : GraphStore { dataClass = None::class.java) .also { UniqueRelationStore(it) } - private val hssNameLookup: HssNameLookupService = config.hssNameLookupService.getKtsService() private val onNewCustomerAction: OnNewCustomerAction = config.onNewCustomerAction.getKtsService() private val allowedRegionsService: AllowedRegionsService = config.allowedRegionsService.getKtsService() + val onRegionApprovedAction: OnRegionApprovedAction = config.onRegionApprovedAction.getKtsService() + private val hssNameLookup: HssNameLookupService = config.hssNameLookupService.getKtsService() // ------------- // Client Store @@ -1540,10 +1541,11 @@ object Neo4jStoreSingleton : GraphStore { transaction = transaction) .flatMap { if (status == APPROVED) { - assignCustomerToSegment( - customerId = customerId, - segmentId = getInitialSegmentNameForRegion(regionCode, transaction), - transaction = transaction) + onRegionApprovedAction.apply( + customer = customer, + regionCode = regionCode, + transaction = PrimeTransaction(transaction) + ) } else { Unit.right() } @@ -1920,10 +1922,11 @@ object Neo4jStoreSingleton : GraphStore { if (approvedNow) { AuditLog.info(customerId = customerId, message = "Approved for region - $regionCode") - assignCustomerToSegment( - customerId = customerId, - segmentId = getInitialSegmentNameForRegion(regionCode, transaction), - transaction = transaction).bind() + onRegionApprovedAction.apply( + customer = customer, + regionCode = regionCode, + transaction = PrimeTransaction(transaction) + ).bind() } customerRegionRelationStore diff --git a/neo4j-store/src/main/resources/OnRegionApprovedAction.kts b/neo4j-store/src/main/resources/OnRegionApprovedAction.kts new file mode 100644 index 000000000..afac60c99 --- /dev/null +++ b/neo4j-store/src/main/resources/OnRegionApprovedAction.kts @@ -0,0 +1,37 @@ +import arrow.core.Either +import arrow.core.fix +import arrow.core.getOrElse +import arrow.effects.IO +import arrow.instances.either.monad.monad +import org.ostelco.prime.auditlog.AuditLog +import org.ostelco.prime.dsl.WriteTransaction +import org.ostelco.prime.dsl.withId +import org.ostelco.prime.model.Customer +import org.ostelco.prime.storage.StoreError +import org.ostelco.prime.storage.graph.OnRegionApprovedAction +import org.ostelco.prime.storage.graph.PrimeTransaction +import org.ostelco.prime.storage.graph.model.Segment + +object : OnRegionApprovedAction { + + override fun apply( + customer: Customer, + regionCode: String, + transaction: PrimeTransaction + ): Either { + return IO { + Either.monad().binding { + WriteTransaction(transaction).apply { + val segmentId = get(Segment withId "plan-country-${regionCode.toLowerCase()}") + .getOrElse { + get(Segment withId "country-${regionCode.toLowerCase()}").bind() + } + .id + fact { (Customer withId customer.id) belongsToSegment (Segment withId segmentId) }.bind() + AuditLog.info(customer.id, "Added customer to segment - $segmentId") + } + Unit + }.fix() + }.unsafeRunSync() + } +} \ No newline at end of file diff --git a/neo4j-store/src/test/kotlin/org/ostelco/prime/dsl/DSLTest.kt b/neo4j-store/src/test/kotlin/org/ostelco/prime/dsl/DSLTest.kt index d03227982..1683d8c0c 100644 --- a/neo4j-store/src/test/kotlin/org/ostelco/prime/dsl/DSLTest.kt +++ b/neo4j-store/src/test/kotlin/org/ostelco/prime/dsl/DSLTest.kt @@ -81,12 +81,6 @@ class DSLTest { ConfigRegistry.config = Config( host = "0.0.0.0", protocol = "bolt", - hssNameLookupService = KtsServiceFactory( - serviceInterface = "org.ostelco.prime.storage.graph.HssNameLookupService", - textReader = ClasspathResourceTextReader( - filename = "/HssNameLookupService.kts" - ) - ), onNewCustomerAction = KtsServiceFactory( serviceInterface = "org.ostelco.prime.storage.graph.OnNewCustomerAction", textReader = ClasspathResourceTextReader( @@ -98,6 +92,18 @@ class DSLTest { textReader = ClasspathResourceTextReader( filename = "/AllowedRegionsService.kts" ) + ), + onRegionApprovedAction = KtsServiceFactory( + serviceInterface = "org.ostelco.prime.storage.graph.OnRegionApprovedAction", + textReader = ClasspathResourceTextReader( + filename = "/OnRegionApprovedAction.kts" + ) + ), + hssNameLookupService = KtsServiceFactory( + serviceInterface = "org.ostelco.prime.storage.graph.HssNameLookupService", + textReader = ClasspathResourceTextReader( + filename = "/HssNameLookupService.kts" + ) ) ) Neo4jClient.start() diff --git a/neo4j-store/src/test/kotlin/org/ostelco/prime/storage/graph/Neo4jLoadTest.kt b/neo4j-store/src/test/kotlin/org/ostelco/prime/storage/graph/Neo4jLoadTest.kt index 8fe987532..caf9b8b44 100644 --- a/neo4j-store/src/test/kotlin/org/ostelco/prime/storage/graph/Neo4jLoadTest.kt +++ b/neo4j-store/src/test/kotlin/org/ostelco/prime/storage/graph/Neo4jLoadTest.kt @@ -156,12 +156,6 @@ class Neo4jLoadTest { ConfigRegistry.config = Config( host = "0.0.0.0", protocol = "bolt", - hssNameLookupService = KtsServiceFactory( - serviceInterface = "org.ostelco.prime.storage.graph.HssNameLookupService", - textReader = ClasspathResourceTextReader( - filename = "/HssNameLookupService.kts" - ) - ), onNewCustomerAction = KtsServiceFactory( serviceInterface = "org.ostelco.prime.storage.graph.OnNewCustomerAction", textReader = ClasspathResourceTextReader( @@ -173,6 +167,18 @@ class Neo4jLoadTest { textReader = ClasspathResourceTextReader( filename = "/AllowedRegionsService.kts" ) + ), + onRegionApprovedAction = KtsServiceFactory( + serviceInterface = "org.ostelco.prime.storage.graph.OnRegionApprovedAction", + textReader = ClasspathResourceTextReader( + filename = "/OnRegionApprovedAction.kts" + ) + ), + hssNameLookupService = KtsServiceFactory( + serviceInterface = "org.ostelco.prime.storage.graph.HssNameLookupService", + textReader = ClasspathResourceTextReader( + filename = "/HssNameLookupService.kts" + ) ) ) Neo4jClient.start() diff --git a/neo4j-store/src/test/kotlin/org/ostelco/prime/storage/graph/Neo4jStoreTest.kt b/neo4j-store/src/test/kotlin/org/ostelco/prime/storage/graph/Neo4jStoreTest.kt index dbdd4bbd4..ae66081ba 100644 --- a/neo4j-store/src/test/kotlin/org/ostelco/prime/storage/graph/Neo4jStoreTest.kt +++ b/neo4j-store/src/test/kotlin/org/ostelco/prime/storage/graph/Neo4jStoreTest.kt @@ -1081,12 +1081,6 @@ class Neo4jStoreTest { ConfigRegistry.config = Config( host = "0.0.0.0", protocol = "bolt", - hssNameLookupService = KtsServiceFactory( - serviceInterface = "org.ostelco.prime.storage.graph.HssNameLookupService", - textReader = ClasspathResourceTextReader( - filename = "/HssNameLookupService.kts" - ) - ), onNewCustomerAction = KtsServiceFactory( serviceInterface = "org.ostelco.prime.storage.graph.OnNewCustomerAction", textReader = ClasspathResourceTextReader( @@ -1098,6 +1092,18 @@ class Neo4jStoreTest { textReader = ClasspathResourceTextReader( filename = "/AllowedRegionsService.kts" ) + ), + onRegionApprovedAction = KtsServiceFactory( + serviceInterface = "org.ostelco.prime.storage.graph.OnRegionApprovedAction", + textReader = ClasspathResourceTextReader( + filename = "/OnRegionApprovedAction.kts" + ) + ), + hssNameLookupService = KtsServiceFactory( + serviceInterface = "org.ostelco.prime.storage.graph.HssNameLookupService", + textReader = ClasspathResourceTextReader( + filename = "/HssNameLookupService.kts" + ) ) ) Neo4jClient.start() diff --git a/neo4j-store/src/test/kotlin/org/ostelco/prime/storage/graph/SchemaTest.kt b/neo4j-store/src/test/kotlin/org/ostelco/prime/storage/graph/SchemaTest.kt index cc8b74448..bc0fc24cf 100644 --- a/neo4j-store/src/test/kotlin/org/ostelco/prime/storage/graph/SchemaTest.kt +++ b/neo4j-store/src/test/kotlin/org/ostelco/prime/storage/graph/SchemaTest.kt @@ -258,12 +258,6 @@ class SchemaTest { ConfigRegistry.config = Config( host = "0.0.0.0", protocol = "bolt", - hssNameLookupService = KtsServiceFactory( - serviceInterface = "org.ostelco.prime.storage.graph.HssNameLookupService", - textReader = ClasspathResourceTextReader( - filename = "/HssNameLookupService.kts" - ) - ), onNewCustomerAction = KtsServiceFactory( serviceInterface = "org.ostelco.prime.storage.graph.OnNewCustomerAction", textReader = ClasspathResourceTextReader( @@ -275,6 +269,18 @@ class SchemaTest { textReader = ClasspathResourceTextReader( filename = "/AllowedRegionsService.kts" ) + ), + onRegionApprovedAction = KtsServiceFactory( + serviceInterface = "org.ostelco.prime.storage.graph.OnRegionApprovedAction", + textReader = ClasspathResourceTextReader( + filename = "/OnRegionApprovedAction.kts" + ) + ), + hssNameLookupService = KtsServiceFactory( + serviceInterface = "org.ostelco.prime.storage.graph.HssNameLookupService", + textReader = ClasspathResourceTextReader( + filename = "/HssNameLookupService.kts" + ) ) ) Neo4jClient.start() diff --git a/prime/config/config.yaml b/prime/config/config.yaml index 4e6ab0785..03402526e 100644 --- a/prime/config/config.yaml +++ b/prime/config/config.yaml @@ -60,11 +60,6 @@ modules: config: host: ${NEO4J_HOST} protocol: bolt+routing - hssNameLookupService: - serviceInterface: org.ostelco.prime.storage.graph.HssNameLookupService - textReader: - type: file - filename: /config-data/HssNameLookupService.kts onNewCustomerAction: serviceInterface: org.ostelco.prime.storage.graph.OnNewCustomerAction textReader: @@ -75,6 +70,16 @@ modules: textReader: type: file filename: /config-data/AllowedRegionsService.kts + onRegionApprovedAction: + serviceInterface: org.ostelco.prime.storage.graph.OnRegionApprovedAction + textReader: + type: file + filename: /config-data/OnRegionApprovedAction.kts + hssNameLookupService: + serviceInterface: org.ostelco.prime.storage.graph.HssNameLookupService + textReader: + type: file + filename: /config-data/HssNameLookupService.kts - type: analytics config: projectId: ${GCP_PROJECT_ID} diff --git a/prime/config/test.yaml b/prime/config/test.yaml index fbe3c5706..1e49ad3b8 100644 --- a/prime/config/test.yaml +++ b/prime/config/test.yaml @@ -45,11 +45,6 @@ modules: config: host: neo4j protocol: bolt - hssNameLookupService: - serviceInterface: org.ostelco.prime.storage.graph.HssNameLookupService - textReader: - type: classpathResource - filename: /HssNameLookupService.kts onNewCustomerAction: serviceInterface: org.ostelco.prime.storage.graph.OnNewCustomerAction textReader: @@ -60,6 +55,16 @@ modules: textReader: type: classpathResource filename: /AllowedRegionsService.kts + onRegionApprovedAction: + serviceInterface: org.ostelco.prime.storage.graph.OnRegionApprovedAction + textReader: + type: classpathResource + filename: /OnRegionApprovedAction.kts + hssNameLookupService: + serviceInterface: org.ostelco.prime.storage.graph.HssNameLookupService + textReader: + type: classpathResource + filename: /HssNameLookupService.kts - type: analytics config: projectId: ${GCP_PROJECT_ID} diff --git a/prime/src/integration-test/kotlin/org/ostelco/prime/storage/graph/Neo4jStorageTest.kt b/prime/src/integration-test/kotlin/org/ostelco/prime/storage/graph/Neo4jStorageTest.kt index 307273a19..b1df9ec73 100644 --- a/prime/src/integration-test/kotlin/org/ostelco/prime/storage/graph/Neo4jStorageTest.kt +++ b/prime/src/integration-test/kotlin/org/ostelco/prime/storage/graph/Neo4jStorageTest.kt @@ -125,12 +125,6 @@ class Neo4jStorageTest { ConfigRegistry.config = Config( host = "0.0.0.0", protocol = "bolt", - hssNameLookupService = KtsServiceFactory( - serviceInterface = "org.ostelco.prime.storage.graph.HssNameLookupService", - textReader = ClasspathResourceTextReader( - filename = "/HssNameLookupService.kts" - ) - ), onNewCustomerAction = KtsServiceFactory( serviceInterface = "org.ostelco.prime.storage.graph.OnNewCustomerAction", textReader = ClasspathResourceTextReader( @@ -142,6 +136,18 @@ class Neo4jStorageTest { textReader = ClasspathResourceTextReader( filename = "/AllowedRegionsService.kts" ) + ), + onRegionApprovedAction = KtsServiceFactory( + serviceInterface = "org.ostelco.prime.storage.graph.OnRegionApprovedAction", + textReader = ClasspathResourceTextReader( + filename = "/OnRegionApprovedAction.kts" + ) + ), + hssNameLookupService = KtsServiceFactory( + serviceInterface = "org.ostelco.prime.storage.graph.HssNameLookupService", + textReader = ClasspathResourceTextReader( + filename = "/HssNameLookupService.kts" + ) ) ) From 4d1677d6f762eb50811da9b4c38101c20c0a7527 Mon Sep 17 00:00:00 2001 From: Vihang Patil Date: Mon, 14 Oct 2019 16:34:30 +0200 Subject: [PATCH 081/173] Replaced Customer ID with Customer in OnNewCustomerAction --- .../ostelco/prime/storage/graph/KtScripts.kt | 8 ++- .../ostelco/prime/storage/graph/Neo4jStore.kt | 68 +++++++++---------- .../main/resources/OnNewCustomerAction.kts | 7 +- .../prime/storage/graph/Neo4jStoreTest.kt | 30 ++++---- 4 files changed, 58 insertions(+), 55 deletions(-) diff --git a/neo4j-store/src/main/kotlin/org/ostelco/prime/storage/graph/KtScripts.kt b/neo4j-store/src/main/kotlin/org/ostelco/prime/storage/graph/KtScripts.kt index c59b2e6d1..07219b840 100644 --- a/neo4j-store/src/main/kotlin/org/ostelco/prime/storage/graph/KtScripts.kt +++ b/neo4j-store/src/main/kotlin/org/ostelco/prime/storage/graph/KtScripts.kt @@ -6,9 +6,11 @@ import org.ostelco.prime.model.Identity import org.ostelco.prime.storage.StoreError interface OnNewCustomerAction { - fun apply(identity: Identity, - customerId: String, - transaction: PrimeTransaction): Either + fun apply( + identity: Identity, + customer: Customer, + transaction: PrimeTransaction + ): Either } interface AllowedRegionsService { diff --git a/neo4j-store/src/main/kotlin/org/ostelco/prime/storage/graph/Neo4jStore.kt b/neo4j-store/src/main/kotlin/org/ostelco/prime/storage/graph/Neo4jStore.kt index 5a9722c3f..e42e4229c 100644 --- a/neo4j-store/src/main/kotlin/org/ostelco/prime/storage/graph/Neo4jStore.kt +++ b/neo4j-store/src/main/kotlin/org/ostelco/prime/storage/graph/Neo4jStore.kt @@ -371,7 +371,7 @@ object Neo4jStoreSingleton : GraphStore { if (referredBy != null) { fact { (Customer withId referredBy) referred (Customer withId customer.id) }.bind() } - onNewCustomerAction.apply(identity = identity, customerId = customer.id, transaction = transaction).bind() + onNewCustomerAction.apply(identity = identity, customer = customer, transaction = transaction).bind() AuditLog.info(customerId = customer.id, message = "Customer is created") }.fix() }.unsafeRunSync() @@ -1514,26 +1514,26 @@ object Neo4jStoreSingleton : GraphStore { } internal fun createCustomerRegionSetting( - customerId: String, + customer: Customer, status: CustomerRegionStatus, regionCode: String): Either = writeTransaction { createCustomerRegionSetting( - customerId = customerId, + customer = customer, status = status, regionCode = regionCode, transaction = transaction) } private fun createCustomerRegionSetting( - customerId: String, + customer: Customer, status: CustomerRegionStatus, regionCode: String, transaction: PrimeTransaction): Either = customerRegionRelationStore .createIfAbsent( - fromId = customerId, + fromId = customer.id, relation = CustomerRegion( status = status, kycStatusMap = getKycStatusMapForRegion(regionCode)), @@ -1579,29 +1579,29 @@ object Neo4jStoreSingleton : GraphStore { identity: org.ostelco.prime.model.Identity, regionCode: String): Either = writeTransaction { - getCustomerId(identity = identity) - .flatMap { customerId -> + getCustomer(identity = identity) + .flatMap { customer -> // Generate new id for the scan val scanId = UUID.randomUUID().toString() val newScan = ScanInformation(scanId = scanId, countryCode = regionCode, status = ScanStatus.PENDING, scanResult = null) createCustomerRegionSetting( - customerId = customerId, status = PENDING, regionCode = regionCode.toLowerCase(), transaction = transaction) + customer = customer, status = PENDING, regionCode = regionCode.toLowerCase(), transaction = transaction) .flatMap { create { newScan } } .flatMap { - scanInformationRelationStore.createIfAbsent(customerId, newScan.id, transaction) + scanInformationRelationStore.createIfAbsent(customer.id, newScan.id, transaction) } .flatMap { setKycStatus( - customerId = customerId, + customer = customer, regionCode = regionCode.toLowerCase(), kycType = JUMIO, kycStatus = KycStatus.PENDING, transaction = transaction) } .map { - AuditLog.info(customerId = customerId, message = "Created new Jumio scan id - ${newScan.id}") + AuditLog.info(customerId = customer.id, message = "Created new Jumio scan id - ${newScan.id}") newScan } } @@ -1668,7 +1668,7 @@ object Neo4jStoreSingleton : GraphStore { ) logger.info(NOTIFY_OPS_MARKER, "Jumio verification succeeded for ${customer.contactEmail} Info: $extendedStatus") setKycStatus( - customerId = customer.id, + customer = customer, regionCode = scanInformation.countryCode.toLowerCase(), kycType = JUMIO, transaction = transaction) @@ -1683,7 +1683,7 @@ object Neo4jStoreSingleton : GraphStore { ) logger.info(NOTIFY_OPS_MARKER, "Jumio verification failed for ${customer.contactEmail} Info: $extendedStatus") setKycStatus( - customerId = customer.id, + customer = customer, regionCode = scanInformation.countryCode.toLowerCase(), kycType = JUMIO, kycStatus = REJECTED, @@ -1709,11 +1709,11 @@ object Neo4jStoreSingleton : GraphStore { return IO { Either.monad().binding { - val customerId = getCustomer(identity = identity).bind().id + val customer = getCustomer(identity = identity).bind() // set MY_INFO KYC Status to Pending setKycStatus( - customerId = customerId, + customer = customer, regionCode = "sg", kycType = MY_INFO, kycStatus = KycStatus.PENDING).bind() @@ -1745,7 +1745,7 @@ object Neo4jStoreSingleton : GraphStore { message = "Failed to fetched MyInfo $version").left().bind() secureArchiveService.archiveEncrypted( - customerId = customerId, + customerId = customer.id, fileName = "myInfoData", regionCodes = listOf("sg"), dataMap = mapOf( @@ -1756,7 +1756,7 @@ object Neo4jStoreSingleton : GraphStore { // set MY_INFO KYC Status to Approved setKycStatus( - customerId = customerId, + customer = customer, regionCode = "sg", kycType = MY_INFO).bind() @@ -1780,11 +1780,11 @@ object Neo4jStoreSingleton : GraphStore { logger.info("checkNricFinIdUsingDave for $nricFinId") - val customerId = getCustomer(identity = identity).bind().id + val customer = getCustomer(identity = identity).bind() // set NRIC_FIN KYC Status to Pending setKycStatus( - customerId = customerId, + customer = customer, regionCode = "sg", kycType = NRIC_FIN, kycStatus = KycStatus.PENDING).bind() @@ -1797,7 +1797,7 @@ object Neo4jStoreSingleton : GraphStore { } secureArchiveService.archiveEncrypted( - customerId = customerId, + customerId = customer.id, fileName = "nricFin", regionCodes = listOf("sg"), dataMap = mapOf("nricFinId" to nricFinId.toByteArray()) @@ -1805,7 +1805,7 @@ object Neo4jStoreSingleton : GraphStore { // set NRIC_FIN KYC Status to Approved setKycStatus( - customerId = customerId, + customer = customer, regionCode = "sg", kycType = NRIC_FIN).bind() }.fix() @@ -1822,17 +1822,17 @@ object Neo4jStoreSingleton : GraphStore { return IO { Either.monad().binding { - val customerId = getCustomer(identity = identity).bind().id + val customer = getCustomer(identity = identity).bind() // set ADDRESS KYC Status to Pending setKycStatus( - customerId = customerId, + customer = customer, regionCode = "sg", kycType = ADDRESS, kycStatus = KycStatus.PENDING).bind() secureArchiveService.archiveEncrypted( - customerId = customerId, + customerId = customer.id, fileName = "address", regionCodes = listOf("sg"), dataMap = mapOf( @@ -1841,7 +1841,7 @@ object Neo4jStoreSingleton : GraphStore { // set ADDRESS KYC Status to Approved setKycStatus( - customerId = customerId, + customer = customer, regionCode = "sg", kycType = ADDRESS).bind() }.fix() @@ -1853,13 +1853,13 @@ object Neo4jStoreSingleton : GraphStore { // internal fun setKycStatus( - customerId: String, + customer: Customer, regionCode: String, kycType: KycType, kycStatus: KycStatus = KycStatus.APPROVED) = writeTransaction { setKycStatus( - customerId = customerId, + customer = customer, regionCode = regionCode, kycType = kycType, kycStatus = kycStatus, @@ -1870,7 +1870,7 @@ object Neo4jStoreSingleton : GraphStore { // FIXME: vihang This implementation has risk of loss of data due during concurrency to stale read since it does // READ-UPDATE-WRITE. private fun setKycStatus( - customerId: String, + customer: Customer, regionCode: String, kycType: KycType, kycStatus: KycStatus = KycStatus.APPROVED, @@ -1882,7 +1882,7 @@ object Neo4jStoreSingleton : GraphStore { val approvedKycTypeSetList = getApprovedKycTypeSetList(regionCode) val existingCustomerRegion = customerRegionRelationStore.get( - fromId = customerId, + fromId = customer.id, toId = regionCode, transaction = transaction) .getOrElse { CustomerRegion(status = PENDING, kycStatusMap = getKycStatusMapForRegion(regionCode)) } @@ -1898,12 +1898,12 @@ object Neo4jStoreSingleton : GraphStore { if (existingKycStatus != newKycStatus) { if (kycStatus == newKycStatus) { - AuditLog.info(customerId = customerId, message = "Setting $kycType status from $existingKycStatus to $newKycStatus") + AuditLog.info(customerId = customer.id, message = "Setting $kycType status from $existingKycStatus to $newKycStatus") } else { - AuditLog.info(customerId = customerId, message = "Setting $kycType status from $existingKycStatus to $newKycStatus instead of $kycStatus") + AuditLog.info(customerId = customer.id, message = "Setting $kycType status from $existingKycStatus to $newKycStatus instead of $kycStatus") } } else { - AuditLog.info(customerId = customerId, message = "Ignoring setting $kycType status to $kycStatus since it is already $existingKycStatus") + AuditLog.info(customerId = customer.id, message = "Ignoring setting $kycType status to $kycStatus since it is already $existingKycStatus") } val newKycStatusMap = existingKycStatusMap.copy(key = kycType, value = newKycStatus) @@ -1921,7 +1921,7 @@ object Neo4jStoreSingleton : GraphStore { } if (approvedNow) { - AuditLog.info(customerId = customerId, message = "Approved for region - $regionCode") + AuditLog.info(customerId = customer.id, message = "Approved for region - $regionCode") onRegionApprovedAction.apply( customer = customer, regionCode = regionCode, @@ -1931,7 +1931,7 @@ object Neo4jStoreSingleton : GraphStore { customerRegionRelationStore .createOrUpdate( - fromId = customerId, + fromId = customer.id, relation = CustomerRegion(status = newStatus, kycStatusMap = newKycStatusMap), toId = regionCode, transaction = transaction) diff --git a/neo4j-store/src/main/resources/OnNewCustomerAction.kts b/neo4j-store/src/main/resources/OnNewCustomerAction.kts index 2f3fda0a8..add8544bc 100644 --- a/neo4j-store/src/main/resources/OnNewCustomerAction.kts +++ b/neo4j-store/src/main/resources/OnNewCustomerAction.kts @@ -4,6 +4,7 @@ import arrow.effects.IO import arrow.instances.either.monad.monad import org.ostelco.prime.dsl.WriteTransaction import org.ostelco.prime.dsl.withSku +import org.ostelco.prime.model.Customer import org.ostelco.prime.model.Identity import org.ostelco.prime.model.Product import org.ostelco.prime.model.PurchaseRecord @@ -17,7 +18,7 @@ import java.util.* object : OnNewCustomerAction { override fun apply(identity: Identity, - customerId: String, + customer: Customer, transaction: PrimeTransaction): Either { val welcomePackProductSku = "2GB_FREE_ON_JOINING" @@ -27,7 +28,7 @@ object : OnNewCustomerAction { WriteTransaction(transaction).apply { val product = get(Product withSku welcomePackProductSku).bind() createPurchaseRecordRelation( - customerId, + customer.id, PurchaseRecord( id = UUID.randomUUID().toString(), product = product, @@ -35,7 +36,7 @@ object : OnNewCustomerAction { ) ).bind() applyProduct( - customerId = customerId, + customerId = customer.id, product = product ).bind() } diff --git a/neo4j-store/src/test/kotlin/org/ostelco/prime/storage/graph/Neo4jStoreTest.kt b/neo4j-store/src/test/kotlin/org/ostelco/prime/storage/graph/Neo4jStoreTest.kt index ae66081ba..bfa4803fd 100644 --- a/neo4j-store/src/test/kotlin/org/ostelco/prime/storage/graph/Neo4jStoreTest.kt +++ b/neo4j-store/src/test/kotlin/org/ostelco/prime/storage/graph/Neo4jStoreTest.kt @@ -271,7 +271,7 @@ class Neo4jStoreTest { .mapLeft { fail(it.message) } Neo4jStoreSingleton.createCustomerRegionSetting( - customerId = CUSTOMER.id, + customer = CUSTOMER, status = APPROVED, regionCode = REGION_CODE) @@ -652,7 +652,7 @@ class Neo4jStoreTest { customer = CUSTOMER).isRight()) assert(Neo4jStoreSingleton.createCustomerRegionSetting( - customerId = CUSTOMER.id, + customer = CUSTOMER, status = APPROVED, regionCode = REGION_CODE).isRight()) @@ -750,11 +750,11 @@ class Neo4jStoreTest { customer = CUSTOMER).isRight()) assert(Neo4jStoreSingleton.createCustomerRegionSetting( - customerId = CUSTOMER.id, + customer = CUSTOMER, status = APPROVED, regionCode = "no").isRight()) assert(Neo4jStoreSingleton.createCustomerRegionSetting( - customerId = CUSTOMER.id, + customer = CUSTOMER, status = PENDING, regionCode = "sg").isRight()) @@ -794,11 +794,11 @@ class Neo4jStoreTest { customer = CUSTOMER).isRight()) assert(Neo4jStoreSingleton.createCustomerRegionSetting( - customerId = CUSTOMER.id, + customer = CUSTOMER, status = APPROVED, regionCode = "no").isRight()) assert(Neo4jStoreSingleton.createCustomerRegionSetting( - customerId = CUSTOMER.id, + customer = CUSTOMER, status = PENDING, regionCode = "sg").isRight()) @@ -833,11 +833,11 @@ class Neo4jStoreTest { customer = CUSTOMER).isRight()) assert(Neo4jStoreSingleton.createCustomerRegionSetting( - customerId = CUSTOMER.id, + customer = CUSTOMER, status = APPROVED, regionCode = "no").isRight()) assert(Neo4jStoreSingleton.createCustomerRegionSetting( - customerId = CUSTOMER.id, + customer = CUSTOMER, status = PENDING, regionCode = "sg").isRight()) @@ -897,11 +897,11 @@ class Neo4jStoreTest { customer = CUSTOMER).isRight()) assert(Neo4jStoreSingleton.createCustomerRegionSetting( - customerId = CUSTOMER.id, + customer = CUSTOMER, status = APPROVED, regionCode = "no").isRight()) assert(Neo4jStoreSingleton.createCustomerRegionSetting( - customerId = CUSTOMER.id, + customer = CUSTOMER, status = PENDING, regionCode = "sg").isRight()) @@ -958,7 +958,7 @@ class Neo4jStoreTest { } Neo4jStoreSingleton.setKycStatus( - customerId = CUSTOMER.id, + customer = CUSTOMER, regionCode = "sg", kycType = MY_INFO) .mapLeft { fail(it.message) } @@ -973,7 +973,7 @@ class Neo4jStoreTest { }) Neo4jStoreSingleton.setKycStatus( - customerId = CUSTOMER.id, + customer = CUSTOMER, regionCode = "sg", kycType = ADDRESS) .mapLeft { fail(it.message) } @@ -1010,7 +1010,7 @@ class Neo4jStoreTest { } Neo4jStoreSingleton.setKycStatus( - customerId = CUSTOMER.id, + customer = CUSTOMER, regionCode = "sg", kycType = NRIC_FIN) @@ -1024,7 +1024,7 @@ class Neo4jStoreTest { }) Neo4jStoreSingleton.setKycStatus( - customerId = CUSTOMER.id, + customer = CUSTOMER, regionCode = "sg", kycType = JUMIO) @@ -1038,7 +1038,7 @@ class Neo4jStoreTest { }) Neo4jStoreSingleton.setKycStatus( - customerId = CUSTOMER.id, + customer = CUSTOMER, regionCode = "sg", kycType = ADDRESS) From 76010b9e2d813a80adc210ad387e5f0373aa7275 Mon Sep 17 00:00:00 2001 From: Vihang Patil Date: Mon, 14 Oct 2019 16:36:08 +0200 Subject: [PATCH 082/173] Removed util function to get default segment for a region. --- .../ostelco/prime/storage/graph/Neo4jStore.kt | 11 ------- .../org/ostelco/prime/storage/graph/Util.kt | 6 ---- .../main/resources/AcceptanceTestSetup.kts | 3 +- .../main/resources/IntegrationTestSetup.kts | 3 +- .../prime/storage/graph/Neo4jLoadTest.kt | 2 +- .../prime/storage/graph/Neo4jStoreTest.kt | 10 +++--- .../prime/admin/actions/CustomerActions.kt | 33 ------------------- 7 files changed, 7 insertions(+), 61 deletions(-) diff --git a/neo4j-store/src/main/kotlin/org/ostelco/prime/storage/graph/Neo4jStore.kt b/neo4j-store/src/main/kotlin/org/ostelco/prime/storage/graph/Neo4jStore.kt index e42e4229c..ba36f6c8e 100644 --- a/neo4j-store/src/main/kotlin/org/ostelco/prime/storage/graph/Neo4jStore.kt +++ b/neo4j-store/src/main/kotlin/org/ostelco/prime/storage/graph/Neo4jStore.kt @@ -1956,17 +1956,6 @@ object Neo4jStoreSingleton : GraphStore { } } - private fun getInitialSegmentNameForRegion(regionCode: String, transaction: Transaction): String = - segmentStore.get(getPlanSegmentNameFromCountryCode(regionCode), transaction) - .fold( - { - getSegmentNameFromCountryCode(regionCode) - }, - { - it.id - } - ) - // ------------ // Admin Store // ------------ diff --git a/neo4j-store/src/main/kotlin/org/ostelco/prime/storage/graph/Util.kt b/neo4j-store/src/main/kotlin/org/ostelco/prime/storage/graph/Util.kt index 9bc37f50f..ecb007b58 100644 --- a/neo4j-store/src/main/kotlin/org/ostelco/prime/storage/graph/Util.kt +++ b/neo4j-store/src/main/kotlin/org/ostelco/prime/storage/graph/Util.kt @@ -12,12 +12,6 @@ import java.text.DecimalFormatSymbols val adminStore by lazy { getResource() } -// Helper for naming of default segments based on country code. -fun getSegmentNameFromCountryCode(countryCode: String): String = "country-$countryCode".toLowerCase() - -// Helper for naming of default plan segments based on country code. -fun getPlanSegmentNameFromCountryCode(countryCode: String): String = "plan-country-$countryCode".toLowerCase() - private val dfs = DecimalFormatSymbols().apply { groupingSeparator = '_' } diff --git a/neo4j-store/src/main/resources/AcceptanceTestSetup.kts b/neo4j-store/src/main/resources/AcceptanceTestSetup.kts index 9e0772cb8..a77f2fef6 100644 --- a/neo4j-store/src/main/resources/AcceptanceTestSetup.kts +++ b/neo4j-store/src/main/resources/AcceptanceTestSetup.kts @@ -19,7 +19,6 @@ import org.ostelco.prime.storage.graph.Neo4jStoreSingleton.atomicCreateOffer import org.ostelco.prime.storage.graph.Neo4jStoreSingleton.createIndex import org.ostelco.prime.storage.graph.adminStore import org.ostelco.prime.storage.graph.createProduct -import org.ostelco.prime.storage.graph.getSegmentNameFromCountryCode private val logger by getLogger() @@ -60,7 +59,7 @@ job { atomicCreateOffer( offer = Offer(id = "default_offer"), - segments = listOf(Segment(id = getSegmentNameFromCountryCode("NO"))), + segments = listOf(Segment(id = "country-no")), products = listOf( createProduct(sku = "1GB_249NOK"), createProduct(sku = "2GB_299NOK"), diff --git a/neo4j-store/src/main/resources/IntegrationTestSetup.kts b/neo4j-store/src/main/resources/IntegrationTestSetup.kts index 2f2199307..2b1f40b8a 100644 --- a/neo4j-store/src/main/resources/IntegrationTestSetup.kts +++ b/neo4j-store/src/main/resources/IntegrationTestSetup.kts @@ -11,7 +11,6 @@ import org.ostelco.prime.model.Segment import org.ostelco.prime.storage.graph.Neo4jStoreSingleton.atomicCreateOffer import org.ostelco.prime.storage.graph.Neo4jStoreSingleton.createIndex import org.ostelco.prime.storage.graph.createProduct -import org.ostelco.prime.storage.graph.getSegmentNameFromCountryCode private val logger by getLogger() @@ -52,7 +51,7 @@ job { atomicCreateOffer( offer = Offer(id = "default_offer"), - segments = listOf(Segment(id = getSegmentNameFromCountryCode("NO"))), + segments = listOf(Segment(id = "country-no")), products = listOf( createProduct(sku = "1GB_249NOK"), createProduct(sku = "2GB_299NOK"), diff --git a/neo4j-store/src/test/kotlin/org/ostelco/prime/storage/graph/Neo4jLoadTest.kt b/neo4j-store/src/test/kotlin/org/ostelco/prime/storage/graph/Neo4jLoadTest.kt index caf9b8b44..c11747cb5 100644 --- a/neo4j-store/src/test/kotlin/org/ostelco/prime/storage/graph/Neo4jLoadTest.kt +++ b/neo4j-store/src/test/kotlin/org/ostelco/prime/storage/graph/Neo4jLoadTest.kt @@ -53,7 +53,7 @@ class Neo4jLoadTest { properties = mapOf(NO_OF_BYTES.s to "1_000_000_000")) } create { - Segment(id = getSegmentNameFromCountryCode(COUNTRY)) + Segment(id = "country-${COUNTRY.toLowerCase()}") } } } diff --git a/neo4j-store/src/test/kotlin/org/ostelco/prime/storage/graph/Neo4jStoreTest.kt b/neo4j-store/src/test/kotlin/org/ostelco/prime/storage/graph/Neo4jStoreTest.kt index bfa4803fd..307bb1ff4 100644 --- a/neo4j-store/src/test/kotlin/org/ostelco/prime/storage/graph/Neo4jStoreTest.kt +++ b/neo4j-store/src/test/kotlin/org/ostelco/prime/storage/graph/Neo4jStoreTest.kt @@ -117,7 +117,7 @@ class Neo4jStoreTest { ) } create { - Segment(id = getSegmentNameFromCountryCode(REGION)) + Segment(id = "country-${REGION.toLowerCase()}") } } } @@ -256,7 +256,7 @@ class Neo4jStoreTest { val offer = Offer( id = "NEW_OFFER", - segments = listOf(getSegmentNameFromCountryCode(REGION)), + segments = listOf("country-${REGION.toLowerCase()}"), products = listOf(sku)) Neo4jStoreSingleton.createOffer(offer) @@ -942,8 +942,7 @@ class Neo4jStoreTest { create { Region("sg", "Singapore") } }.mapLeft { fail(it.message) } - /* Note: (kmm) For 'sg' the first segment offered is always a plan. */ - Neo4jStoreSingleton.createSegment(org.ostelco.prime.model.Segment(id = getPlanSegmentNameFromCountryCode("sg"))) + Neo4jStoreSingleton.createSegment(org.ostelco.prime.model.Segment(id = "country-sg")) .mapLeft { fail(it.message) } assert(Neo4jStoreSingleton.addCustomer( @@ -991,8 +990,7 @@ class Neo4jStoreTest { @Test fun `test NRIC_FIN JUMIO and ADDRESS_PHONE status`() { - /* Note: (kmm) For 'sg' the first segment offered is always a plan. */ - Neo4jStoreSingleton.createSegment(org.ostelco.prime.model.Segment(id = getPlanSegmentNameFromCountryCode("sg"))) + Neo4jStoreSingleton.createSegment(org.ostelco.prime.model.Segment(id = "country-sg")) job { create { Region("sg", "Singapore") } diff --git a/tools/prime-admin/src/main/kotlin/org/ostelco/tools/prime/admin/actions/CustomerActions.kt b/tools/prime-admin/src/main/kotlin/org/ostelco/tools/prime/admin/actions/CustomerActions.kt index d086fbac4..0e899c092 100644 --- a/tools/prime-admin/src/main/kotlin/org/ostelco/tools/prime/admin/actions/CustomerActions.kt +++ b/tools/prime-admin/src/main/kotlin/org/ostelco/tools/prime/admin/actions/CustomerActions.kt @@ -11,12 +11,10 @@ import org.ostelco.prime.dsl.writeTransaction import org.ostelco.prime.model.Bundle import org.ostelco.prime.model.Customer import org.ostelco.prime.model.Identity -import org.ostelco.prime.model.Segment import org.ostelco.prime.storage.NotFoundError import org.ostelco.prime.storage.StoreError import org.ostelco.prime.storage.ValidationError import org.ostelco.prime.storage.graph.adminStore -import org.ostelco.prime.storage.graph.getSegmentNameFromCountryCode import java.util.* // @@ -37,29 +35,6 @@ fun createCustomer(email: String, nickname: String): Either = analyticsId = UUID.randomUUID().toString(), referralId = UUID.randomUUID().toString())) -fun assignCustomerToRegionSegment(email: String, regionCode: String): Either = IO { - Either.monad().binding { - - val customerId = adminStore.getCustomer( - identity = Identity( - id = email, - type = "EMAIL", - provider = "email" - ) - ) - .bind() - .id - - adminStore.updateSegment( - segment = Segment( - id = getSegmentNameFromCountryCode(regionCode), - subscribers = listOf(customerId) - ) - ) - .bind() - }.fix() -}.unsafeRunSync() - fun approveRegionForCustomer(email: String, regionCode: String): Either = IO { Either.monad().binding { @@ -69,14 +44,6 @@ fun approveRegionForCustomer(email: String, regionCode: String): Either Date: Mon, 14 Oct 2019 16:36:55 +0200 Subject: [PATCH 083/173] Log message for failed unit test --- .../org/ostelco/prime/storage/graph/Neo4jStoreTest.kt | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/neo4j-store/src/test/kotlin/org/ostelco/prime/storage/graph/Neo4jStoreTest.kt b/neo4j-store/src/test/kotlin/org/ostelco/prime/storage/graph/Neo4jStoreTest.kt index 307bb1ff4..72ac97599 100644 --- a/neo4j-store/src/test/kotlin/org/ostelco/prime/storage/graph/Neo4jStoreTest.kt +++ b/neo4j-store/src/test/kotlin/org/ostelco/prime/storage/graph/Neo4jStoreTest.kt @@ -1011,6 +1011,9 @@ class Neo4jStoreTest { customer = CUSTOMER, regionCode = "sg", kycType = NRIC_FIN) + .mapLeft { + fail(it.message) + } Neo4jStoreSingleton.getRegionDetails( identity = IDENTITY, @@ -1025,6 +1028,9 @@ class Neo4jStoreTest { customer = CUSTOMER, regionCode = "sg", kycType = JUMIO) + .mapLeft { + fail(it.message) + } Neo4jStoreSingleton.getRegionDetails( identity = IDENTITY, @@ -1039,6 +1045,9 @@ class Neo4jStoreTest { customer = CUSTOMER, regionCode = "sg", kycType = ADDRESS) + .mapLeft { + fail(it.message) + } Neo4jStoreSingleton.getRegionDetails( identity = IDENTITY, From 44ec17c62440c03f2bdb7faaa7ecf19bcd2e56ae Mon Sep 17 00:00:00 2001 From: Vihang Patil Date: Mon, 14 Oct 2019 19:42:32 +0200 Subject: [PATCH 084/173] Updated prime version. --- prime/build.gradle.kts | 2 +- prime/script/start.sh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/prime/build.gradle.kts b/prime/build.gradle.kts index 170f2725d..dd4fa55c8 100644 --- a/prime/build.gradle.kts +++ b/prime/build.gradle.kts @@ -10,7 +10,7 @@ plugins { } // Update version in [script/start.sh] too. -version = "1.64.0" +version = "1.65.0" dependencies { // interface module between prime and prime-modules diff --git a/prime/script/start.sh b/prime/script/start.sh index 7310187fa..c6c0f145c 100755 --- a/prime/script/start.sh +++ b/prime/script/start.sh @@ -5,5 +5,5 @@ exec java \ -Dfile.encoding=UTF-8 \ --add-opens java.base/java.lang=ALL-UNNAMED \ --add-opens java.base/java.io=ALL-UNNAMED \ - -agentpath:/opt/cprof/profiler_java_agent.so=-cprof_service=prime,-cprof_service_version=1.64.0,-logtostderr,-minloglevel=2,-cprof_enable_heap_sampling \ + -agentpath:/opt/cprof/profiler_java_agent.so=-cprof_service=prime,-cprof_service_version=1.65.0,-logtostderr,-minloglevel=2,-cprof_enable_heap_sampling \ -jar /prime.jar server /config/config.yaml From 9b397e14e31dd0e41715f8db116519ecc88cb370 Mon Sep 17 00:00:00 2001 From: Prasanth Ullattil Date: Tue, 15 Oct 2019 10:20:19 +0200 Subject: [PATCH 085/173] Whitespace fixes to use the same style in the file --- model/src/main/kotlin/org/ostelco/prime/model/Entities.kt | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/model/src/main/kotlin/org/ostelco/prime/model/Entities.kt b/model/src/main/kotlin/org/ostelco/prime/model/Entities.kt index 901627d8e..296754f50 100644 --- a/model/src/main/kotlin/org/ostelco/prime/model/Entities.kt +++ b/model/src/main/kotlin/org/ostelco/prime/model/Entities.kt @@ -27,8 +27,7 @@ data class Region( data class Offer( val id: String, val segments: Collection = emptyList(), - val products: Collection = emptyList() -) + val products: Collection = emptyList()) data class Segment( val id: String, From 12ae2fb0eaba0cd84a0003821da5399fcaab5a52 Mon Sep 17 00:00:00 2001 From: Prasanth Ullattil Date: Tue, 15 Oct 2019 10:21:51 +0200 Subject: [PATCH 086/173] Whitespace fixes --- neo4j-store/src/main/kotlin/org/ostelco/prime/dsl/Syntax.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/neo4j-store/src/main/kotlin/org/ostelco/prime/dsl/Syntax.kt b/neo4j-store/src/main/kotlin/org/ostelco/prime/dsl/Syntax.kt index 4ee3a6a28..9e5b4267c 100644 --- a/neo4j-store/src/main/kotlin/org/ostelco/prime/dsl/Syntax.kt +++ b/neo4j-store/src/main/kotlin/org/ostelco/prime/dsl/Syntax.kt @@ -299,4 +299,4 @@ infix fun PurchaseRecord.Companion.forPurchasesBy(customer: CustomerContext) = // // Segment // -infix fun Segment.Companion.withId(id: String): SegmentContext = SegmentContext(id) \ No newline at end of file +infix fun Segment.Companion.withId(id: String): SegmentContext = SegmentContext(id) From ae0b88bde6c5d55add4fc2889cd1f063830049f4 Mon Sep 17 00:00:00 2001 From: Prasanth Ullattil Date: Tue, 15 Oct 2019 11:18:36 +0200 Subject: [PATCH 087/173] Suggestion from Intellij --- .../main/kotlin/org/ostelco/prime/storage/graph/Neo4jStore.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/neo4j-store/src/main/kotlin/org/ostelco/prime/storage/graph/Neo4jStore.kt b/neo4j-store/src/main/kotlin/org/ostelco/prime/storage/graph/Neo4jStore.kt index ba36f6c8e..ee454338f 100644 --- a/neo4j-store/src/main/kotlin/org/ostelco/prime/storage/graph/Neo4jStore.kt +++ b/neo4j-store/src/main/kotlin/org/ostelco/prime/storage/graph/Neo4jStore.kt @@ -292,7 +292,7 @@ object Neo4jStoreSingleton : GraphStore { private val onNewCustomerAction: OnNewCustomerAction = config.onNewCustomerAction.getKtsService() private val allowedRegionsService: AllowedRegionsService = config.allowedRegionsService.getKtsService() - val onRegionApprovedAction: OnRegionApprovedAction = config.onRegionApprovedAction.getKtsService() + private val onRegionApprovedAction: OnRegionApprovedAction = config.onRegionApprovedAction.getKtsService() private val hssNameLookup: HssNameLookupService = config.hssNameLookupService.getKtsService() // ------------- From 3ddf663d489f91f6beb528abe0ac9213203096a2 Mon Sep 17 00:00:00 2001 From: Vihang Patil Date: Tue, 15 Oct 2019 11:42:39 +0200 Subject: [PATCH 088/173] Updated prime-admin with function to add the customer to a segment. --- .../ostelco/prime/storage/graph/KtScripts.kt | 8 +-- .../integration-test/resources/config.yaml | 20 ++++-- tools/prime-admin/config/config.yaml | 15 +++-- .../org/ostelco/tools/prime/admin/Main.kt | 5 ++ .../prime/admin/actions/CustomerActions.kt | 66 ++++++++----------- 5 files changed, 61 insertions(+), 53 deletions(-) diff --git a/neo4j-store/src/main/kotlin/org/ostelco/prime/storage/graph/KtScripts.kt b/neo4j-store/src/main/kotlin/org/ostelco/prime/storage/graph/KtScripts.kt index 07219b840..bacfcd80d 100644 --- a/neo4j-store/src/main/kotlin/org/ostelco/prime/storage/graph/KtScripts.kt +++ b/neo4j-store/src/main/kotlin/org/ostelco/prime/storage/graph/KtScripts.kt @@ -20,10 +20,6 @@ interface AllowedRegionsService { ): Either> } -interface HssNameLookupService { - fun getHssName(regionCode: String): String -} - interface OnRegionApprovedAction { fun apply( customer: Customer, @@ -31,3 +27,7 @@ interface OnRegionApprovedAction { transaction: PrimeTransaction ): Either } + +interface HssNameLookupService { + fun getHssName(regionCode: String): String +} diff --git a/prime/src/integration-test/resources/config.yaml b/prime/src/integration-test/resources/config.yaml index 54bed830d..b146a9b3b 100644 --- a/prime/src/integration-test/resources/config.yaml +++ b/prime/src/integration-test/resources/config.yaml @@ -8,16 +8,26 @@ modules: config: host: 0.0.0.0 protocol: bolt - hssNameLookupService: - serviceInterface: org.ostelco.prime.storage.graph.HssNameLookupService - textReader: - type: classpathResource - filename: /HssNameLookupService.kts onNewCustomerAction: serviceInterface: org.ostelco.prime.storage.graph.OnNewCustomerAction textReader: type: classpathResource filename: /OnNewCustomerAction.kts + allowedRegionsService: + serviceInterface: org.ostelco.prime.storage.graph.AllowedRegionsService + textReader: + type: classpathResource + filename: /AllowedRegionsService.kts + onRegionApprovedAction: + serviceInterface: org.ostelco.prime.storage.graph.OnRegionApprovedAction + textReader: + type: classpathResource + filename: /OnRegionApprovedAction.kts + hssNameLookupService: + serviceInterface: org.ostelco.prime.storage.graph.HssNameLookupService + textReader: + type: classpathResource + filename: /HssNameLookupService.kts - type: analytics config: projectId: ${GCP_PROJECT_ID} diff --git a/tools/prime-admin/config/config.yaml b/tools/prime-admin/config/config.yaml index df9082dc7..ed83e752c 100644 --- a/tools/prime-admin/config/config.yaml +++ b/tools/prime-admin/config/config.yaml @@ -7,11 +7,6 @@ modules: config: host: neo4j protocol: bolt - hssNameLookupService: - serviceInterface: org.ostelco.prime.storage.graph.HssNameLookupService - textReader: - type: classpathResource - filename: /HssNameLookupService.kts onNewCustomerAction: serviceInterface: org.ostelco.prime.storage.graph.OnNewCustomerAction textReader: @@ -22,6 +17,16 @@ modules: textReader: type: classpathResource filename: /AllowedRegionsService.kts + onRegionApprovedAction: + serviceInterface: org.ostelco.prime.storage.graph.OnRegionApprovedAction + textReader: + type: classpathResource + filename: /OnRegionApprovedAction.kts + hssNameLookupService: + serviceInterface: org.ostelco.prime.storage.graph.HssNameLookupService + textReader: + type: classpathResource + filename: /HssNameLookupService.kts # - type: sim-manager # config: # hlrs: [] diff --git a/tools/prime-admin/src/main/kotlin/org/ostelco/tools/prime/admin/Main.kt b/tools/prime-admin/src/main/kotlin/org/ostelco/tools/prime/admin/Main.kt index 75f055b19..fde5d1e94 100644 --- a/tools/prime-admin/src/main/kotlin/org/ostelco/tools/prime/admin/Main.kt +++ b/tools/prime-admin/src/main/kotlin/org/ostelco/tools/prime/admin/Main.kt @@ -1,6 +1,7 @@ package org.ostelco.tools.prime.admin import org.ostelco.prime.PrimeApplication +import org.ostelco.tools.prime.admin.actions.addCustomerToSegment import org.ostelco.tools.prime.admin.actions.approveRegionForCustomer import org.ostelco.tools.prime.admin.actions.createCustomer import org.ostelco.tools.prime.admin.actions.createSubscription @@ -38,6 +39,7 @@ fun doActions() { val email = "" val nickname = "" val regionCode = "" + val segmentId = "" val iccId = "" val alias = "" val msisdn = "" @@ -53,6 +55,9 @@ fun doActions() { // link to region approveRegionForCustomer(email = email, regionCode = regionCode).printLeft() + // link to segment + addCustomerToSegment(email = email, segmentId = segmentId).printLeft() + // add SimProfile createSubscription( email = email, diff --git a/tools/prime-admin/src/main/kotlin/org/ostelco/tools/prime/admin/actions/CustomerActions.kt b/tools/prime-admin/src/main/kotlin/org/ostelco/tools/prime/admin/actions/CustomerActions.kt index 0e899c092..343e3d658 100644 --- a/tools/prime-admin/src/main/kotlin/org/ostelco/tools/prime/admin/actions/CustomerActions.kt +++ b/tools/prime-admin/src/main/kotlin/org/ostelco/tools/prime/admin/actions/CustomerActions.kt @@ -7,6 +7,7 @@ import arrow.core.left import arrow.core.right import arrow.effects.IO import arrow.instances.either.monad.monad +import org.ostelco.prime.dsl.withId import org.ostelco.prime.dsl.writeTransaction import org.ostelco.prime.model.Bundle import org.ostelco.prime.model.Customer @@ -23,11 +24,7 @@ import java.util.* fun createCustomer(email: String, nickname: String): Either = adminStore .addCustomer( - identity = Identity( - id = email, - type = "EMAIL", - provider = "email" - ), + identity = emailAsIdentity(email = email), customer = Customer( id = UUID.randomUUID().toString(), nickname = nickname, @@ -38,9 +35,8 @@ fun createCustomer(email: String, nickname: String): Either = fun approveRegionForCustomer(email: String, regionCode: String): Either = IO { Either.monad().binding { - val customerId = adminStore.getCustomer( - identity = Identity(id = email, type = "EMAIL", provider = "email") - ) + val customerId = adminStore + .getCustomer(identity = emailAsIdentity(email = email)) .bind() .id @@ -52,14 +48,20 @@ fun approveRegionForCustomer(email: String, regionCode: String): Either = writeTransaction { + IO { + Either.monad().binding { + val customerId = adminStore + .getCustomer(identity = emailAsIdentity(email = email)) + .bind() + .id + fact { (Customer withId customerId) belongsToSegment (org.ostelco.prime.storage.graph.model.Segment withId segmentId) }.bind() + }.fix() + }.unsafeRunSync() +} + fun deleteCustomer(email: String) = adminStore - .removeCustomer( - identity = Identity( - id = email, - type = "EMAIL", - provider = "email" - ) - ) + .removeCustomer(identity = emailAsIdentity(email = email)) fun createSubscription( email: String, @@ -68,11 +70,7 @@ fun createSubscription( alias: String = "", msisdn: String) = adminStore .addSubscription( - identity = Identity( - id = email, - type = "EMAIL", - provider = "email" - ), + identity = emailAsIdentity(email = email), regionCode = regionCode, iccId = iccId, alias = alias, @@ -84,13 +82,7 @@ fun createSubscription( // fun setBalance(email: String, balance: Long) = adminStore - .getBundles( - identity = Identity( - id = email, - type = "EMAIL", - provider = "email" - ) - ) + .getBundles(identity = emailAsIdentity(email = email)) .flatMap { bundles -> when (bundles.size) { 0 -> NotFoundError( @@ -121,21 +113,17 @@ fun setBalance(email: String, balance: Long) = adminStore // fun getAllRegionDetails(email: String) = adminStore - .getAllRegionDetails( - identity = Identity( - id = email, - type = "EMAIL", - provider = "email" - ) - ) + .getAllRegionDetails(identity = emailAsIdentity(email = email)) fun getRegionDetails(email: String, regionCode: String) = adminStore .getRegionDetails( - identity = Identity( - id = email, - type = "EMAIL", - provider = "email" - ), + identity = emailAsIdentity(email = email), regionCode = regionCode ) +// Common +private fun emailAsIdentity(email: String) = Identity( + id = email, + type = "EMAIL", + provider = "email" +) \ No newline at end of file From 6e7a3f3ef1284949c948c4ae776e3a065dea0075 Mon Sep 17 00:00:00 2001 From: Prasanth Ullattil Date: Tue, 15 Oct 2019 11:19:42 +0200 Subject: [PATCH 089/173] Whitespace fix --- .../main/kotlin/org/ostelco/prime/storage/graph/model/Model.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/neo4j-store/src/main/kotlin/org/ostelco/prime/storage/graph/model/Model.kt b/neo4j-store/src/main/kotlin/org/ostelco/prime/storage/graph/model/Model.kt index 2a537d718..397befbcd 100644 --- a/neo4j-store/src/main/kotlin/org/ostelco/prime/storage/graph/model/Model.kt +++ b/neo4j-store/src/main/kotlin/org/ostelco/prime/storage/graph/model/Model.kt @@ -37,4 +37,4 @@ data class Segment(override val id: String) : HasId { companion object } -data class Offer(override val id: String) : HasId \ No newline at end of file +data class Offer(override val id: String) : HasId From 8f4ebe7ea1e97f87935bb834bd3a667f3ba3bb16 Mon Sep 17 00:00:00 2001 From: Vihang Patil Date: Tue, 15 Oct 2019 15:06:13 +0200 Subject: [PATCH 090/173] Skip sending email with eSIM Activation QR code for iPhone and Test phones. --- .../main/kotlin/org/ostelco/prime/storage/graph/Neo4jStore.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/neo4j-store/src/main/kotlin/org/ostelco/prime/storage/graph/Neo4jStore.kt b/neo4j-store/src/main/kotlin/org/ostelco/prime/storage/graph/Neo4jStore.kt index ee454338f..fdd62a707 100644 --- a/neo4j-store/src/main/kotlin/org/ostelco/prime/storage/graph/Neo4jStore.kt +++ b/neo4j-store/src/main/kotlin/org/ostelco/prime/storage/graph/Neo4jStore.kt @@ -651,7 +651,7 @@ object Neo4jStoreSingleton : GraphStore { fact { (Customer withId customerId) subscribesTo (Subscription withMsisdn msisdn) }.bind() fact { (Subscription withMsisdn msisdn) isUnder (SimProfile withId simProfile.id) }.bind() } - if (profileType != "android") { + if (!setOf("android", "iphone", "test").contains(profileType)) { emailNotifier.sendESimQrCodeEmail( email = customer.contactEmail, name = customer.nickname, From ff911356b73a0eab92e51d1bcbd5c71f9653050e Mon Sep 17 00:00:00 2001 From: Vihang Patil Date: Tue, 15 Oct 2019 15:40:19 +0200 Subject: [PATCH 091/173] Version updates. --- analytics-module/build.gradle.kts | 2 +- build.gradle.kts | 2 +- .../org/ostelco/prime/gradle/Version.kt | 34 +++++++++---------- diameter-ha/build.gradle.kts | 2 +- prime-customer-api/build.gradle.kts | 4 +-- 5 files changed, 22 insertions(+), 22 deletions(-) diff --git a/analytics-module/build.gradle.kts b/analytics-module/build.gradle.kts index 2f2faedb7..88e4e612c 100644 --- a/analytics-module/build.gradle.kts +++ b/analytics-module/build.gradle.kts @@ -9,5 +9,5 @@ dependencies { implementation(project(":prime-modules")) implementation("com.google.cloud:google-cloud-pubsub:${Version.googleCloudPubSub}") - implementation("com.google.code.gson:gson:2.8.5") + implementation("com.google.code.gson:gson:${Version.gson}") } diff --git a/build.gradle.kts b/build.gradle.kts index 5ce6150d4..8fb614791 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -8,7 +8,7 @@ plugins { base java id("project-report") - id("com.github.ben-manes.versions") version "0.25.0" + id("com.github.ben-manes.versions") version "0.26.0" jacoco kotlin("jvm") version "1.3.50" apply false id("com.google.protobuf") version "0.8.10" apply false diff --git a/buildSrc/src/main/kotlin/org/ostelco/prime/gradle/Version.kt b/buildSrc/src/main/kotlin/org/ostelco/prime/gradle/Version.kt index 71bd9916f..5edfdb53a 100644 --- a/buildSrc/src/main/kotlin/org/ostelco/prime/gradle/Version.kt +++ b/buildSrc/src/main/kotlin/org/ostelco/prime/gradle/Version.kt @@ -4,27 +4,27 @@ object Version { const val assertJ = "3.13.2" const val arrow = "0.8.2" - const val arrowTypeClasses = "0.9.0" const val beam = "2.15.0" const val byteBuddy = "1.10.1" const val csv = "1.7" const val cxf = "3.3.3" const val dockerComposeJunitRule = "1.3.0" - const val dropwizard = "1.3.14" + const val dropwizard = "1.3.15" const val metrics = "4.1.0" const val firebase = "6.10.0" - const val googleCloud = "1.91.0" - const val googleCloudDataStore = "1.92.0" - const val googleCloudLogging = "0.110.0-alpha" - const val googleCloudPubSub = "1.92.0" - const val googleCloudStorage = "1.92.0" + const val googleCloud = "1.91.2" + const val googleCloudDataStore = "1.97.0" + const val googleCloudLogging = "0.115.0-alpha" + const val googleCloudPubSub = "1.97.0" + const val googleCloudStorage = "1.97.0" - const val grpc = "1.23.0" + const val gson = "2.8.6" + const val grpc = "1.24.0" const val guava = "28.1-jre" - const val jackson = "2.9.9" - const val jacksonDatabind = "2.9.9.3" + const val jackson = "2.10.0" + const val jacksonDatabind = "2.10.0" const val javaxActivation = "1.1.1" const val javaxActivationApi = "1.2.0" const val javaxAnnotation = "1.3.2" @@ -35,11 +35,11 @@ object Version { const val jjwt = "0.10.7" const val junit5 = "5.5.2" const val kotlin = "1.3.50" - const val kotlinXCoroutines = "1.3.1" - const val mockito = "3.0.0" + const val kotlinXCoroutines = "1.3.2" + const val mockito = "3.1.0" const val mockitoKotlin = "2.2.0" const val neo4jDriver = "1.7.5" - const val neo4j = "3.5.9" + const val neo4j = "3.5.11" const val opencensus = "0.24.0" const val postgresql = "42.2.8" // See comment in ./sim-administration/simmanager/build.gradle const val prometheusDropwizard = "2.2.0" @@ -47,10 +47,10 @@ object Version { const val slf4j = "1.7.28" // IMPORTANT: When Stripe SDK library version is updated, check if the Stripe API version has changed. // If so, then update API version in Stripe Web Console for callback Webhooks. - const val stripe = "12.0.0" - const val swagger = "2.0.9" - const val swaggerCodegen = "2.4.8" - const val testcontainers = "1.12.1" + const val stripe = "13.1.0" + const val swagger = "2.0.10" + const val swaggerCodegen = "2.4.9" + const val testcontainers = "1.12.2" const val tink = "1.2.2" const val zxing = "3.4.0" } \ No newline at end of file diff --git a/diameter-ha/build.gradle.kts b/diameter-ha/build.gradle.kts index 5df14a317..105d4934d 100644 --- a/diameter-ha/build.gradle.kts +++ b/diameter-ha/build.gradle.kts @@ -18,7 +18,7 @@ dependencies { } implementation("org.slf4j:log4j-over-slf4j:${Version.slf4j}") - compile("io.lettuce:lettuce-core:5.1.8.RELEASE") + compile("io.lettuce:lettuce-core:5.2.0.RELEASE") testImplementation(kotlin("test-junit")) testRuntimeOnly("org.hamcrest:hamcrest-all:1.3") diff --git a/prime-customer-api/build.gradle.kts b/prime-customer-api/build.gradle.kts index 8ad67239c..e5751b0b7 100644 --- a/prime-customer-api/build.gradle.kts +++ b/prime-customer-api/build.gradle.kts @@ -30,8 +30,8 @@ dependencies { implementation("javax.annotation:javax.annotation-api:${Version.javaxAnnotation}") // taken from build/swagger-code-java-client/build.gradle - implementation("io.swagger:swagger-annotations:1.5.23") - implementation("com.google.code.gson:gson:2.8.5") + implementation("io.swagger:swagger-annotations:1.5.24") + implementation("com.google.code.gson:gson:${Version.gson}") implementation("com.squareup.okhttp:okhttp:2.7.5") implementation("com.squareup.okhttp:logging-interceptor:2.7.5") implementation("io.gsonfire:gson-fire:1.8.3") From fd607a7d5f3e17b309a538a0de851fc549ef9921 Mon Sep 17 00:00:00 2001 From: Vihang Patil Date: Tue, 15 Oct 2019 15:48:15 +0200 Subject: [PATCH 092/173] Updated prime-version --- prime/build.gradle.kts | 2 +- prime/script/start.sh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/prime/build.gradle.kts b/prime/build.gradle.kts index dd4fa55c8..076665d9a 100644 --- a/prime/build.gradle.kts +++ b/prime/build.gradle.kts @@ -10,7 +10,7 @@ plugins { } // Update version in [script/start.sh] too. -version = "1.65.0" +version = "1.65.1" dependencies { // interface module between prime and prime-modules diff --git a/prime/script/start.sh b/prime/script/start.sh index c6c0f145c..2a0ba1160 100755 --- a/prime/script/start.sh +++ b/prime/script/start.sh @@ -5,5 +5,5 @@ exec java \ -Dfile.encoding=UTF-8 \ --add-opens java.base/java.lang=ALL-UNNAMED \ --add-opens java.base/java.io=ALL-UNNAMED \ - -agentpath:/opt/cprof/profiler_java_agent.so=-cprof_service=prime,-cprof_service_version=1.65.0,-logtostderr,-minloglevel=2,-cprof_enable_heap_sampling \ + -agentpath:/opt/cprof/profiler_java_agent.so=-cprof_service=prime,-cprof_service_version=1.65.1,-logtostderr,-minloglevel=2,-cprof_enable_heap_sampling \ -jar /prime.jar server /config/config.yaml From 1947b42592cae01ef30ff03452d05a912346e475 Mon Sep 17 00:00:00 2001 From: Vihang Patil Date: Tue, 15 Oct 2019 15:40:49 +0200 Subject: [PATCH 093/173] Code changes to for new jackson --- .../src/main/kotlin/org/ostelco/prime/store/datastore/Schema.kt | 2 +- .../src/main/kotlin/org/ostelco/ext/myinfo/JsonUtils.kt | 2 +- .../test/kotlin/org/ostelco/prime/storage/graph/SchemaTest.kt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/data-store/src/main/kotlin/org/ostelco/prime/store/datastore/Schema.kt b/data-store/src/main/kotlin/org/ostelco/prime/store/datastore/Schema.kt index d10d0b0bf..7167025f1 100644 --- a/data-store/src/main/kotlin/org/ostelco/prime/store/datastore/Schema.kt +++ b/data-store/src/main/kotlin/org/ostelco/prime/store/datastore/Schema.kt @@ -129,7 +129,7 @@ class EntityStore( // convert object to map of (field name, field value) // TODO: Fails to serialize datastore 'Value<*>' types such as 'StringValue'. - val map: MutableMap = objectMapper.convertValue(entity, object : TypeReference>() {}) + val map: Map = objectMapper.convertValue(entity, object : TypeReference>() {}) val keyFactory = parents.fold( initial = datastore.newKeyFactory().setKind(entityClass.qualifiedName) diff --git a/ext-myinfo-emulator/src/main/kotlin/org/ostelco/ext/myinfo/JsonUtils.kt b/ext-myinfo-emulator/src/main/kotlin/org/ostelco/ext/myinfo/JsonUtils.kt index cc9c3f4ab..71392c3a9 100644 --- a/ext-myinfo-emulator/src/main/kotlin/org/ostelco/ext/myinfo/JsonUtils.kt +++ b/ext-myinfo-emulator/src/main/kotlin/org/ostelco/ext/myinfo/JsonUtils.kt @@ -5,5 +5,5 @@ import org.ostelco.prime.jsonmapper.objectMapper object JsonUtils { fun compactJson(json: String): String = objectMapper.writeValueAsString( - objectMapper.readValue>(json, object : TypeReference>() {})) + objectMapper.readValue>(json, object : TypeReference>() {})) } \ No newline at end of file diff --git a/neo4j-store/src/test/kotlin/org/ostelco/prime/storage/graph/SchemaTest.kt b/neo4j-store/src/test/kotlin/org/ostelco/prime/storage/graph/SchemaTest.kt index bc0fc24cf..1e3530ba0 100644 --- a/neo4j-store/src/test/kotlin/org/ostelco/prime/storage/graph/SchemaTest.kt +++ b/neo4j-store/src/test/kotlin/org/ostelco/prime/storage/graph/SchemaTest.kt @@ -191,7 +191,7 @@ class SchemaTest { @Test fun `json to map`() { - val map = objectMapper.readValue>("""{"label":"3GB for 300 NOK"}""", object : TypeReference>() {}) + val map = objectMapper.readValue>("""{"label":"3GB for 300 NOK"}""", object : TypeReference>() {}) assertEquals("3GB for 300 NOK", map["label"]) } From da187e752e87a99641031c3ee1d62ec31e60c424 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B8rn=20Remseth?= Date: Wed, 16 Oct 2019 10:22:45 +0200 Subject: [PATCH 094/173] Don't ignore go.sum --- .gitignore | 1 - 1 file changed, 1 deletion(-) diff --git a/.gitignore b/.gitignore index 9edbae017..1f09c6d5a 100644 --- a/.gitignore +++ b/.gitignore @@ -45,4 +45,3 @@ metrics_descriptor.pb # Python virtual env venv prime-service-account.json -go.sum From 372a2a50c86c98a4d7b04f88c29449b120f7b6e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B8rn=20Remseth?= Date: Wed, 16 Oct 2019 10:23:01 +0200 Subject: [PATCH 095/173] Add go.sum --- go.sum | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 go.sum diff --git a/go.sum b/go.sum new file mode 100644 index 000000000..802d8ce53 --- /dev/null +++ b/go.sum @@ -0,0 +1,6 @@ +github.com/google/go-cmp v0.3.1 h1:Xye71clBPdm5HgqGwUkwhbynsUJZhDbS20FvLhQ2izg= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo= +gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= From 9f61aab7990b2f780dbd48cd776e15b78026e5fc Mon Sep 17 00:00:00 2001 From: "Kjell M. Myksvoll" Date: Wed, 16 Oct 2019 13:02:51 +0200 Subject: [PATCH 096/173] Adds delay loop to some direct calls to Stripe used in acceptance test In some cases acceptance test that checks for correct updates in Stripe, as a result of requests to Prime, might fail because the check is done "too early". This update tries to work around this by performing the check in Stripe multiple times with delay in between before it gives up. --- .../org/ostelco/at/common/StripePayment.kt | 26 ++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/acceptance-tests/src/main/kotlin/org/ostelco/at/common/StripePayment.kt b/acceptance-tests/src/main/kotlin/org/ostelco/at/common/StripePayment.kt index 1ff45beb9..5d6a9bd3f 100644 --- a/acceptance-tests/src/main/kotlin/org/ostelco/at/common/StripePayment.kt +++ b/acceptance-tests/src/main/kotlin/org/ostelco/at/common/StripePayment.kt @@ -75,6 +75,9 @@ object StripePayment { return token.card.id } + val MAX_TRIES = 3 + val WAIT_DELAY = 300L + /** * Obtains 'default source' directly from Stripe. Use in tests to * verify that the correspondng 'setDefaultSource' API works as @@ -85,8 +88,17 @@ object StripePayment { // https://stripe.com/docs/api/java#create_source Stripe.apiKey = System.getenv("STRIPE_API_KEY") - val customer = Customer.retrieve(stripeCustomerId) - return customer.defaultSource + var error = Exception() + + (0..MAX_TRIES).forEach { + try { + return Customer.retrieve(stripeCustomerId).defaultSource + } catch (e: Exception) { + error = e + } + } + + throw(error) } /** @@ -96,7 +108,15 @@ object StripePayment { // https://stripe.com/docs/api/java#create_card_token Stripe.apiKey = System.getenv("STRIPE_API_KEY") - val customers = Customer.list(emptyMap()).data + var customers: List = emptyList() + + (0..MAX_TRIES).forEach { + customers = Customer.list(emptyMap()).data + if (!customers.isEmpty()) + return@forEach + Thread.sleep(WAIT_DELAY) + } + return customers.first { it.id == customerId }.id } From cab949913a5e772a30ade061e1c619b21c51792d Mon Sep 17 00:00:00 2001 From: Vihang Patil Date: Wed, 16 Oct 2019 14:07:00 +0200 Subject: [PATCH 097/173] Changes in tool/prime-admin --- .../org/ostelco/tools/prime/admin/Main.kt | 19 +++++++++++-------- .../prime/admin/actions/SystemActions.kt | 12 +++++++----- 2 files changed, 18 insertions(+), 13 deletions(-) diff --git a/tools/prime-admin/src/main/kotlin/org/ostelco/tools/prime/admin/Main.kt b/tools/prime-admin/src/main/kotlin/org/ostelco/tools/prime/admin/Main.kt index fde5d1e94..a1730b088 100644 --- a/tools/prime-admin/src/main/kotlin/org/ostelco/tools/prime/admin/Main.kt +++ b/tools/prime-admin/src/main/kotlin/org/ostelco/tools/prime/admin/Main.kt @@ -20,7 +20,7 @@ import kotlin.math.pow fun main() { PrimeApplication().run("server", "config/config.yaml") try { - doActions() + setupCustomer() println("Done") } finally { DwEnvModule.env.applicationContext.server.stop() @@ -29,12 +29,7 @@ fun main() { } } -fun doActions() { - -// check() -// sync() -// setup() -// index() +fun setupCustomer() { val email = "" val nickname = "" @@ -73,7 +68,6 @@ fun doActions() { // Get all region details getAllRegionDetails(email = email).print() - } fun batchProvision() { @@ -118,4 +112,13 @@ fun batchProvision() { getAllRegionDetails(email = email).print() } +fun doActions() { + +// check() +// sync() +// setup() +// index() + +} + data class SimProfileData(val iccId: String, val msisdn: String) \ No newline at end of file diff --git a/tools/prime-admin/src/main/kotlin/org/ostelco/tools/prime/admin/actions/SystemActions.kt b/tools/prime-admin/src/main/kotlin/org/ostelco/tools/prime/admin/actions/SystemActions.kt index 3062d97a7..90c7a1c36 100644 --- a/tools/prime-admin/src/main/kotlin/org/ostelco/tools/prime/admin/actions/SystemActions.kt +++ b/tools/prime-admin/src/main/kotlin/org/ostelco/tools/prime/admin/actions/SystemActions.kt @@ -4,18 +4,20 @@ import org.ostelco.prime.kts.engine.reader.ClasspathResourceTextReader import org.ostelco.prime.kts.engine.script.RunnableKotlinScript +private val scriptBaseDir = "" + fun setup() { - RunnableKotlinScript(ClasspathResourceTextReader("/Setup.kts").readText()).eval() + RunnableKotlinScript(ClasspathResourceTextReader("$scriptBaseDir/Setup.kts").readText()).eval() } fun sync() { - RunnableKotlinScript(ClasspathResourceTextReader("/Sync.kts").readText()).eval() + RunnableKotlinScript(ClasspathResourceTextReader("$scriptBaseDir/Sync.kts").readText()).eval() } fun check() { - RunnableKotlinScript(ClasspathResourceTextReader("/Check.kts").readText()).eval() + RunnableKotlinScript(ClasspathResourceTextReader("$scriptBaseDir/Check.kts").readText()).eval() } fun index() { - RunnableKotlinScript(ClasspathResourceTextReader("/Index.kts").readText()).eval() -} \ No newline at end of file + RunnableKotlinScript(ClasspathResourceTextReader("$scriptBaseDir/Index.kts").readText()).eval() +} From ac06457dafddb7204a44796cbbb3bea06240f3e4 Mon Sep 17 00:00:00 2001 From: mpeterss Date: Wed, 16 Oct 2019 14:34:27 +0200 Subject: [PATCH 098/173] Force sync on CCR-U CCA-U after CCR-I with zero RSU If there was no request for units in the CCR-I it is only used for authentication. In this case the proxy adapter need to sync on the first CCR-U as we do not want to hand out interim buckets before we have a balance. --- .../main/kotlin/org/ostelco/at/pgw/OcsTest.kt | 171 ++++++++++++++++++ .../datasource/protobuf/ProtobufDataSource.kt | 6 + 2 files changed, 177 insertions(+) diff --git a/acceptance-tests/src/main/kotlin/org/ostelco/at/pgw/OcsTest.kt b/acceptance-tests/src/main/kotlin/org/ostelco/at/pgw/OcsTest.kt index 90aa91aff..7ce1ec9b2 100644 --- a/acceptance-tests/src/main/kotlin/org/ostelco/at/pgw/OcsTest.kt +++ b/acceptance-tests/src/main/kotlin/org/ostelco/at/pgw/OcsTest.kt @@ -636,6 +636,177 @@ class OcsTest { } + /** + * Test with: + * CCR-I that has no MSCC or Requested-Service-Units. + * CCR-U with MSCC and Requested-Service-Units. + * The user should not have any balance, so we should se DIAMETER_CREDIT_LIMIT_REACHED + */ + @Test + fun creditControlRequestInitNoRsuUpdateWithRsuNoBalance() { + + val email = "ocs-${randomInt()}@test.com" + createCustomer(name = "Test OCS User", email = email) + + val msisdn = createSubscription(email = email) + + val session = testClient.createSession(object{}.javaClass.enclosingMethod.name) ?: fail("Failed to create session") + val initRequest = testClient.createRequest( + DEST_REALM, + DEST_HOST, + session + ) ?: fail("Failed to create request") + + val ratingGroup = 10 + val serviceIdentifier = -1 + + // Authenticate + TestHelper.createInitRequest(initRequest.avps, msisdn) + + testClient.sendNextRequest(initRequest, session) + + waitForAnswer(session.sessionId) + + run { + val result = testClient.getAnswer(session.sessionId) + assertEquals(DIAMETER_SUCCESS, result?.resultCode) + val resultAvps = result?.resultAvps ?: fail("Missing AVPs") + assertEquals(DEST_HOST, resultAvps.getAvp(Avp.ORIGIN_HOST).utF8String) + assertEquals(DEST_REALM, resultAvps.getAvp(Avp.ORIGIN_REALM).utF8String) + assertEquals(RequestType.INITIAL_REQUEST.toLong(), resultAvps.getAvp(Avp.CC_REQUEST_TYPE).integer32.toLong()) + val resultMSCC = resultAvps.getAvp(Avp.MULTIPLE_SERVICES_CREDIT_CONTROL) + assertNull(resultMSCC, "There should not be any MSCC if there is no MSCC in the CCR") + } + + + // Use up all quota + val updateRequest = testClient.createRequest( + DEST_REALM, + DEST_HOST, + session + ) ?: fail("Failed to create request") + + TestHelper.createUpdateRequest(updateRequest.avps, msisdn, INITIAL_BALANCE + BUCKET_SIZE, 0L, ratingGroup, serviceIdentifier) + + testClient.sendNextRequest(updateRequest, session) + + waitForAnswer(session.sessionId) + + // First bucket request should reserve at least the default bucket balance + run { + val result = testClient.getAnswer(session.sessionId) + assertEquals(DIAMETER_SUCCESS, result?.resultCode) + val resultAvps = result?.resultAvps ?: fail("Missing AVPs") + assertEquals(DEST_HOST, resultAvps.getAvp(Avp.ORIGIN_HOST).utF8String) + assertEquals(DEST_REALM, resultAvps.getAvp(Avp.ORIGIN_REALM).utF8String) + assertEquals(RequestType.UPDATE_REQUEST.toLong(), resultAvps.getAvp(Avp.CC_REQUEST_TYPE).integer32.toLong()) + val resultMSCC = resultAvps.getAvp(Avp.MULTIPLE_SERVICES_CREDIT_CONTROL) + assertEquals(DIAMETER_SUCCESS, resultMSCC.grouped.getAvp(Avp.RESULT_CODE).integer32.toLong()) + assertEquals(ratingGroup.toLong(), resultMSCC.grouped.getAvp(Avp.RATING_GROUP).integer32.toLong()) + val granted = resultMSCC.grouped.getAvp(Avp.GRANTED_SERVICE_UNIT) + assertEquals(INITIAL_BALANCE, granted.grouped.getAvp(Avp.CC_TOTAL_OCTETS).unsigned64) + } + + val updateRequest2 = testClient.createRequest( + DEST_REALM, + DEST_HOST, + session + ) ?: fail("Failed to create request") + + TestHelper.createUpdateRequest(updateRequest2.avps, msisdn, INITIAL_BALANCE, INITIAL_BALANCE, ratingGroup, serviceIdentifier) + + testClient.sendNextRequest(updateRequest2, session) + + waitForAnswer(session.sessionId) + + run { + val result = testClient.getAnswer(session.sessionId) + assertEquals(DIAMETER_SUCCESS, result?.resultCode) + val resultAvps = result?.resultAvps ?: fail("Missing AVPs") + assertEquals(DEST_HOST, resultAvps.getAvp(Avp.ORIGIN_HOST).utF8String) + assertEquals(DEST_REALM, resultAvps.getAvp(Avp.ORIGIN_REALM).utF8String) + assertEquals(RequestType.UPDATE_REQUEST.toLong(), resultAvps.getAvp(Avp.CC_REQUEST_TYPE).integer32.toLong()) + val resultMSCC = resultAvps.getAvp(Avp.MULTIPLE_SERVICES_CREDIT_CONTROL) + assertEquals(ratingGroup.toLong(), resultMSCC.grouped.getAvp(Avp.RATING_GROUP).integer32.toLong()) + assertEquals(DIAMETER_CREDIT_LIMIT_REACHED, resultMSCC.grouped.getAvp(Avp.RESULT_CODE).integer32.toLong()) + } + + // Simulate UE disconnect by P-GW sending CCR-Terminate + val terminateRequest = testClient.createRequest( + DEST_REALM, + DEST_HOST, + session + ) ?: fail("Failed to create request") + TestHelper.createTerminateRequest(terminateRequest.avps, msisdn) + + testClient.sendNextRequest(terminateRequest, session) + + waitForAnswer(session.sessionId) + + run { + val result = testClient.getAnswer(session.sessionId) + assertEquals(DIAMETER_SUCCESS, result?.resultCode) + val resultAvps = result?.resultAvps ?: fail("Missing AVPs") + assertEquals(DEST_HOST, resultAvps.getAvp(Avp.ORIGIN_HOST).utF8String) + assertEquals(DEST_REALM, resultAvps.getAvp(Avp.ORIGIN_REALM).utF8String) + assertEquals(RequestType.TERMINATION_REQUEST.toLong(), resultAvps.getAvp(Avp.CC_REQUEST_TYPE).integer32.toLong()) + } + + + // Now restart again to get denied on first bucket request + val session2 = testClient.createSession(object{}.javaClass.enclosingMethod.name) ?: fail("Failed to create session") + + val initRequest2 = testClient.createRequest( + DEST_REALM, + DEST_HOST, + session2 + ) ?: fail("Failed to create request") + + + TestHelper.createInitRequest(initRequest2.avps, msisdn) + + testClient.sendNextRequest(initRequest2, session2) + + waitForAnswer(session2.sessionId) + + run { + val result = testClient.getAnswer(session2.sessionId) + assertEquals(DIAMETER_SUCCESS, result?.resultCode) + val resultAvps = result?.resultAvps ?: fail("Missing AVPs") + assertEquals(DEST_HOST, resultAvps.getAvp(Avp.ORIGIN_HOST).utF8String) + assertEquals(DEST_REALM, resultAvps.getAvp(Avp.ORIGIN_REALM).utF8String) + assertEquals(RequestType.INITIAL_REQUEST.toLong(), resultAvps.getAvp(Avp.CC_REQUEST_TYPE).integer32.toLong()) + val resultMSCC = resultAvps.getAvp(Avp.MULTIPLE_SERVICES_CREDIT_CONTROL) + assertNull(resultMSCC, "There should not be any MSCC if there is no MSCC in the CCR") + } + + // First Update Request with Requested-Service-Units ( no Used-Service-Units ), this should now be denied + val updateRequest3 = testClient.createRequest( + DEST_REALM, + DEST_HOST, + session2 + ) + + TestHelper.createUpdateRequest(updateRequest3!!.avps, msisdn, 0L, -1L, ratingGroup, serviceIdentifier) + + testClient.sendNextRequest(updateRequest3, session2) + + waitForAnswer(session2.sessionId) + + run { + val result = testClient.getAnswer(session2.sessionId) + assertEquals(DIAMETER_SUCCESS, result?.resultCode) + val resultAvps = result?.resultAvps ?: fail("Missing AVPs") + assertEquals(DEST_HOST, resultAvps.getAvp(Avp.ORIGIN_HOST).utF8String) + assertEquals(DEST_REALM, resultAvps.getAvp(Avp.ORIGIN_REALM).utF8String) + assertEquals(RequestType.UPDATE_REQUEST.toLong(), resultAvps.getAvp(Avp.CC_REQUEST_TYPE).integer32.toLong()) + val resultMSCC = resultAvps.getAvp(Avp.MULTIPLE_SERVICES_CREDIT_CONTROL) + assertEquals(DIAMETER_CREDIT_LIMIT_REACHED, resultMSCC.grouped.getAvp(Avp.RESULT_CODE).integer32.toLong()) + assertEquals(ratingGroup.toLong(), resultMSCC.grouped.getAvp(Avp.RATING_GROUP).integer32.toLong()) + val granted = resultMSCC.grouped.getAvp(Avp.GRANTED_SERVICE_UNIT) + assertEquals(0L, granted.grouped.getAvp(Avp.CC_TOTAL_OCTETS).unsigned64) + } + } /** * This test will check that we can handle CCR-U requests that also report CC-Time and CC-Service-Specific-Units diff --git a/ocsgw/src/main/java/org/ostelco/ocsgw/datasource/protobuf/ProtobufDataSource.kt b/ocsgw/src/main/java/org/ostelco/ocsgw/datasource/protobuf/ProtobufDataSource.kt index bf4fc0939..183465f54 100644 --- a/ocsgw/src/main/java/org/ostelco/ocsgw/datasource/protobuf/ProtobufDataSource.kt +++ b/ocsgw/src/main/java/org/ostelco/ocsgw/datasource/protobuf/ProtobufDataSource.kt @@ -125,6 +125,12 @@ class ProtobufDataSource { return } + // If CCR-I is only authentication we should block to force sync on CCR-U / CCA-U + if ((request.ccRequestType?.integer32 == CreditControlRequestType.INITIAL_REQUEST.number) && request.multipleServiceCreditControls.isEmpty()) { + blocked.add(answer.msisdn) + return + } + for (mssAnswerInfo in answer.extraInfo.msccInfoList) { for (msccRequest in request.multipleServiceCreditControls) { if (mssAnswerInfo.serviceIdentifier == msccRequest.serviceIdentifier && mssAnswerInfo.ratingGroup == msccRequest.ratingGroup) { From 1b3e7f172d787e7bbdbfd9bb1a06cd419b528e7d Mon Sep 17 00:00:00 2001 From: mpeterss Date: Wed, 16 Oct 2019 14:45:13 +0200 Subject: [PATCH 099/173] setup receiver before starting ccrkeepalive --- .../org/ostelco/ocsgw/datasource/protobuf/PubSubDataSource.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ocsgw/src/main/java/org/ostelco/ocsgw/datasource/protobuf/PubSubDataSource.kt b/ocsgw/src/main/java/org/ostelco/ocsgw/datasource/protobuf/PubSubDataSource.kt index 7535626e3..396188732 100644 --- a/ocsgw/src/main/java/org/ostelco/ocsgw/datasource/protobuf/PubSubDataSource.kt +++ b/ocsgw/src/main/java/org/ostelco/ocsgw/datasource/protobuf/PubSubDataSource.kt @@ -52,8 +52,8 @@ class PubSubDataSource( } publisher = setupPublisherToTopic(projectId, ccrTopicId) - initCcrKeepAlive() setupCcaReceiver(projectId, ccaSubscriptionId) + initCcrKeepAlive() setupActivateReceiver(projectId, activateSubscriptionId) } From 5ca86801726855ff9718af64e84d5632eea43f36 Mon Sep 17 00:00:00 2001 From: Prasanth Ullattil Date: Wed, 16 Oct 2019 15:51:09 +0200 Subject: [PATCH 100/173] Change API to use customer Id instead of contactEmail --- .../support/resources/HoustonResources.kt | 307 +++++++----------- houston/src/actions/subscriber.actions.js | 62 ++-- houston/src/components/Search/Profile.js | 3 +- houston/src/components/Search/Search.js | 5 +- houston/src/helpers/api.js | 2 +- .../ostelco/prime/storage/graph/Neo4jStore.kt | 26 +- .../prime/storage/graph/Neo4jStoreTest.kt | 10 +- .../org/ostelco/prime/storage/Variants.kt | 3 +- 8 files changed, 179 insertions(+), 239 deletions(-) diff --git a/customer-support-endpoint/src/main/kotlin/org/ostelco/prime/support/resources/HoustonResources.kt b/customer-support-endpoint/src/main/kotlin/org/ostelco/prime/support/resources/HoustonResources.kt index c17c53490..a00c9f896 100644 --- a/customer-support-endpoint/src/main/kotlin/org/ostelco/prime/support/resources/HoustonResources.kt +++ b/customer-support-endpoint/src/main/kotlin/org/ostelco/prime/support/resources/HoustonResources.kt @@ -3,6 +3,7 @@ package org.ostelco.prime.support.resources import arrow.core.Either import arrow.core.flatMap import arrow.core.left +import arrow.core.right import io.dropwizard.auth.Auth import org.ostelco.prime.apierror.ApiError import org.ostelco.prime.apierror.ApiErrorCode @@ -52,22 +53,22 @@ class ProfilesResource { * Get the subscriber profile. */ @GET - @Path("{id}") + @Path("{query}") @Produces(MediaType.APPLICATION_JSON) fun getProfile(@Auth token: AccessTokenPrincipal?, @NotNull - @PathParam("id") - id: String): Response = + @PathParam("query") + query: String): Response = if (token == null) { Response.status(Response.Status.UNAUTHORIZED) } else { - if (!isEmail(id)) { - logger.info("${token.name} Accessing profile for msisdn:$id") - getProfileForMsisdn(id) + if (!isEmail(query)) { + logger.info("${token.name} Accessing profile for msisdn:$query") + getProfileListForMsisdn(query) .responseBuilder() } else { - logger.info("${token.name} Accessing profile for email:$id") - getProfile(contactEmail = id) + logger.info("${token.name} Accessing profile for email:$query") + getProfileList(contactEmail = query) .responseBuilder() } }.build() @@ -76,17 +77,17 @@ class ProfilesResource { * Get the subscriptions for this subscriber. */ @GET - @Path("{email}/subscriptions") + @Path("{id}/subscriptions") @Produces(MediaType.APPLICATION_JSON) fun getSubscriptions(@Auth token: AccessTokenPrincipal?, @NotNull - @PathParam("email") - email: String): Response = + @PathParam("id") + id: String): Response = if (token == null) { Response.status(Response.Status.UNAUTHORIZED) } else { - logger.info("${token.name} Accessing subscriptions for email:$email") - getSubscriptions(contactEmail = email) + logger.info("${token.name} Accessing subscriptions for customerId: $id") + getSubscriptions(customerId = id) .responseBuilder() }.build() @@ -95,40 +96,48 @@ class ProfilesResource { * Get all the eKYC scan information for this subscriber. */ @GET - @Path("{email}/scans") + @Path("{id}/scans") @Produces(MediaType.APPLICATION_JSON) fun getAllScanInformation(@Auth token: AccessTokenPrincipal?, @NotNull - @PathParam("email") - email: String): Response = + @PathParam("id") + id: String): Response = if (token == null) { Response.status(Response.Status.UNAUTHORIZED) } else { - logger.info("${token.name} Accessing scan information for email:$email") - getAllScanInformation(contactEmail = email) + logger.info("${token.name} Accessing scan information for customerId: $id") + getAllScanInformation(customerId = id) .responseBuilder() }.build() - private fun getAllScanInformation(contactEmail: String): Either> { + private fun getAllScanInformation(customerId: String): Either> { return try { - storage.getIdentityForContactEmail(contactEmail = contactEmail).flatMap {identity: Identity -> + storage.getIdentityForCustomerId(id = customerId).flatMap { identity: Identity -> storage.getAllScanInformation(identity = identity) }.mapLeft { NotFoundError("Failed to fetch scan information.", ApiErrorCode.FAILED_TO_FETCH_SCAN_INFORMATION, it) } } catch (e: Exception) { - logger.error("Failed to fetch scan information for customer with contactEmail - $contactEmail", e) + logger.error("Failed to fetch scan information for customer with customerId: $customerId", e) Either.left(NotFoundError("Failed to fetch scan information", ApiErrorCode.FAILED_TO_FETCH_SCAN_INFORMATION)) } } // TODO: Reuse the one from SubscriberDAO - private fun getProfile(contactEmail: String): Either { + private fun getProfileList(contactEmail: String): Either> { return try { - storage.getIdentityForContactEmail(contactEmail = contactEmail).flatMap {identity: Identity -> - storage.getCustomer(identity) - }.mapLeft { + storage.getIdentitiesForContactEmail(contactEmail = contactEmail).mapLeft { NotFoundError("Failed to fetch profile.", ApiErrorCode.FAILED_TO_FETCH_CUSTOMER, it) + }.flatMap { identityList -> + val customerList = identityList.mapNotNull { identity: Identity -> + storage.getCustomer(identity).fold( + { + logger.error("Error fetching customer for $identity, $it") + null + }, + { it }) + } + Either.right(customerList) } } catch (e: Exception) { logger.error("Failed to fetch profile for customer with contactEmail - $contactEmail", e) @@ -142,10 +151,12 @@ class ProfilesResource { return pattern.matcher(email).matches() } - private fun getProfileForMsisdn(msisdn: String): Either { + private fun getProfileListForMsisdn(msisdn: String): Either> { return try { storage.getCustomerForMsisdn(msisdn).mapLeft { NotFoundError("Failed to fetch profile.", ApiErrorCode.FAILED_TO_FETCH_CUSTOMER, it) + }.map { + listOf(it) } } catch (e: Exception) { logger.error("Failed to fetch profile for msisdn $msisdn", e) @@ -154,78 +165,18 @@ class ProfilesResource { } // TODO: Reuse the one from SubscriberDAO - private fun getSubscriptions(contactEmail: String): Either> { + private fun getSubscriptions(customerId: String): Either> { return try { - storage.getIdentityForContactEmail(contactEmail = contactEmail).flatMap {identity: Identity -> + storage.getIdentityForCustomerId(id = customerId).flatMap { identity: Identity -> storage.getSubscriptions(identity) }.mapLeft { NotFoundError("Failed to get subscriptions.", ApiErrorCode.FAILED_TO_FETCH_SUBSCRIPTIONS, it) } } catch (e: Exception) { - logger.error("Failed to get subscriptions for customer with contactEmail - $contactEmail", e) + logger.error("Failed to get subscriptions for customer with customerId: - $customerId", e) InternalServerError("Failed to get subscriptions", ApiErrorCode.FAILED_TO_FETCH_SUBSCRIPTIONS).left() } } - - /** - * Fetches and return all plans that a subscriber subscribes - * to if any. - */ - @GET - @Path("{email}/plans") - @Produces("application/json") - fun getPlans(@PathParam("email") email: String): Response = - storage.getIdentityForContactEmail(contactEmail = email).flatMap { identity: Identity -> - storage.getPlans(identity = identity) - }.mapLeft { - ApiErrorMapper.mapStorageErrorToApiError("Failed to fetch plans", - ApiErrorCode.FAILED_TO_FETCH_PLANS_FOR_SUBSCRIBER, - it) - } - .responseBuilder() - .build() - - /** - * Attaches (subscribes) a subscriber to a plan. - */ - @POST - @Path("{email}/plans/{planId}") - @Produces("application/json") - fun attachPlan(@PathParam("email") email: String, - @PathParam("planId") planId: String, - @QueryParam("trial_end") trialEnd: Long): Response = - storage.getIdentityForContactEmail(contactEmail = email).flatMap { identity: Identity -> - storage.subscribeToPlan( - identity = identity, - planId = planId, - trialEnd = trialEnd) - }.mapLeft { - ApiErrorMapper.mapStorageErrorToApiError("Failed to store subscription", - ApiErrorCode.FAILED_TO_STORE_SUBSCRIPTION, - it) - } - .responseBuilder(Response.Status.CREATED) - .build() - - /** - * Removes a plan from the list subscriptions for a subscriber. - */ - @DELETE - @Path("{email}/plans/{planId}") - @Produces("application/json") - fun detachPlan(@PathParam("email") email: String, - @PathParam("planId") planId: String): Response = - storage.getIdentityForContactEmail(contactEmail = email).flatMap { identity: Identity -> - storage.unsubscribeFromPlan( - identity = identity, - planId = planId) - }.mapLeft { - ApiErrorMapper.mapStorageErrorToApiError("Failed to remove subscription", - ApiErrorCode.FAILED_TO_REMOVE_SUBSCRIPTION, - it) - } - .responseBuilder() - .build() } /** @@ -240,30 +191,30 @@ class BundlesResource { * Get all bundles for the subscriber. */ @GET - @Path("{email}") + @Path("{id}") @Produces(MediaType.APPLICATION_JSON) - fun getBundlesByEmail(@Auth token: AccessTokenPrincipal?, - @NotNull - @PathParam("email") - email: String): Response = + fun getBundlesById(@Auth token: AccessTokenPrincipal?, + @NotNull + @PathParam("id") + id: String): Response = if (token == null) { Response.status(Response.Status.UNAUTHORIZED) } else { - logger.info("${token.name} Accessing bundles for $email") - getBundles(contactEmail = email) + logger.info("${token.name} Accessing bundles for customerId: $id") + getBundles(customerId = id) .responseBuilder() }.build() // TODO: Reuse the one from SubscriberDAO - private fun getBundles(contactEmail: String): Either> { + private fun getBundles(customerId: String): Either> { return try { - storage.getIdentityForContactEmail(contactEmail = contactEmail).flatMap { identity: Identity -> + storage.getIdentityForCustomerId(id = customerId).flatMap { identity: Identity -> storage.getBundles(identity) }.mapLeft { NotFoundError("Failed to get bundles. ${it.message}", ApiErrorCode.FAILED_TO_FETCH_BUNDLES) } } catch (e: Exception) { - logger.error("Failed to get bundles for customer with contactEmail - $contactEmail", e) + logger.error("Failed to get bundles for customer with customerId: $customerId", e) Either.left(NotFoundError("Failed to get bundles", ApiErrorCode.FAILED_TO_FETCH_BUNDLES)) } } @@ -281,30 +232,30 @@ class PurchaseResource { * Get all purchase history for the subscriber. */ @GET - @Path("{email}") + @Path("{id}") @Produces(MediaType.APPLICATION_JSON) fun getPurchaseHistoryByEmail(@Auth token: AccessTokenPrincipal?, @NotNull - @PathParam("email") - email: String): Response = + @PathParam("id") + id: String): Response = if (token == null) { Response.status(Response.Status.UNAUTHORIZED) } else { - logger.info("${token.name} Accessing bundles for $email") - getPurchaseHistory(contactEmail = email) + logger.info("${token.name} Accessing bundles for customerId: $id") + getPurchaseHistory(customerId = id) .responseBuilder() }.build() // TODO: Reuse the one from SubscriberDAO - private fun getPurchaseHistory(contactEmail: String): Either> { + private fun getPurchaseHistory(customerId: String): Either> { return try { - storage.getIdentityForContactEmail(contactEmail = contactEmail).flatMap { identity: Identity -> + storage.getIdentityForCustomerId(id = customerId).flatMap { identity: Identity -> storage.getPurchaseRecords(identity) }.bimap( { NotFoundError("Failed to get purchase history.", ApiErrorCode.FAILED_TO_FETCH_PAYMENT_HISTORY, it) }, { it.toList() }) } catch (e: Exception) { - logger.error("Failed to get purchase history for customer with contactEmail - $contactEmail", e) + logger.error("Failed to get purchase history for customer with customerId - $customerId", e) Either.left(InternalServerError("Failed to get purchase history", ApiErrorCode.FAILED_TO_FETCH_PAYMENT_HISTORY)) } } @@ -322,33 +273,33 @@ class RefundResource { * Refund a specified purchase for the subscriber. */ @PUT - @Path("{email}") + @Path("{id}") @Produces(MediaType.APPLICATION_JSON) - fun refundPurchaseByEmail(@Auth token: AccessTokenPrincipal?, - @NotNull - @PathParam("email") - email: String, - @NotNull - @QueryParam("purchaseRecordId") - purchaseRecordId: String, - @NotNull - @QueryParam("reason") - reason: String): Response = + fun refundPurchaseById(@Auth token: AccessTokenPrincipal?, + @NotNull + @PathParam("id") + id: String, + @NotNull + @QueryParam("purchaseRecordId") + purchaseRecordId: String, + @NotNull + @QueryParam("reason") + reason: String): Response = if (token == null) { Response.status(Response.Status.UNAUTHORIZED) } else { - logger.info("${token.name} Refunding purchase for $email at id: $purchaseRecordId") - refundPurchase(email, purchaseRecordId, reason) + logger.info("${token.name} Refunding purchase for customerId: $id at id: $purchaseRecordId") + refundPurchase(id, purchaseRecordId, reason) .map { - logger.info(NOTIFY_OPS_MARKER, "${token.name} refunded the purchase (id:$purchaseRecordId) for $email ") + logger.info(NOTIFY_OPS_MARKER, "${token.name} refunded the purchase (id:$purchaseRecordId) for customerId: $id ") it } .responseBuilder() }.build() - private fun refundPurchase(contactEmail: String, purchaseRecordId: String, reason: String): Either { + private fun refundPurchase(customerId: String, purchaseRecordId: String, reason: String): Either { return try { - storage.getIdentityForContactEmail(contactEmail = contactEmail).flatMap { identity: Identity -> + storage.getIdentityForCustomerId(id = customerId).flatMap { identity: Identity -> storage.refundPurchase(identity, purchaseRecordId, reason) }.mapLeft { when (it) { @@ -357,7 +308,7 @@ class RefundResource { } } } catch (e: Exception) { - logger.error("Failed to refund purchase for customer with contactEmail - $contactEmail, id: $purchaseRecordId", e) + logger.error("Failed to refund purchase for customer with customerId - $customerId, id: $purchaseRecordId", e) Either.left(InternalServerError("Failed to refund purchase", ApiErrorCode.FAILED_TO_REFUND_PURCHASE)) } } @@ -375,24 +326,24 @@ class ContextResource { * Get context for the subscriber. */ @GET - @Path("{email}") + @Path("{id}") @Produces(MediaType.APPLICATION_JSON) - fun getContextByEmail(@Auth token: AccessTokenPrincipal?, - @NotNull - @PathParam("email") - email: String): Response = + fun getContextById(@Auth token: AccessTokenPrincipal?, + @NotNull + @PathParam("id") + id: String): Response = if (token == null) { Response.status(Response.Status.UNAUTHORIZED) } else { - logger.info("${token.name} Accessing context for $email") - getContext(contactEmail = email) + logger.info("${token.name} Accessing context for customer with id: $id") + getContext(customerId = id) .responseBuilder() }.build() // TODO: Reuse the one from SubscriberDAO - fun getContext(contactEmail: String): Either { + private fun getContext(customerId: String): Either { return try { - storage.getIdentityForContactEmail(contactEmail = contactEmail).flatMap { identity: Identity -> + storage.getIdentityForCustomerId(id = customerId).flatMap { identity: Identity -> storage.getCustomer(identity).map { customer -> storage.getAllRegionDetails(identity = identity) .fold( @@ -403,54 +354,30 @@ class ContextResource { NotFoundError("Failed to fetch customer.", ApiErrorCode.FAILED_TO_FETCH_CONTEXT, it) } } catch (e: Exception) { - logger.error("Failed to fetch context for customer with contactEmail - $contactEmail", e) + logger.error("Failed to fetch context for customer with id - $customerId", e) Either.left(NotFoundError("Failed to fetch context", ApiErrorCode.FAILED_TO_FETCH_CONTEXT)) } } } -open class HoustonResource { - private val logger by getLogger() - - private val storage by lazy { getResource() } - private val notifier by lazy { getResource() } - - fun getCustomerId(contactEmail: String): Either { - return try { - storage.getIdentityForContactEmail(contactEmail = contactEmail).flatMap { identity: Identity -> - storage.getCustomer(identity = identity) - }.mapLeft { - NotFoundError("Did not find subscription.", ApiErrorCode.FAILED_TO_FETCH_SUBSCRIPTIONS, it) - }.map { - it.id - } - } catch (e: Exception) { - logger.error("Did not find subscription for email $contactEmail", e) - InternalServerError("Did not find subscription", ApiErrorCode.FAILED_TO_FETCH_SUBSCRIPTIONS).left() - } - } -} - /** * Resource used to handle notification related REST calls. */ @Path("/support/notify") -class NotifyResource: HoustonResource() { +class NotifyResource { private val logger by getLogger() - - private val storage by lazy { getResource() } private val notifier by lazy { getResource() } /** * Sends a notification to all devices for a subscriber. */ @PUT - @Path("{email}") + @Path("{id}") @Produces(MediaType.APPLICATION_JSON) fun sendNotificationByEmail(@Auth token: AccessTokenPrincipal?, @NotNull - @PathParam("email") - email: String, + @PathParam("id") + id: String, @NotNull @QueryParam("title") title: String, @@ -460,14 +387,10 @@ class NotifyResource: HoustonResource() { if (token == null) { Response.status(Response.Status.UNAUTHORIZED) } else { - getCustomerId(contactEmail = email) - .map { customerId -> - logger.info("${token.name} Sending notification to $email customerId: $customerId") - val data = mapOf("timestamp" to "${System.currentTimeMillis()}") - notifier.notify(customerId, title, message, data) - customerId - } - .responseBuilder("Message Sent") + logger.info("${token.name} Sending notification to customerId: $id") + val data = mapOf("timestamp" to "${System.currentTimeMillis()}") + notifier.notify(id, title, message, data) + id.right().responseBuilder("Message Sent") }.build() } @@ -476,35 +399,28 @@ class NotifyResource: HoustonResource() { * Resource used to handle audit log related REST calls. */ @Path("/support/auditLog") -class AuditLogResource: HoustonResource() { - +class AuditLogResource { private val logger by getLogger() - - private val storage by lazy { getResource() } private val auditLogStore by lazy { getResource() } /** * Fetch all audit logs for a subscriber. */ @GET - @Path("{email}") + @Path("{id}") @Produces(MediaType.APPLICATION_JSON) fun query(@Auth token: AccessTokenPrincipal?, @NotNull - @PathParam("email") - email: String): Response = + @PathParam("id") + id: String): Response = if (token == null) { Response.status(Response.Status.UNAUTHORIZED) } else { - getCustomerId(contactEmail = email) - .flatMap { customerId -> - logger.info("${token.name} fetching audit log of $email customerId: $customerId") - auditLogStore.getCustomerActivityHistory(customerId = customerId) - .mapLeft { errorMessage -> - InternalServerError(errorMessage, FAILED_TO_FETCH_AUDIT_LOGS) - } - } - .responseBuilder() + logger.info("${token.name} fetching audit log of customerId: $id") + auditLogStore.getCustomerActivityHistory(customerId = id) + .mapLeft { errorMessage -> + InternalServerError(errorMessage, FAILED_TO_FETCH_AUDIT_LOGS) + }.responseBuilder() }.build() } @@ -513,7 +429,6 @@ class AuditLogResource: HoustonResource() { */ @Path("/support/customer") class CustomerResource { - private val logger by getLogger() private val storage by lazy { getResource() } @@ -521,30 +436,30 @@ class CustomerResource { * Remove customer from Prime. */ @DELETE - @Path("{email}") + @Path("{id}") @Produces(MediaType.APPLICATION_JSON) fun removeCustomer(@Auth token: AccessTokenPrincipal?, - @NotNull - @PathParam("email") - email: String): Response = + @NotNull + @PathParam("id") + id: String): Response = if (token == null) { Response.status(Response.Status.UNAUTHORIZED) } else { - logger.info("${token.name} Removing the customer for $email") - removeCustomer(contactEmail = email) + logger.info("${token.name} Removing the customer for customerId: $id") + removeCustomer(customerId = id) .responseBuilder(Response.Status.NO_CONTENT) }.build() // TODO: Reuse the one from SubscriberDAO - private fun removeCustomer(contactEmail: String): Either { + private fun removeCustomer(customerId: String): Either { return try { - storage.getIdentityForContactEmail(contactEmail = contactEmail).flatMap { identity: Identity -> + storage.getIdentityForCustomerId(id = customerId).flatMap { identity: Identity -> storage.removeCustomer(identity) }.mapLeft { NotFoundError("Failed to remove customer.", ApiErrorCode.FAILED_TO_REMOVE_CUSTOMER, it) } } catch (e: Exception) { - logger.error("Failed to fetch profile for customer with contactEmail - $contactEmail", e) + logger.error("Failed to fetch profile for customer with customerId - $customerId", e) Either.left(NotFoundError("Failed to remove customer", ApiErrorCode.FAILED_TO_REMOVE_CUSTOMER)) } } diff --git a/houston/src/actions/subscriber.actions.js b/houston/src/actions/subscriber.actions.js index fcc2a85db..d8c36f965 100644 --- a/houston/src/actions/subscriber.actions.js +++ b/houston/src/actions/subscriber.actions.js @@ -83,80 +83,80 @@ const fetchSubscriberById = (id) => ({ } }); -const fetchContextByEmail = (email) => ({ +const fetchContextById = (id) => ({ [CALL_API]: { actions: [ actions.contextByEmailRequest, actions.contextByEmailSuccess, actions.contextByEmailFailure], - endpoint: `context/${email}`, + endpoint: `context/${id}`, method: 'GET' } }); -const fetchSubscriptionsByEmail = (email) => ({ +const fetchSubscriptionsById = (id) => ({ [CALL_API]: { actions: [ actions.subscriptionsRequest, actions.subscriptionsSuccess, actions.subscriptionsFailure], - endpoint: `profiles/${email}/subscriptions`, + endpoint: `profiles/${id}/subscriptions`, method: 'GET' } }); -const fetchBundlesByEmail = (email) => ({ +const fetchBundlesById = (id) => ({ [CALL_API]: { actions: [ actions.bundlesRequest, actions.bundlesSuccess, actions.bundlesFailure], - endpoint: `bundles/${email}`, + endpoint: `bundles/${id}`, method: 'GET' } }); -const fetchPaymentHistoryByEmail = (email) => ({ +const fetchPaymentHistoryById = (id) => ({ [CALL_API]: { actions: [ actions.paymentHistoryRequest, actions.paymentHistorySuccess, actions.paymentHistoryFailure], - endpoint: `purchases/${email}`, + endpoint: `purchases/${id}`, method: 'GET' } }); -const putRefundPurchaseByEmail = (email, purchaseRecordId, reason) => ({ +const putRefundPurchaseById = (id, purchaseRecordId, reason) => ({ [CALL_API]: { actions: [ actions.refundPaymentRequest, actions.refundPaymentSuccess, actions.refundPaymentFailure], - endpoint: `refund/${email}`, + endpoint: `refund/${id}`, method: 'PUT', params: { purchaseRecordId, reason } } }); -const fetchAuditLogsByEmail = (email) => ({ +const fetchAuditLogsById = (id) => ({ [CALL_API]: { actions: [ actions.auditLogsRequest, actions.auditLogsSuccess, actions.auditLogsFailure], - endpoint: `auditLog/${email}`, + endpoint: `auditLog/${id}`, method: 'GET' } }); -const deleteUserByEmail = (email) => ({ +const deleteUserById = (id) => ({ [CALL_API]: { actions: [ actions.deleteUserRequest, actions.deleteUserSuccess, actions.deleteUserFailure], - endpoint: `customer/${email}`, + endpoint: `customer/${id}`, allowEmptyResponse: true, method: 'DELETE' } @@ -174,15 +174,15 @@ const getSubscriberAndBundlesByEmail = (email) => (dispatch, getState) => { return dispatch(fetchSubscriberById(email)) .then(() => { - // Get the email from the fetched user - const subscriberEmail = encodeEmail(_.get(getState(), 'subscriber.contactEmail')); - if (subscriberEmail) { - dispatch(fetchContextByEmail(subscriberEmail)).catch(handleError); - dispatch(fetchAuditLogsByEmail(subscriberEmail)).catch(handleError); - dispatch(fetchSubscriptionsByEmail(subscriberEmail)).catch(handleError); - return dispatch(fetchBundlesByEmail(subscriberEmail)) + // Get the id from the fetched user + const subscriberId = _.get(getState(), 'subscriber[0].id'); + if (subscriberId) { + dispatch(fetchContextById(subscriberId)).catch(handleError); + dispatch(fetchAuditLogsById(subscriberId)).catch(handleError); + dispatch(fetchSubscriptionsById(subscriberId)).catch(handleError); + return dispatch(fetchBundlesById(subscriberId)) .then(() => { - return dispatch(fetchPaymentHistoryByEmail(subscriberEmail)); + return dispatch(fetchPaymentHistoryById(subscriberId)); }) .catch(handleError); } @@ -197,12 +197,12 @@ const refundPurchase = (purchaseRecordId, reason) => (dispatch, getState) => { dispatch(alertActions.alertError(error)); }; - // Get the email from the fetched user - const subscriberEmail = encodeEmail(_.get(getState(), 'subscriber.contactEmail')); - if (subscriberEmail) { - return dispatch(putRefundPurchaseByEmail(subscriberEmail, purchaseRecordId, reason)) + // Get the id from the fetched user + const subscriberId = _.get(getState(), 'subscriber[0].id'); + if (subscriberId) { + return dispatch(putRefundPurchaseById(subscriberId, purchaseRecordId, reason)) .then(() => { - return dispatch(fetchPaymentHistoryByEmail(subscriberEmail)); + return dispatch(fetchPaymentHistoryById(subscriberId)); }) .catch(handleError); } @@ -215,10 +215,10 @@ const deleteUser = () => (dispatch, getState) => { dispatch(alertActions.alertError({message})); }; - // Get the email from the fetched user - const subscriberEmail = encodeEmail(_.get(getState(), 'subscriber.contactEmail')); - if (subscriberEmail) { - return dispatch(deleteUserByEmail(subscriberEmail)) + // Get the id from the fetched user + const subscriberId = _.get(getState(), 'subscriber[0].id'); + if (subscriberId) { + return dispatch(deleteUserById(subscriberId)) .catch(handleError); } }; diff --git a/houston/src/components/Search/Profile.js b/houston/src/components/Search/Profile.js index ad72a29fd..71a819faf 100644 --- a/houston/src/components/Search/Profile.js +++ b/houston/src/components/Search/Profile.js @@ -2,6 +2,7 @@ import React from 'react'; import { connect } from 'react-redux'; import PropTypes from 'prop-types'; import { Col, Row, Card, CardBody, CardTitle, Button } from 'reactstrap'; +import _ from 'lodash'; import { subscriberActions } from '../../actions/subscriber.actions'; import Subscription from './Subscription'; @@ -96,8 +97,8 @@ Profile.propTypes = { }; function mapStateToProps(state) { - const { subscriber } = state; const { subscriptions } = state; + const subscriber = _.get(state, 'subscriber[0]') return { profile: subscriber, subscriptions diff --git a/houston/src/components/Search/Search.js b/houston/src/components/Search/Search.js index def31a8db..0e78a65fb 100644 --- a/houston/src/components/Search/Search.js +++ b/houston/src/components/Search/Search.js @@ -1,6 +1,7 @@ import React from 'react'; import { connect } from 'react-redux'; import PropTypes from 'prop-types'; +import _ from 'lodash'; import { subscriberActions } from '../../actions/subscriber.actions'; import SearchForm from './SearchForm'; @@ -14,7 +15,7 @@ class Search extends React.Component { } render() { - const hasResults = this.props.profile.nickname || false; + const hasResults = (this.props.profile && this.props.profile.nickname) || false; return (
@@ -38,7 +39,7 @@ Search.propTypes = { function mapStateToProps(state) { const { loggedIn } = state.authentication; - const { subscriber } = state; + const subscriber = _.get(state, 'subscriber[0]') return { loggedIn, profile: subscriber diff --git a/houston/src/helpers/api.js b/houston/src/helpers/api.js index 06740de43..e3c6351f9 100644 --- a/houston/src/helpers/api.js +++ b/houston/src/helpers/api.js @@ -41,7 +41,7 @@ const apiCaller = async (endpoint, method, body, allowEmptyResponse, params = [] Accept: 'application/json', Authorization: auth.header, //TODO: remove this when we are ready to deploy in all versions of prime - //"x-mode": "prime-direct" + "x-mode": "prime-direct" } }; if (body) { diff --git a/neo4j-store/src/main/kotlin/org/ostelco/prime/storage/graph/Neo4jStore.kt b/neo4j-store/src/main/kotlin/org/ostelco/prime/storage/graph/Neo4jStore.kt index fdd62a707..a763809aa 100644 --- a/neo4j-store/src/main/kotlin/org/ostelco/prime/storage/graph/Neo4jStore.kt +++ b/neo4j-store/src/main/kotlin/org/ostelco/prime/storage/graph/Neo4jStore.kt @@ -1995,17 +1995,37 @@ object Neo4jStoreSingleton : GraphStore { } - override fun getIdentityForContactEmail(contactEmail: String): Either = readTransaction { + override fun getIdentityForCustomerId(id: String): Either = readTransaction { read(""" - MATCH (:${customerEntity.name} { contactEmail:'$contactEmail' })<-[r:${identifiesRelation.name}]-(identity:${identityEntity.name}) + MATCH (:${customerEntity.name} { id:'$id' })<-[r:${identifiesRelation.name}]-(identity:${identityEntity.name}) RETURN identity, r.provider as provider """.trimIndent(), transaction) { if (it.hasNext()) { - val record = it.single() + val record = it.list().first() val identity = identityEntity.createEntity(record.get("identity").asMap()) val provider = record.get("provider").asString() Either.right(ModelIdentity(id = identity.id, type = identity.type, provider = provider)) + } else { + Either.left(NotFoundError(type = customerEntity.name, id = id)) + } + } + } + + override fun getIdentitiesForContactEmail(contactEmail: String): Either> = readTransaction { + read(""" + MATCH (:${customerEntity.name} { contactEmail:'$contactEmail' })<-[r:${identifiesRelation.name}]-(identity:${identityEntity.name}) + RETURN identity, r.provider as provider + """.trimIndent(), + transaction) { + if (it.hasNext()) { + val identityList = mutableListOf() + it.forEach { record -> + val identity = identityEntity.createEntity(record.get("identity").asMap()) + val provider = record.get("provider").asString() + identityList.add(ModelIdentity(id = identity.id, type = identity.type, provider = provider)) + } + Either.right(identityList) } else { Either.left(NotFoundError(type = customerEntity.name, id = contactEmail)) } diff --git a/neo4j-store/src/test/kotlin/org/ostelco/prime/storage/graph/Neo4jStoreTest.kt b/neo4j-store/src/test/kotlin/org/ostelco/prime/storage/graph/Neo4jStoreTest.kt index 72ac97599..305d3cf74 100644 --- a/neo4j-store/src/test/kotlin/org/ostelco/prime/storage/graph/Neo4jStoreTest.kt +++ b/neo4j-store/src/test/kotlin/org/ostelco/prime/storage/graph/Neo4jStoreTest.kt @@ -150,9 +150,10 @@ class Neo4jStoreTest { referredBy = null) .mapLeft { fail(it.message) } - Neo4jStoreSingleton.getIdentityForContactEmail(contactEmail = EMAIL).bimap( + Neo4jStoreSingleton.getIdentitiesForContactEmail(contactEmail = EMAIL).bimap( { fail(it.message) }, - { identity: Identity -> + { list-> + val identity: Identity = list.first() Neo4jStoreSingleton.getCustomer(identity).bimap( { fail(it.message) }, { assertEquals(CUSTOMER, it) })}) @@ -167,9 +168,10 @@ class Neo4jStoreTest { referredBy = null) .mapLeft { fail(it.message) } - Neo4jStoreSingleton.getIdentityForContactEmail(contactEmail = EMAIL).bimap( + Neo4jStoreSingleton.getIdentitiesForContactEmail(contactEmail = EMAIL).bimap( { fail(it.message) }, - { identity: Identity -> + { list -> + val identity: Identity = list.first() assertEquals("EMAIL", identity.type) assertEquals(EMAIL, identity.id) assertEquals(IDENTITY.provider, identity.provider) diff --git a/prime-modules/src/main/kotlin/org/ostelco/prime/storage/Variants.kt b/prime-modules/src/main/kotlin/org/ostelco/prime/storage/Variants.kt index 1f7f4771d..d75971791 100644 --- a/prime-modules/src/main/kotlin/org/ostelco/prime/storage/Variants.kt +++ b/prime-modules/src/main/kotlin/org/ostelco/prime/storage/Variants.kt @@ -197,7 +197,8 @@ interface AdminGraphStore { fun getCustomerForMsisdn(msisdn: String): Either - fun getIdentityForContactEmail(contactEmail: String): Either + fun getIdentityForCustomerId(id: String): Either + fun getIdentitiesForContactEmail(contactEmail: String): Either> /** * Link Customer to MSISDN From 63760c13f4991e7cf14248c29e00436200937f29 Mon Sep 17 00:00:00 2001 From: Prasanth Ullattil Date: Wed, 16 Oct 2019 16:11:30 +0200 Subject: [PATCH 101/173] Use array instead of Object --- houston/src/reducers/subscriber.reducer.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/houston/src/reducers/subscriber.reducer.js b/houston/src/reducers/subscriber.reducer.js index fc7bcfa46..dc9f88a4d 100644 --- a/houston/src/reducers/subscriber.reducer.js +++ b/houston/src/reducers/subscriber.reducer.js @@ -8,9 +8,7 @@ export const subscriber = handleActions( [actions.subscriberByEmailRequest]: (state, action) => ({ loading: true }), - [actions.subscriberByEmailSuccess]: (state, action) => ({ - ...action.payload - }), + [actions.subscriberByEmailSuccess]: (state, action) => (action.payload), // Array of subscribers [actions.subscriberByEmailFailure]: (state, action) => ({ ...action.payload }) From 294ad2ffb2e085549629589d6670cd596fe55f72 Mon Sep 17 00:00:00 2001 From: Prasanth Ullattil Date: Wed, 16 Oct 2019 16:35:40 +0200 Subject: [PATCH 102/173] Fix notifications --- houston/src/actions/notifiy.actions.js | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/houston/src/actions/notifiy.actions.js b/houston/src/actions/notifiy.actions.js index c72099deb..e8e78153a 100644 --- a/houston/src/actions/notifiy.actions.js +++ b/houston/src/actions/notifiy.actions.js @@ -39,13 +39,13 @@ const { setNotificationType, } = actions; -const putNotificationByEmail = (email, title, message) => ({ +const putNotificationById = (id, title, message) => ({ [CALL_API]: { actions: [ actions.notifyRequest, actions.notifySuccess, actions.notifyFailure], - endpoint: `notify/${email}`, + endpoint: `notify/${id}`, method: 'PUT', allowEmptyResponse: true, params: { message, title } @@ -57,10 +57,11 @@ const sendNotificationToSubscriber = (title, message) => (dispatch, getState) => console.log('Error reported.', error); dispatch(alertActions.alertError(error)); }; - // Get the email from the fetched user - const subscriberEmail = encodeEmail(_.get(getState(), 'subscriber.contactEmail')); - if (subscriberEmail) { - return dispatch(putNotificationByEmail(subscriberEmail, title, message)) + + // Get the id from the fetched user + const subscriberId = _.get(getState(), 'subscriber[0].id'); + if (subscriberId) { + return dispatch(putNotificationById(subscriberId, title, message)) .catch(handleError); } }; From 25017a3137413a5d37d1f001abb662b2e3fbdf9a Mon Sep 17 00:00:00 2001 From: Prasanth Ullattil Date: Wed, 16 Oct 2019 18:11:18 +0200 Subject: [PATCH 103/173] Remove duplicates --- .../org/ostelco/prime/support/resources/HoustonResources.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/customer-support-endpoint/src/main/kotlin/org/ostelco/prime/support/resources/HoustonResources.kt b/customer-support-endpoint/src/main/kotlin/org/ostelco/prime/support/resources/HoustonResources.kt index a00c9f896..2f4e53d7d 100644 --- a/customer-support-endpoint/src/main/kotlin/org/ostelco/prime/support/resources/HoustonResources.kt +++ b/customer-support-endpoint/src/main/kotlin/org/ostelco/prime/support/resources/HoustonResources.kt @@ -136,7 +136,7 @@ class ProfilesResource { null }, { it }) - } + }.distinctBy { it.id } Either.right(customerList) } } catch (e: Exception) { From 07852f8871b249360c2c02239123acb6f7709965 Mon Sep 17 00:00:00 2001 From: Prasanth Ullattil Date: Wed, 16 Oct 2019 20:54:05 +0200 Subject: [PATCH 104/173] Use customerId instead of contactEmail --- .../src/main/kotlin/org/ostelco/at/jersey/Tests.kt | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/acceptance-tests/src/main/kotlin/org/ostelco/at/jersey/Tests.kt b/acceptance-tests/src/main/kotlin/org/ostelco/at/jersey/Tests.kt index 0f0d7928d..72cdaad53 100644 --- a/acceptance-tests/src/main/kotlin/org/ostelco/at/jersey/Tests.kt +++ b/acceptance-tests/src/main/kotlin/org/ostelco/at/jersey/Tests.kt @@ -738,9 +738,8 @@ class PurchaseTest { assert(Instant.now().toEpochMilli() - purchaseRecords.last().timestamp < 10_000) { "Missing Purchase Record" } assertEquals(expectedProducts().first(), purchaseRecords.last().product, "Incorrect 'Product' in purchase record") - val encodedEmail = URLEncoder.encode(email, StandardCharsets.UTF_8) val refundedProduct = put { - path = "/support/refund/$encodedEmail" + path = "/support/refund/$customerId" this.email = email queryParams = mapOf( "purchaseRecordId" to purchaseRecords.last().id, @@ -1257,9 +1256,8 @@ class JumioKycTest { } assertEquals("APPROVED", scanInformation.status, message = "Wrong status") - val encodedEmail = URLEncoder.encode(email, StandardCharsets.UTF_8) val scanInformationList = get> { - path = "/support/profiles/$encodedEmail/scans" + path = "/support/profiles/$customerId/scans" this.email = email } assertEquals(1, scanInformationList.size, message = "More scans than expected") @@ -1363,9 +1361,8 @@ class JumioKycTest { expected = mapOf(KycType.JUMIO.name to KycStatus.APPROVED), actual = noRegionDetails.kycStatusMap) - val encodedEmail = URLEncoder.encode(email, StandardCharsets.UTF_8) val scanInformationList = get> { - path = "/support/profiles/$encodedEmail/scans" + path = "/support/profiles/$customerId/scans" this.email = email } assertEquals(2, scanInformationList.size, message = "More scans than expected") From 195007ebf4a1d0320dc1ff518a0fa8944d425492 Mon Sep 17 00:00:00 2001 From: Prasanth Ullattil Date: Wed, 16 Oct 2019 21:48:17 +0200 Subject: [PATCH 105/173] Cleanup swaggerspec for houston API --- .../support/resources/HoustonResources.kt | 48 +-- prime/infra/dev/prime-houston-api.yaml | 323 +++-------------- prime/infra/prod/prime-houston-api.yaml | 325 +++--------------- 3 files changed, 115 insertions(+), 581 deletions(-) diff --git a/customer-support-endpoint/src/main/kotlin/org/ostelco/prime/support/resources/HoustonResources.kt b/customer-support-endpoint/src/main/kotlin/org/ostelco/prime/support/resources/HoustonResources.kt index 2f4e53d7d..880cdddd5 100644 --- a/customer-support-endpoint/src/main/kotlin/org/ostelco/prime/support/resources/HoustonResources.kt +++ b/customer-support-endpoint/src/main/kotlin/org/ostelco/prime/support/resources/HoustonResources.kt @@ -55,10 +55,10 @@ class ProfilesResource { @GET @Path("{query}") @Produces(MediaType.APPLICATION_JSON) - fun getProfile(@Auth token: AccessTokenPrincipal?, - @NotNull - @PathParam("query") - query: String): Response = + fun getCustomerList(@Auth token: AccessTokenPrincipal?, + @NotNull + @PathParam("query") + query: String): Response = if (token == null) { Response.status(Response.Status.UNAUTHORIZED) } else { @@ -68,7 +68,7 @@ class ProfilesResource { .responseBuilder() } else { logger.info("${token.name} Accessing profile for email:$query") - getProfileList(contactEmail = query) + getCustomerList(contactEmail = query) .responseBuilder() } }.build() @@ -124,7 +124,7 @@ class ProfilesResource { } // TODO: Reuse the one from SubscriberDAO - private fun getProfileList(contactEmail: String): Either> { + private fun getCustomerList(contactEmail: String): Either> { return try { storage.getIdentitiesForContactEmail(contactEmail = contactEmail).mapLeft { NotFoundError("Failed to fetch profile.", ApiErrorCode.FAILED_TO_FETCH_CUSTOMER, it) @@ -234,10 +234,10 @@ class PurchaseResource { @GET @Path("{id}") @Produces(MediaType.APPLICATION_JSON) - fun getPurchaseHistoryByEmail(@Auth token: AccessTokenPrincipal?, - @NotNull - @PathParam("id") - id: String): Response = + fun getPurchaseHistoryById(@Auth token: AccessTokenPrincipal?, + @NotNull + @PathParam("id") + id: String): Response = if (token == null) { Response.status(Response.Status.UNAUTHORIZED) } else { @@ -374,16 +374,16 @@ class NotifyResource { @PUT @Path("{id}") @Produces(MediaType.APPLICATION_JSON) - fun sendNotificationByEmail(@Auth token: AccessTokenPrincipal?, - @NotNull - @PathParam("id") - id: String, - @NotNull - @QueryParam("title") - title: String, - @NotNull - @QueryParam("message") - message: String): Response = + fun sendNotificationById(@Auth token: AccessTokenPrincipal?, + @NotNull + @PathParam("id") + id: String, + @NotNull + @QueryParam("title") + title: String, + @NotNull + @QueryParam("message") + message: String): Response = if (token == null) { Response.status(Response.Status.UNAUTHORIZED) } else { @@ -409,10 +409,10 @@ class AuditLogResource { @GET @Path("{id}") @Produces(MediaType.APPLICATION_JSON) - fun query(@Auth token: AccessTokenPrincipal?, - @NotNull - @PathParam("id") - id: String): Response = + fun queryAuditLogs(@Auth token: AccessTokenPrincipal?, + @NotNull + @PathParam("id") + id: String): Response = if (token == null) { Response.status(Response.Status.UNAUTHORIZED) } else { diff --git a/prime/infra/dev/prime-houston-api.yaml b/prime/infra/dev/prime-houston-api.yaml index e1e4c6660..e815a2d45 100644 --- a/prime/infra/dev/prime-houston-api.yaml +++ b/prime/infra/dev/prime-houston-api.yaml @@ -2,7 +2,7 @@ swagger: "2.0" info: title: "Houston Admin API" description: "The APIs for the Houston Admin Client." - version: "1.0.0" + version: "2.0.0" host: "houston-api.dev.oya.world" x-google-endpoints: - name: "houston-api.dev.oya.world" @@ -10,28 +10,28 @@ x-google-endpoints: schemes: - "https" paths: - "/support/profiles/{id}": + "/support/profiles/{query}": get: - description: "Get profile for the given email-id or msisdn (url encoded)." + description: "Get list of customers for the given email-id or msisdn (url encoded)." produces: - application/json - operationId: "getCustomer" + operationId: "getCustomerList" responses: 200: description: "Get the profile for this user." schema: - $ref: '#/definitions/Profile' + $ref: '#/definitions/CustomerList' 404: description: "Profile not found." security: - auth0_jwt: [] parameters: - - name: id + - name: query in: path description: "The id of the user (msisdn or email)" required: true type: string - "/support/profiles/{email}/subscriptions": + "/support/profiles/{id}/subscriptions": get: description: "Get subscription (msisdn) for the user." produces: @@ -47,12 +47,12 @@ paths: security: - auth0_jwt: [] parameters: - - name: email + - name: id in: path - description: "The email of the user" + description: "The customerId of the user" required: true type: string - "/support/profiles/{email}/scans": + "/support/profiles/{id}/scans": get: description: "Get eKYC scan information for the user." produces: @@ -68,87 +68,17 @@ paths: security: - auth0_jwt: [] parameters: - - name: email + - name: id in: path - description: "The email of the user" + description: "The customerId of the user" required: true type: string - "/support/profiles/{email}/plans": - get: - description: "Get all plans subscribed to by a customer" - produces: - - application/json - operationId: "getPlans" - security: - - auth0_jwt: [] - responses: - 200: - description: "Plans subscribed to" - schema: - $ref: '#/definitions/PlanList' - 404: - description: "No plans found" - parameters: - - name: email - in: path - description: "The email of the customer" - required: true - type: string - "/support/profiles/{email}/plans/{planId}": - post: - description: "Subscribe a customer to a plan" - produces: - - application/json - - text/plain - operationId: "attachPlan" - responses: - 201: - description: "The subscription was created successfully" - 400: - description: "Failed to create subscription" - security: - - auth0_jwt: [] - parameters: - - name: email - in: path - description: "The email of the customer" - required: true - type: string - - name: planId - in: path - description: "The name of the plan to subscribe to" - required: true - type: string - delete: - description: "Remove a customer from a plan" - produces: - - application/json - - text/plain - operationId: "detachPlan" - responses: - 200: - description: "The subscription was removed successfully" - 400: - description: "Failed to remove subscription" - security: - - auth0_jwt: [] - parameters: - - name: email - in: path - description: "The email of the customer" - required: true - type: string - - name: planId - in: path - description: "The name of the plan to remove the subscription for" - required: true - type: string - "/support/bundles/{email}": + "/support/bundles/{id}": get: description: "Get bundles (balance) for the user (identified by email)." produces: - application/json - operationId: "getBundlesByEmail" + operationId: "getBundlesById" responses: 200: description: "Get bundles for this user." @@ -159,18 +89,18 @@ paths: security: - auth0_jwt: [] parameters: - - name: email + - name: id in: path - description: "The email of the user" + description: "The customerId of the user" required: true type: string - "/support/purchases/{email}": + "/support/purchases/{id}": get: description: "Get list of all purchases." produces: - application/json - text/plain - operationId: "getPurchaseHistoryByEmail" + operationId: "getPurchaseHistoryById" responses: 200: description: "List of Purchase Records." @@ -185,18 +115,18 @@ paths: security: - auth0_jwt: [] parameters: - - name: email + - name: id in: path - description: "The email of the user" + description: "The customerId of the user" required: true type: string - "/support/refund/{email}": + "/support/refund/{id}": put: description: "Full refund of a purchase." produces: - application/json - text/plain - operationId: "refundPurchaseByEmail" + operationId: "refundPurchaseById" responses: 200: description: "Purchase is refunded." @@ -211,9 +141,9 @@ paths: security: - auth0_jwt: [] parameters: - - name: email + - name: id in: path - description: "The email of the user" + description: "The customerId of the user" required: true type: string - name: purchaseRecordId @@ -226,13 +156,13 @@ paths: description: "The reason for refund" required: true type: string - "/support/notify/{email}": + "/support/notify/{id}": put: description: "Send notification to a customer." produces: - application/json - text/plain - operationId: "sendNotificationByEmail" + operationId: "sendNotificationById" responses: 200: description: "Sent notification." @@ -243,9 +173,9 @@ paths: security: - auth0_jwt: [] parameters: - - name: email + - name: id in: path - description: "The email of the user" + description: "The customerId of the user" required: true type: string - name: title @@ -258,99 +188,12 @@ paths: description: "The notification message" required: true type: string - "/support/plans": - get: - description: "Get plan details" - produces: - - application/json - - text/plain - operationId: "getPlan" - responses: - 200: - description: "Plan details is returned" - schema: - $ref: '#/definitions/Plan' - 404: - description: "No such plan" - parameters: - - name: planId - in: path - description: "Name of plan to get" - required: true - type: string - security: - - auth0_jwt: [] - post: - description: "Create a plan" - produces: - - application/json - - text/plain - operationId: "createPlan" - responses: - 201: - description: "Successfully purchased the plan." - schema: - $ref: '#/definitions/Plan' - 400: - description: "Failed to create the plan" - parameters: - - name: createPlanRequest - in: body - description: Plan details - schema: - $ref: '#/definitions/CreatePlanRequest' - security: - - auth0_jwt: [] - delete: - description: "Removes a plan" - produces: - - application/json - - text/plain - operationId: "deletePlan" - responses: - 200: - description: "Plan is removed" - schema: - $ref: '#/definitions/Plan' - 400: - description: "Failed to remove plan" - 404: - description: "No such plan" - parameters: - - name: planId - in: path - description: "The name of the plan to remove" - required: true - type: string - security: - - auth0_jwt: [] - "/support/profiles/{email}/state": - get: - description: "Get state of the user." - produces: - - application/json - operationId: "getCustomerState" - responses: - 200: - description: "Successfully retrieved the state." - schema: - $ref: '#/definitions/SubscriberState' - 404: - description: "No state information available for this user." - security: - - auth0_jwt: [] - parameters: - - name: email - in: path - description: "The email of the user" - required: true - type: string - "/support/context/{email}": + "/support/context/{id}": get: description: "Get context which is customer and region details." produces: - application/json - operationId: "getContext" + operationId: "getContextById" responses: 200: description: "Get the customer context." @@ -361,18 +204,18 @@ paths: security: - auth0_jwt: [] parameters: - - name: email + - name: id in: path - description: "The email of the user" + description: "The customerId of the user" required: true type: string - "/support/auditLog/{email}": + "/support/auditLog/{id}": get: description: "Get list of all audit logs." produces: - application/json - text/plain - operationId: "getAuditLogsByEmail" + operationId: "queryAuditLogs" responses: 200: description: "List of AuditLog records." @@ -383,12 +226,12 @@ paths: security: - auth0_jwt: [] parameters: - - name: email + - name: id in: path - description: "The email of the user" + description: "The customerId of the user" required: true type: string - "/support/customer/{email}": + "/support/customer/{id}": delete: description: "Remove the customer from the backend." produces: @@ -402,9 +245,9 @@ paths: security: - auth0_jwt: [] parameters: - - name: email + - name: id in: path - description: "The contact email of the customer" + description: "The customerId of the customer" required: true type: string @@ -416,6 +259,10 @@ definitions: $ref: '#/definitions/Customer' regions: $ref: '#/definitions/RegionDetailsList' + CustomerList: + type: array + items: + $ref: '#/definitions/Customer' Customer: type: object properties: @@ -431,8 +278,8 @@ definitions: referralId: type: string required: - - name - - email + - id + - contactEmail RegionDetailsList: type: array items: @@ -467,12 +314,6 @@ definitions: KycStatus: type: string enum: [ PENDING, REJECTED, APPROVED ] - MyInfoConfig: - type: object - properties: - url: - type: string - description: "URL for MyInfo authorise" Region: type: object properties: @@ -504,26 +345,6 @@ definitions: - iccId - activationCode - status - Profile: - type: object - properties: - name: - type: string - address: - type: string - postCode: - type: string - city: - type: string - country: - type: string - email: - type: string - format: email - referralId: - type: string - required: - - email SubscriptionList: type: array items: @@ -623,60 +444,6 @@ definitions: required: - amount - currency - Plan: - type: object - properties: - id: - description: "An unique id representing the plan" - type: string - stripePlanId: - type: string - stripeProductId: - type: string - interval: - description: "The recurring period for the plan" - type: string - enum: [ day, week, month, year ] - intervalCount: - description: "Number of intervals in a period" - type: integer - default: 1 - minimum: 1 - required: - - id - PlanList: - type: array - items: - $ref: '#/definitions/Plan' - CreatePlanRequest: - type: object - properties: - plan: - description: Plan to be created. - $ref: '#/definitions/Plan' - stripeProductName: - description: Name of product in Stripe for this plan. - type: string - productPlan: - description: Product associated with this plan. - $ref: '#/definitions/Product' - SubscriberState: - type: object - properties: - id: - description: "User Id" - type: string - status: - description: "Current status of the customer" - type: string - modifiedTimestamp: - description: "Last modified time for the status (Unix timestamp)" - type: integer - format: int64 - required: - - id - - status - - modifiedTimestamp ScanInformationList: type: array items: diff --git a/prime/infra/prod/prime-houston-api.yaml b/prime/infra/prod/prime-houston-api.yaml index ad3a3c6bc..4efa2b006 100644 --- a/prime/infra/prod/prime-houston-api.yaml +++ b/prime/infra/prod/prime-houston-api.yaml @@ -2,36 +2,36 @@ swagger: "2.0" info: title: "Houston Admin API" description: "The APIs for the Houston Admin Client." - version: "1.0.0" -host: "houston-api.oya.world" + version: "2.0.0" +host: "houston-api.dev.oya.world" x-google-endpoints: - name: "houston-api.oya.world" allowCors: true schemes: - "https" paths: - "/support/profiles/{id}": + "/support/profiles/{query}": get: - description: "Get profile for the given email-id or msisdn (url encoded)." + description: "Get list of customers for the given email-id or msisdn (url encoded)." produces: - application/json - operationId: "getCustomer" + operationId: "getCustomerList" responses: 200: description: "Get the profile for this user." schema: - $ref: '#/definitions/Profile' + $ref: '#/definitions/CustomerList' 404: description: "Profile not found." security: - auth0_jwt: [] parameters: - - name: id + - name: query in: path description: "The id of the user (msisdn or email)" required: true type: string - "/support/profiles/{email}/subscriptions": + "/support/profiles/{id}/subscriptions": get: description: "Get subscription (msisdn) for the user." produces: @@ -47,12 +47,12 @@ paths: security: - auth0_jwt: [] parameters: - - name: email + - name: id in: path - description: "The email of the user" + description: "The customerId of the user" required: true type: string - "/support/profiles/{email}/scans": + "/support/profiles/{id}/scans": get: description: "Get eKYC scan information for the user." produces: @@ -68,87 +68,17 @@ paths: security: - auth0_jwt: [] parameters: - - name: email + - name: id in: path - description: "The email of the user" + description: "The customerId of the user" required: true type: string - "/support/profiles/{email}/plans": - get: - description: "Get all plans subscribed to by a customer" - produces: - - application/json - operationId: "getPlans" - security: - - auth0_jwt: [] - responses: - 200: - description: "Plans subscribed to" - schema: - $ref: '#/definitions/PlanList' - 404: - description: "No plans found" - parameters: - - name: email - in: path - description: "The email of the customer" - required: true - type: string - "/support/profiles/{email}/plans/{planId}": - post: - description: "Subscribe a customer to a plan" - produces: - - application/json - - text/plain - operationId: "attachPlan" - responses: - 201: - description: "The subscription was created successfully" - 400: - description: "Failed to create subscription" - security: - - auth0_jwt: [] - parameters: - - name: email - in: path - description: "The email of the customer" - required: true - type: string - - name: planId - in: path - description: "The name of the plan to subscribe to" - required: true - type: string - delete: - description: "Remove a customer from a plan" - produces: - - application/json - - text/plain - operationId: "detachPlan" - responses: - 200: - description: "The subscription was removed successfully" - 400: - description: "Failed to remove subscription" - security: - - auth0_jwt: [] - parameters: - - name: email - in: path - description: "The email of the customer" - required: true - type: string - - name: planId - in: path - description: "The name of the plan to remove the subscription for" - required: true - type: string - "/support/bundles/{email}": + "/support/bundles/{id}": get: description: "Get bundles (balance) for the user (identified by email)." produces: - application/json - operationId: "getBundlesByEmail" + operationId: "getBundlesById" responses: 200: description: "Get bundles for this user." @@ -159,18 +89,18 @@ paths: security: - auth0_jwt: [] parameters: - - name: email + - name: id in: path - description: "The email of the user" + description: "The customerId of the user" required: true type: string - "/support/purchases/{email}": + "/support/purchases/{id}": get: description: "Get list of all purchases." produces: - application/json - text/plain - operationId: "getPurchaseHistoryByEmail" + operationId: "getPurchaseHistoryById" responses: 200: description: "List of Purchase Records." @@ -185,18 +115,18 @@ paths: security: - auth0_jwt: [] parameters: - - name: email + - name: id in: path - description: "The email of the user" + description: "The customerId of the user" required: true type: string - "/support/refund/{email}": + "/support/refund/{id}": put: description: "Full refund of a purchase." produces: - application/json - text/plain - operationId: "refundPurchaseByEmail" + operationId: "refundPurchaseById" responses: 200: description: "Purchase is refunded." @@ -211,9 +141,9 @@ paths: security: - auth0_jwt: [] parameters: - - name: email + - name: id in: path - description: "The email of the user" + description: "The customerId of the user" required: true type: string - name: purchaseRecordId @@ -226,13 +156,13 @@ paths: description: "The reason for refund" required: true type: string - "/support/notify/{email}": + "/support/notify/{id}": put: description: "Send notification to a customer." produces: - application/json - text/plain - operationId: "sendNotificationByEmail" + operationId: "sendNotificationById" responses: 200: description: "Sent notification." @@ -243,9 +173,9 @@ paths: security: - auth0_jwt: [] parameters: - - name: email + - name: id in: path - description: "The email of the user" + description: "The customerId of the user" required: true type: string - name: title @@ -258,99 +188,12 @@ paths: description: "The notification message" required: true type: string - "/support/plans": - get: - description: "Get plan details" - produces: - - application/json - - text/plain - operationId: "getPlan" - responses: - 200: - description: "Plan details is returned" - schema: - $ref: '#/definitions/Plan' - 404: - description: "No such plan" - parameters: - - name: planId - in: path - description: "Name of plan to get" - required: true - type: string - security: - - auth0_jwt: [] - post: - description: "Create a plan" - produces: - - application/json - - text/plain - operationId: "createPlan" - responses: - 201: - description: "Successfully purchased the plan." - schema: - $ref: '#/definitions/Plan' - 400: - description: "Failed to create the plan" - parameters: - - name: createPlanRequest - in: body - description: Plan details - schema: - $ref: '#/definitions/CreatePlanRequest' - security: - - auth0_jwt: [] - delete: - description: "Removes a plan" - produces: - - application/json - - text/plain - operationId: "deletePlan" - responses: - 200: - description: "Plan is removed" - schema: - $ref: '#/definitions/Plan' - 400: - description: "Failed to remove plan" - 404: - description: "No such plan" - parameters: - - name: planId - in: path - description: "The name of the plan to remove" - required: true - type: string - security: - - auth0_jwt: [] - "/support/profiles/{email}/state": - get: - description: "Get state of the user." - produces: - - application/json - operationId: "getCustomerState" - responses: - 200: - description: "Successfully retrieved the state." - schema: - $ref: '#/definitions/SubscriberState' - 404: - description: "No state information available for this user." - security: - - auth0_jwt: [] - parameters: - - name: email - in: path - description: "The email of the user" - required: true - type: string - "/support/context/{email}": + "/support/context/{id}": get: description: "Get context which is customer and region details." produces: - application/json - operationId: "getContext" + operationId: "getContextById" responses: 200: description: "Get the customer context." @@ -361,18 +204,18 @@ paths: security: - auth0_jwt: [] parameters: - - name: email + - name: id in: path - description: "The email of the user" + description: "The customerId of the user" required: true type: string - "/support/auditLog/{email}": + "/support/auditLog/{id}": get: description: "Get list of all audit logs." produces: - application/json - text/plain - operationId: "getAuditLogsByEmail" + operationId: "queryAuditLogs" responses: 200: description: "List of AuditLog records." @@ -383,12 +226,12 @@ paths: security: - auth0_jwt: [] parameters: - - name: email + - name: id in: path - description: "The email of the user" + description: "The customerId of the user" required: true type: string - "/support/customer/{email}": + "/support/customer/{id}": delete: description: "Remove the customer from the backend." produces: @@ -402,9 +245,9 @@ paths: security: - auth0_jwt: [] parameters: - - name: email + - name: id in: path - description: "The contact email of the customer" + description: "The customerId of the customer" required: true type: string @@ -416,6 +259,10 @@ definitions: $ref: '#/definitions/Customer' regions: $ref: '#/definitions/RegionDetailsList' + CustomerList: + type: array + items: + $ref: '#/definitions/Customer' Customer: type: object properties: @@ -431,8 +278,8 @@ definitions: referralId: type: string required: - - name - - email + - id + - contactEmail RegionDetailsList: type: array items: @@ -467,12 +314,6 @@ definitions: KycStatus: type: string enum: [ PENDING, REJECTED, APPROVED ] - MyInfoConfig: - type: object - properties: - url: - type: string - description: "URL for MyInfo authorise" Region: type: object properties: @@ -504,26 +345,6 @@ definitions: - iccId - activationCode - status - Profile: - type: object - properties: - name: - type: string - address: - type: string - postCode: - type: string - city: - type: string - country: - type: string - email: - type: string - format: email - referralId: - type: string - required: - - email SubscriptionList: type: array items: @@ -623,60 +444,6 @@ definitions: required: - amount - currency - Plan: - type: object - properties: - id: - description: "An unique id representing the plan" - type: string - stripePlanId: - type: string - stripeProductId: - type: string - interval: - description: "The recurring period for the plan" - type: string - enum: [ day, week, month, year ] - intervalCount: - description: "Number of intervals in a period" - type: integer - default: 1 - minimum: 1 - required: - - id - PlanList: - type: array - items: - $ref: '#/definitions/Plan' - CreatePlanRequest: - type: object - properties: - plan: - description: Plan to be created. - $ref: '#/definitions/Plan' - stripeProductName: - description: Name of product in Stripe for this plan. - type: string - productPlan: - description: Product associated with this plan. - $ref: '#/definitions/Product' - SubscriberState: - type: object - properties: - id: - description: "User Id" - type: string - status: - description: "Current status of the customer" - type: string - modifiedTimestamp: - description: "Last modified time for the status (Unix timestamp)" - type: integer - format: int64 - required: - - id - - status - - modifiedTimestamp ScanInformationList: type: array items: From beac8d591966bb27aafffdd3ad66aa56a7d81450 Mon Sep 17 00:00:00 2001 From: Prasanth Ullattil Date: Wed, 16 Oct 2019 22:27:02 +0200 Subject: [PATCH 106/173] Remove prime-direct from sources --- houston/src/helpers/api.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/houston/src/helpers/api.js b/houston/src/helpers/api.js index e3c6351f9..d6fdfba23 100644 --- a/houston/src/helpers/api.js +++ b/houston/src/helpers/api.js @@ -25,7 +25,7 @@ const apiCaller = async (endpoint, method, body, allowEmptyResponse, params = [] } else if (typeof params === 'string') { fullUrl += params; } - + //console.log('API URL:', fullUrl); if (authHeaderResolver === null) { console.log("apiCaller: authHeaderResolver not set"); @@ -41,7 +41,7 @@ const apiCaller = async (endpoint, method, body, allowEmptyResponse, params = [] Accept: 'application/json', Authorization: auth.header, //TODO: remove this when we are ready to deploy in all versions of prime - "x-mode": "prime-direct" + //"x-mode": "prime-direct" } }; if (body) { From 3a19865a8cb98fa37e72906a9d1beb069f5b58cd Mon Sep 17 00:00:00 2001 From: Prasanth Ullattil Date: Thu, 17 Oct 2019 09:49:31 +0200 Subject: [PATCH 107/173] fix warning --- houston/src/actions/notifiy.actions.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/houston/src/actions/notifiy.actions.js b/houston/src/actions/notifiy.actions.js index e8e78153a..a07d03fba 100644 --- a/houston/src/actions/notifiy.actions.js +++ b/houston/src/actions/notifiy.actions.js @@ -3,8 +3,6 @@ import { alertActions } from './alert.actions'; import { CALL_API } from '../helpers/api'; import _ from 'lodash'; -import { encodeEmail } from '../helpers/utils'; - const NOTIFY_REQUEST = 'NOTIFY_REQUEST'; const NOTIFY_SUCCESS = 'NOTIFY_SUCCESS'; const NOTIFY_FAILURE = 'NOTIFY_FAILURE'; From fca0dfbaf3b0ee585cf53fca273cb177b88ee59c Mon Sep 17 00:00:00 2001 From: Prasanth Ullattil Date: Thu, 17 Oct 2019 10:31:30 +0200 Subject: [PATCH 108/173] Search for name & email --- .../support/resources/HoustonResources.kt | 19 ++++++++++--------- houston/.gitignore | 1 + houston/src/helpers/api.js | 2 +- .../ostelco/prime/storage/graph/Neo4jStore.kt | 9 +++++---- .../org/ostelco/prime/storage/Variants.kt | 2 +- 5 files changed, 18 insertions(+), 15 deletions(-) diff --git a/customer-support-endpoint/src/main/kotlin/org/ostelco/prime/support/resources/HoustonResources.kt b/customer-support-endpoint/src/main/kotlin/org/ostelco/prime/support/resources/HoustonResources.kt index 880cdddd5..2886efec8 100644 --- a/customer-support-endpoint/src/main/kotlin/org/ostelco/prime/support/resources/HoustonResources.kt +++ b/customer-support-endpoint/src/main/kotlin/org/ostelco/prime/support/resources/HoustonResources.kt @@ -62,13 +62,14 @@ class ProfilesResource { if (token == null) { Response.status(Response.Status.UNAUTHORIZED) } else { - if (!isEmail(query)) { - logger.info("${token.name} Accessing profile for msisdn:$query") - getProfileListForMsisdn(query) + val trimmedQuery = query.trim() + if (trimmedQuery.toIntOrNull() != null) { + logger.info("${token.name} Accessing profile for msisdn: $query") + getProfileListForMsisdn(trimmedQuery) .responseBuilder() } else { - logger.info("${token.name} Accessing profile for email:$query") - getCustomerList(contactEmail = query) + logger.info("${token.name} Accessing profile for nickname/contactEmail: $query") + getCustomerList(queryString = trimmedQuery) .responseBuilder() } }.build() @@ -124,15 +125,15 @@ class ProfilesResource { } // TODO: Reuse the one from SubscriberDAO - private fun getCustomerList(contactEmail: String): Either> { + private fun getCustomerList(queryString: String): Either> { return try { - storage.getIdentitiesForContactEmail(contactEmail = contactEmail).mapLeft { + storage.getIdentitiesFor(queryString = queryString).mapLeft { NotFoundError("Failed to fetch profile.", ApiErrorCode.FAILED_TO_FETCH_CUSTOMER, it) }.flatMap { identityList -> val customerList = identityList.mapNotNull { identity: Identity -> storage.getCustomer(identity).fold( { - logger.error("Error fetching customer for $identity, $it") + logger.error("Error fetching customer for $queryString, $it") null }, { it }) @@ -140,7 +141,7 @@ class ProfilesResource { Either.right(customerList) } } catch (e: Exception) { - logger.error("Failed to fetch profile for customer with contactEmail - $contactEmail", e) + logger.error("Failed to fetch profile for customer with nickname/contactEmail - $queryString", e) Either.left(NotFoundError("Failed to fetch profile", ApiErrorCode.FAILED_TO_FETCH_CUSTOMER)) } } diff --git a/houston/.gitignore b/houston/.gitignore index c6e369dd4..498c6f439 100644 --- a/houston/.gitignore +++ b/houston/.gitignore @@ -10,6 +10,7 @@ /build .firebase/*.cache +.vscode/ # misc .DS_Store diff --git a/houston/src/helpers/api.js b/houston/src/helpers/api.js index d6fdfba23..17b3a6f5c 100644 --- a/houston/src/helpers/api.js +++ b/houston/src/helpers/api.js @@ -41,7 +41,7 @@ const apiCaller = async (endpoint, method, body, allowEmptyResponse, params = [] Accept: 'application/json', Authorization: auth.header, //TODO: remove this when we are ready to deploy in all versions of prime - //"x-mode": "prime-direct" + "x-mode": "prime-direct" } }; if (body) { diff --git a/neo4j-store/src/main/kotlin/org/ostelco/prime/storage/graph/Neo4jStore.kt b/neo4j-store/src/main/kotlin/org/ostelco/prime/storage/graph/Neo4jStore.kt index a763809aa..4abb8af3e 100644 --- a/neo4j-store/src/main/kotlin/org/ostelco/prime/storage/graph/Neo4jStore.kt +++ b/neo4j-store/src/main/kotlin/org/ostelco/prime/storage/graph/Neo4jStore.kt @@ -2012,10 +2012,11 @@ object Neo4jStoreSingleton : GraphStore { } } - override fun getIdentitiesForContactEmail(contactEmail: String): Either> = readTransaction { + override fun getIdentitiesFor(queryString: String): Either> = readTransaction { read(""" - MATCH (:${customerEntity.name} { contactEmail:'$contactEmail' })<-[r:${identifiesRelation.name}]-(identity:${identityEntity.name}) - RETURN identity, r.provider as provider + MATCH (c:${customerEntity.name})<-[r:${identifiesRelation.name}]-(identity:${identityEntity.name}) + WHERE c.contactEmail contains $queryString or c.nickname contains $queryString + RETURN c, identity, r.provider as provider """.trimIndent(), transaction) { if (it.hasNext()) { @@ -2027,7 +2028,7 @@ object Neo4jStoreSingleton : GraphStore { } Either.right(identityList) } else { - Either.left(NotFoundError(type = customerEntity.name, id = contactEmail)) + Either.left(NotFoundError(type = customerEntity.name, id = queryString)) } } } diff --git a/prime-modules/src/main/kotlin/org/ostelco/prime/storage/Variants.kt b/prime-modules/src/main/kotlin/org/ostelco/prime/storage/Variants.kt index d75971791..8a6c231fd 100644 --- a/prime-modules/src/main/kotlin/org/ostelco/prime/storage/Variants.kt +++ b/prime-modules/src/main/kotlin/org/ostelco/prime/storage/Variants.kt @@ -198,7 +198,7 @@ interface AdminGraphStore { fun getCustomerForMsisdn(msisdn: String): Either fun getIdentityForCustomerId(id: String): Either - fun getIdentitiesForContactEmail(contactEmail: String): Either> + fun getIdentitiesFor(queryString: String): Either> /** * Link Customer to MSISDN From 8a06b140462a0c1225f80ae1240fb992669a3677 Mon Sep 17 00:00:00 2001 From: Prasanth Ullattil Date: Thu, 17 Oct 2019 10:56:09 +0200 Subject: [PATCH 109/173] Fix tests --- .../main/kotlin/org/ostelco/prime/storage/graph/Neo4jStore.kt | 2 +- .../kotlin/org/ostelco/prime/storage/graph/Neo4jStoreTest.kt | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/neo4j-store/src/main/kotlin/org/ostelco/prime/storage/graph/Neo4jStore.kt b/neo4j-store/src/main/kotlin/org/ostelco/prime/storage/graph/Neo4jStore.kt index 4abb8af3e..b7fc0808f 100644 --- a/neo4j-store/src/main/kotlin/org/ostelco/prime/storage/graph/Neo4jStore.kt +++ b/neo4j-store/src/main/kotlin/org/ostelco/prime/storage/graph/Neo4jStore.kt @@ -2015,7 +2015,7 @@ object Neo4jStoreSingleton : GraphStore { override fun getIdentitiesFor(queryString: String): Either> = readTransaction { read(""" MATCH (c:${customerEntity.name})<-[r:${identifiesRelation.name}]-(identity:${identityEntity.name}) - WHERE c.contactEmail contains $queryString or c.nickname contains $queryString + WHERE c.contactEmail contains '$queryString' or c.nickname contains '$queryString' RETURN c, identity, r.provider as provider """.trimIndent(), transaction) { diff --git a/neo4j-store/src/test/kotlin/org/ostelco/prime/storage/graph/Neo4jStoreTest.kt b/neo4j-store/src/test/kotlin/org/ostelco/prime/storage/graph/Neo4jStoreTest.kt index 305d3cf74..bf0bfd624 100644 --- a/neo4j-store/src/test/kotlin/org/ostelco/prime/storage/graph/Neo4jStoreTest.kt +++ b/neo4j-store/src/test/kotlin/org/ostelco/prime/storage/graph/Neo4jStoreTest.kt @@ -150,7 +150,7 @@ class Neo4jStoreTest { referredBy = null) .mapLeft { fail(it.message) } - Neo4jStoreSingleton.getIdentitiesForContactEmail(contactEmail = EMAIL).bimap( + Neo4jStoreSingleton.getIdentitiesFor(queryString = EMAIL).bimap( { fail(it.message) }, { list-> val identity: Identity = list.first() @@ -168,7 +168,7 @@ class Neo4jStoreTest { referredBy = null) .mapLeft { fail(it.message) } - Neo4jStoreSingleton.getIdentitiesForContactEmail(contactEmail = EMAIL).bimap( + Neo4jStoreSingleton.getIdentitiesFor(queryString = EMAIL).bimap( { fail(it.message) }, { list -> val identity: Identity = list.first() From e0803a4e767aeac1ed32c62e0fbbd0bfc164475f Mon Sep 17 00:00:00 2001 From: Prasanth Ullattil Date: Thu, 17 Oct 2019 11:17:46 +0200 Subject: [PATCH 110/173] Add actions for selecting and resetting a customer --- houston/src/actions/cutomer.actions.js | 31 +++++++++++++++++++++++ houston/src/actions/subscriber.actions.js | 4 +++ houston/src/reducers/index.js | 2 ++ 3 files changed, 37 insertions(+) create mode 100644 houston/src/actions/cutomer.actions.js diff --git a/houston/src/actions/cutomer.actions.js b/houston/src/actions/cutomer.actions.js new file mode 100644 index 000000000..a80223084 --- /dev/null +++ b/houston/src/actions/cutomer.actions.js @@ -0,0 +1,31 @@ +import { createActions, handleActions } from 'redux-actions'; + +const SELECT_CUSTOMER = 'SELECT_CUSTOMER'; +const CLEAR_CUSTOMER = 'CLEAR_CUSTOMER'; + +const defaultState = {}; + +const actions = createActions( + SELECT_CUSTOMER, + CLEAR_CUSTOMER); + +const { + selectCustomer, + clearCustomer, +} = actions; + +export const customerActions = { ...actions }; + + const reducer = handleActions( + { + [selectCustomer]: (state, { payload }) => { + return { ...payload }; + }, + [clearCustomer]: () => { + return defaultState; + }, + }, + defaultState +); + +export default reducer; diff --git a/houston/src/actions/subscriber.actions.js b/houston/src/actions/subscriber.actions.js index d8c36f965..180296329 100644 --- a/houston/src/actions/subscriber.actions.js +++ b/houston/src/actions/subscriber.actions.js @@ -3,6 +3,7 @@ import { createActions } from 'redux-actions'; import { CALL_API } from '../helpers/api'; import { alertActions } from './alert.actions'; +import { customerActions } from './cutomer.actions'; import { encodeEmail } from '../helpers/utils'; const SUBSCRIBER_BY_EMAIL_REQUEST = 'SUBSCRIBER_BY_EMAIL_REQUEST'; @@ -164,6 +165,7 @@ const deleteUserById = (id) => ({ // TODO: API based implementaion. Reference: https://github.com/reduxjs/redux/issues/1676 const getSubscriberAndBundlesByEmail = (email) => (dispatch, getState) => { + dispatch(customerActions.clearCustomer()); localStorage.setItem('searchedEmail', email) email = encodeEmail(email); @@ -175,8 +177,10 @@ const getSubscriberAndBundlesByEmail = (email) => (dispatch, getState) => { return dispatch(fetchSubscriberById(email)) .then(() => { // Get the id from the fetched user + const subscriber = _.get(getState(), 'subscriber[0]'); const subscriberId = _.get(getState(), 'subscriber[0].id'); if (subscriberId) { + dispatch(customerActions.selectCustomer(subscriber)); dispatch(fetchContextById(subscriberId)).catch(handleError); dispatch(fetchAuditLogsById(subscriberId)).catch(handleError); dispatch(fetchSubscriptionsById(subscriberId)).catch(handleError); diff --git a/houston/src/reducers/index.js b/houston/src/reducers/index.js index 30620b424..76a82208d 100644 --- a/houston/src/reducers/index.js +++ b/houston/src/reducers/index.js @@ -7,6 +7,7 @@ import { notifyConstants } from '../actions/notifiy.actions'; // Reducers. import alert from '../actions/alert.actions'; +import customer from '../actions/cutomer.actions'; import notification from '../actions/notifiy.actions'; import authentication from './auth.reducer'; import { @@ -24,6 +25,7 @@ const appReducer = combineReducers({ alert, notification, subscriber, + customer, context, subscriptions, bundles, From 7b863a217c742c5249985bc32a68489aff2f09cd Mon Sep 17 00:00:00 2001 From: Prasanth Ullattil Date: Thu, 17 Oct 2019 11:18:25 +0200 Subject: [PATCH 111/173] Remove "prime-direct" --- houston/src/helpers/api.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/houston/src/helpers/api.js b/houston/src/helpers/api.js index 17b3a6f5c..d6fdfba23 100644 --- a/houston/src/helpers/api.js +++ b/houston/src/helpers/api.js @@ -41,7 +41,7 @@ const apiCaller = async (endpoint, method, body, allowEmptyResponse, params = [] Accept: 'application/json', Authorization: auth.header, //TODO: remove this when we are ready to deploy in all versions of prime - "x-mode": "prime-direct" + //"x-mode": "prime-direct" } }; if (body) { From a9b803523c6dee407df4e27b89dc8e26b442b9fa Mon Sep 17 00:00:00 2001 From: Prasanth Ullattil Date: Thu, 17 Oct 2019 11:56:06 +0200 Subject: [PATCH 112/173] Whitespace fixes --- .../ostelco/prime/support/resources/HoustonResources.kt | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/customer-support-endpoint/src/main/kotlin/org/ostelco/prime/support/resources/HoustonResources.kt b/customer-support-endpoint/src/main/kotlin/org/ostelco/prime/support/resources/HoustonResources.kt index 2886efec8..e2263a84a 100644 --- a/customer-support-endpoint/src/main/kotlin/org/ostelco/prime/support/resources/HoustonResources.kt +++ b/customer-support-endpoint/src/main/kotlin/org/ostelco/prime/support/resources/HoustonResources.kt @@ -202,8 +202,7 @@ class BundlesResource { Response.status(Response.Status.UNAUTHORIZED) } else { logger.info("${token.name} Accessing bundles for customerId: $id") - getBundles(customerId = id) - .responseBuilder() + getBundles(customerId = id).responseBuilder() }.build() // TODO: Reuse the one from SubscriberDAO @@ -243,8 +242,7 @@ class PurchaseResource { Response.status(Response.Status.UNAUTHORIZED) } else { logger.info("${token.name} Accessing bundles for customerId: $id") - getPurchaseHistory(customerId = id) - .responseBuilder() + getPurchaseHistory(customerId = id).responseBuilder() }.build() // TODO: Reuse the one from SubscriberDAO @@ -447,8 +445,7 @@ class CustomerResource { Response.status(Response.Status.UNAUTHORIZED) } else { logger.info("${token.name} Removing the customer for customerId: $id") - removeCustomer(customerId = id) - .responseBuilder(Response.Status.NO_CONTENT) + removeCustomer(customerId = id).responseBuilder(Response.Status.NO_CONTENT) }.build() // TODO: Reuse the one from SubscriberDAO From 83b03050e1fac3b9b9aa9a53ba749ccc220367b5 Mon Sep 17 00:00:00 2001 From: Prasanth Ullattil Date: Thu, 17 Oct 2019 15:37:29 +0200 Subject: [PATCH 113/173] Show a list of users matching the search criteria --- houston/src/actions/cutomer.actions.js | 1 + houston/src/actions/notifiy.actions.js | 2 +- houston/src/actions/subscriber.actions.js | 47 +++++---- houston/src/components/Search/CustomerList.js | 95 +++++++++++++++++++ houston/src/components/Search/Profile.js | 5 +- houston/src/components/Search/Search.js | 8 +- houston/src/reducers/index.js | 4 +- houston/src/reducers/subscriber.reducer.js | 2 +- 8 files changed, 135 insertions(+), 29 deletions(-) create mode 100644 houston/src/components/Search/CustomerList.js diff --git a/houston/src/actions/cutomer.actions.js b/houston/src/actions/cutomer.actions.js index a80223084..a37c6e03f 100644 --- a/houston/src/actions/cutomer.actions.js +++ b/houston/src/actions/cutomer.actions.js @@ -14,6 +14,7 @@ const { clearCustomer, } = actions; + export const customerActions = { ...actions }; const reducer = handleActions( diff --git a/houston/src/actions/notifiy.actions.js b/houston/src/actions/notifiy.actions.js index a07d03fba..e2369cfd3 100644 --- a/houston/src/actions/notifiy.actions.js +++ b/houston/src/actions/notifiy.actions.js @@ -57,7 +57,7 @@ const sendNotificationToSubscriber = (title, message) => (dispatch, getState) => }; // Get the id from the fetched user - const subscriberId = _.get(getState(), 'subscriber[0].id'); + const subscriberId = _.get(getState(), 'customer.id'); if (subscriberId) { return dispatch(putNotificationById(subscriberId, title, message)) .catch(handleError); diff --git a/houston/src/actions/subscriber.actions.js b/houston/src/actions/subscriber.actions.js index 180296329..dd51761a2 100644 --- a/houston/src/actions/subscriber.actions.js +++ b/houston/src/actions/subscriber.actions.js @@ -164,7 +164,7 @@ const deleteUserById = (id) => ({ }); // TODO: API based implementaion. Reference: https://github.com/reduxjs/redux/issues/1676 -const getSubscriberAndBundlesByEmail = (email) => (dispatch, getState) => { +const getSubscriberList = (email) => (dispatch, getState) => { dispatch(customerActions.clearCustomer()); localStorage.setItem('searchedEmail', email) @@ -176,33 +176,42 @@ const getSubscriberAndBundlesByEmail = (email) => (dispatch, getState) => { return dispatch(fetchSubscriberById(email)) .then(() => { - // Get the id from the fetched user - const subscriber = _.get(getState(), 'subscriber[0]'); - const subscriberId = _.get(getState(), 'subscriber[0].id'); - if (subscriberId) { - dispatch(customerActions.selectCustomer(subscriber)); - dispatch(fetchContextById(subscriberId)).catch(handleError); - dispatch(fetchAuditLogsById(subscriberId)).catch(handleError); - dispatch(fetchSubscriptionsById(subscriberId)).catch(handleError); - return dispatch(fetchBundlesById(subscriberId)) - .then(() => { - return dispatch(fetchPaymentHistoryById(subscriberId)); - }) - .catch(handleError); + const subscribers = _.get(getState(), 'subscribers'); + if (Array.isArray(subscribers) && subscribers.length === 1) { + dispatch(selectCustomer(subscribers[0])); } }) .catch(handleError); }; -const refundPurchase = (purchaseRecordId, reason) => (dispatch, getState) => { +const selectCustomer = (customer) => (dispatch, getState) => { + dispatch(customerActions.selectCustomer(customer)); + const handleError = (error) => { + console.log('Error reported.', error); + dispatch(alertActions.alertError(error)); + }; + const customerId = _.get(getState(), 'customer.id');; + if (customerId) { + dispatch(fetchContextById(customerId)).catch(handleError); + dispatch(fetchAuditLogsById(customerId)).catch(handleError); + dispatch(fetchSubscriptionsById(customerId)).catch(handleError); + return dispatch(fetchBundlesById(customerId)) + .then(() => { + return dispatch(fetchPaymentHistoryById(customerId)); + }) + .catch(handleError); + } +}; + +const refundPurchase = (purchaseRecordId, reason) => (dispatch, getState) => { const handleError = (error) => { console.log('Error reported.', error); dispatch(alertActions.alertError(error)); }; // Get the id from the fetched user - const subscriberId = _.get(getState(), 'subscriber[0].id'); + const subscriberId = _.get(getState(), 'customer.id'); if (subscriberId) { return dispatch(putRefundPurchaseById(subscriberId, purchaseRecordId, reason)) .then(() => { @@ -212,7 +221,6 @@ const refundPurchase = (purchaseRecordId, reason) => (dispatch, getState) => { } }; const deleteUser = () => (dispatch, getState) => { - const handleError = (error) => { console.log('Error reported.', error.message); let message = "Failed to delete user (" +error.message+")" @@ -220,14 +228,15 @@ const deleteUser = () => (dispatch, getState) => { }; // Get the id from the fetched user - const subscriberId = _.get(getState(), 'subscriber[0].id'); + const subscriberId = _.get(getState(), 'customer.id'); if (subscriberId) { return dispatch(deleteUserById(subscriberId)) .catch(handleError); } }; export const subscriberActions = { - getSubscriberAndBundlesByEmail, + getSubscriberList, + selectCustomer, refundPurchase, deleteUser }; diff --git a/houston/src/components/Search/CustomerList.js b/houston/src/components/Search/CustomerList.js new file mode 100644 index 000000000..d2b2dc421 --- /dev/null +++ b/houston/src/components/Search/CustomerList.js @@ -0,0 +1,95 @@ +import React from 'react'; +import { connect } from 'react-redux'; +import PropTypes from 'prop-types'; +import { Col, Row, Card, CardBody, Button } from 'reactstrap'; + +import { subscriberActions } from '../../actions/subscriber.actions'; + +export const CustomerRow = props => { + + function onSelect(e) { + e.preventDefault(); + console.log(`Selecting customer with id ${props.customer.id}`); + props.selectCustomer(props.customer); + } + + return ( +
+ + {'Name:'} + {`${props.customer.nickname}`} + + + {'Email:'} + {`${props.customer.contactEmail}`} + + + {'ID:'} + {`${props.customer.id}`} + +
+ + + + + +
); +} + +CustomerRow.propTypes = { + customer: PropTypes.shape({ + id: PropTypes.string, + nickname: PropTypes.string, + contactEmail: PropTypes.string, + }), + selectCustomer: PropTypes.func.isRequired +}; + +export const CustomerList = props => { + // If customer is set, remove the list. + console.log(JSON.stringify(props)) + if (props.customer.id || !Array.isArray(props.subscribers)) { + return null; + } + let listItems = null; + if (Array.isArray(props.subscribers)) { + listItems = props.subscribers.map((customer, index) => +
+ +
+
+ ); + } + return ( +
+
Found following matching records...
+ + + {listItems} + + +
+ ); +} + +CustomerList.propTypes = { + subscribers: PropTypes.oneOfType([ + PropTypes.object, + PropTypes.array + ]), + customer: PropTypes.object, + selectCustomer: PropTypes.func.isRequired +}; + +function mapStateToProps(state) { + const { subscribers, customer } = state + + return { + subscribers, + customer + }; +} +const mapDispatchToProps = { + selectCustomer: subscriberActions.selectCustomer +} +export default connect(mapStateToProps, mapDispatchToProps)(CustomerList); diff --git a/houston/src/components/Search/Profile.js b/houston/src/components/Search/Profile.js index 71a819faf..039a2d4d4 100644 --- a/houston/src/components/Search/Profile.js +++ b/houston/src/components/Search/Profile.js @@ -2,7 +2,6 @@ import React from 'react'; import { connect } from 'react-redux'; import PropTypes from 'prop-types'; import { Col, Row, Card, CardBody, CardTitle, Button } from 'reactstrap'; -import _ from 'lodash'; import { subscriberActions } from '../../actions/subscriber.actions'; import Subscription from './Subscription'; @@ -98,9 +97,9 @@ Profile.propTypes = { function mapStateToProps(state) { const { subscriptions } = state; - const subscriber = _.get(state, 'subscriber[0]') + const { customer } = state return { - profile: subscriber, + profile: customer, subscriptions }; } diff --git a/houston/src/components/Search/Search.js b/houston/src/components/Search/Search.js index 0e78a65fb..2b5985a93 100644 --- a/houston/src/components/Search/Search.js +++ b/houston/src/components/Search/Search.js @@ -5,13 +5,14 @@ import _ from 'lodash'; import { subscriberActions } from '../../actions/subscriber.actions'; import SearchForm from './SearchForm'; +import CustomerList from './CustomerList'; import SearchResults from './SearchResults'; import AlertMessage from './Alert'; class Search extends React.Component { onSubmit = (text) => { - this.props.getSubscriberAndBundlesByEmail(text); + this.props.getSubscriberList(text); } render() { @@ -21,6 +22,7 @@ class Search extends React.Component {
+ { hasResults && ( @@ -39,7 +41,7 @@ Search.propTypes = { function mapStateToProps(state) { const { loggedIn } = state.authentication; - const subscriber = _.get(state, 'subscriber[0]') + const subscriber = _.get(state, 'customer') return { loggedIn, profile: subscriber @@ -47,6 +49,6 @@ function mapStateToProps(state) { }; const mapDispatchToProps = { - getSubscriberAndBundlesByEmail: subscriberActions.getSubscriberAndBundlesByEmail + getSubscriberList: subscriberActions.getSubscriberList }; export default connect(mapStateToProps, mapDispatchToProps)(Search); diff --git a/houston/src/reducers/index.js b/houston/src/reducers/index.js index 76a82208d..0000096ab 100644 --- a/houston/src/reducers/index.js +++ b/houston/src/reducers/index.js @@ -12,7 +12,7 @@ import notification from '../actions/notifiy.actions'; import authentication from './auth.reducer'; import { context, - subscriber, + subscribers, subscriptions, bundles, paymentHistory, @@ -24,7 +24,7 @@ const appReducer = combineReducers({ authentication, alert, notification, - subscriber, + subscribers, customer, context, subscriptions, diff --git a/houston/src/reducers/subscriber.reducer.js b/houston/src/reducers/subscriber.reducer.js index dc9f88a4d..c12ab265a 100644 --- a/houston/src/reducers/subscriber.reducer.js +++ b/houston/src/reducers/subscriber.reducer.js @@ -3,7 +3,7 @@ import { actions } from '../actions/subscriber.actions'; const defaultState = {}; -export const subscriber = handleActions( +export const subscribers = handleActions( { [actions.subscriberByEmailRequest]: (state, action) => ({ loading: true From bc1ee3592012dba95b8a94fb36cdb56be201d71b Mon Sep 17 00:00:00 2001 From: Prasanth Ullattil Date: Thu, 17 Oct 2019 15:50:35 +0200 Subject: [PATCH 114/173] Allow search by id --- .../main/kotlin/org/ostelco/prime/storage/graph/Neo4jStore.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/neo4j-store/src/main/kotlin/org/ostelco/prime/storage/graph/Neo4jStore.kt b/neo4j-store/src/main/kotlin/org/ostelco/prime/storage/graph/Neo4jStore.kt index b7fc0808f..29aa2e4d5 100644 --- a/neo4j-store/src/main/kotlin/org/ostelco/prime/storage/graph/Neo4jStore.kt +++ b/neo4j-store/src/main/kotlin/org/ostelco/prime/storage/graph/Neo4jStore.kt @@ -2015,7 +2015,7 @@ object Neo4jStoreSingleton : GraphStore { override fun getIdentitiesFor(queryString: String): Either> = readTransaction { read(""" MATCH (c:${customerEntity.name})<-[r:${identifiesRelation.name}]-(identity:${identityEntity.name}) - WHERE c.contactEmail contains '$queryString' or c.nickname contains '$queryString' + WHERE c.contactEmail contains '$queryString' or c.nickname contains '$queryString' or c.id contains '$queryString' RETURN c, identity, r.provider as provider """.trimIndent(), transaction) { From ce402f2831c18f2684da1fd689405488cafb13db Mon Sep 17 00:00:00 2001 From: Prasanth Ullattil Date: Fri, 18 Oct 2019 10:09:54 +0200 Subject: [PATCH 115/173] Add highlights to the searched text --- houston/package.json | 1 + houston/src/components/Search/CustomerList.js | 23 ++++++++++++++++--- houston/yarn.lock | 19 +++++++++++++++ 3 files changed, 40 insertions(+), 3 deletions(-) diff --git a/houston/package.json b/houston/package.json index 0d948bcee..72296c7da 100644 --- a/houston/package.json +++ b/houston/package.json @@ -21,6 +21,7 @@ "prop-types": "^15.6.2", "react": "^16.8.6", "react-dom": "^16.8.6", + "react-highlight-words": "^0.16.0", "react-json-view": "^1.19.1", "react-redux": "^7.0.1", "react-router": "^5.0.0", diff --git a/houston/src/components/Search/CustomerList.js b/houston/src/components/Search/CustomerList.js index d2b2dc421..7964159fb 100644 --- a/houston/src/components/Search/CustomerList.js +++ b/houston/src/components/Search/CustomerList.js @@ -2,11 +2,28 @@ import React from 'react'; import { connect } from 'react-redux'; import PropTypes from 'prop-types'; import { Col, Row, Card, CardBody, Button } from 'reactstrap'; +import Highlighter from "react-highlight-words"; import { subscriberActions } from '../../actions/subscriber.actions'; +const Highlight = ({ children, highlightIndex }) => ( + {children} +); + +const convertToHighlightedText = (text, query) => { + return ( + ); +} + export const CustomerRow = props => { + let query = localStorage.getItem('searchedEmail') function onSelect(e) { e.preventDefault(); console.log(`Selecting customer with id ${props.customer.id}`); @@ -17,15 +34,15 @@ export const CustomerRow = props => {
{'Name:'} - {`${props.customer.nickname}`} + {convertToHighlightedText(props.customer.nickname, query)} {'Email:'} - {`${props.customer.contactEmail}`} + {convertToHighlightedText(props.customer.contactEmail, query)} {'ID:'} - {`${props.customer.id}`} + {convertToHighlightedText(props.customer.id, query)}
diff --git a/houston/yarn.lock b/houston/yarn.lock index ac6c25ff5..515ed7df0 100644 --- a/houston/yarn.lock +++ b/houston/yarn.lock @@ -4705,6 +4705,11 @@ hex-color-regex@^1.1.0: resolved "https://registry.yarnpkg.com/hex-color-regex/-/hex-color-regex-1.1.0.tgz#4c06fccb4602fe2602b3c93df82d7e7dbf1a8a8e" integrity sha512-l9sfDFsuqtOqKDsQdqrMRk0U85RZc0RtOR9yPI7mRVOa4FsR/BVnZ0shmQRM96Ji99kYZP/7hn1cedc1+ApsTQ== +highlight-words-core@^1.2.0: + version "1.2.2" + resolved "https://registry.yarnpkg.com/highlight-words-core/-/highlight-words-core-1.2.2.tgz#1eff6d7d9f0a22f155042a00791237791b1eeaaa" + integrity sha512-BXUKIkUuh6cmmxzi5OIbUJxrG8OAk2MqoL1DtO3Wo9D2faJg2ph5ntyuQeLqaHJmzER6H5tllCDA9ZnNe9BVGg== + history@^4.9.0: version "4.9.0" resolved "https://registry.yarnpkg.com/history/-/history-4.9.0.tgz#84587c2068039ead8af769e9d6a6860a14fa1bca" @@ -6466,6 +6471,11 @@ mem@^4.0.0: mimic-fn "^2.0.0" p-is-promise "^2.0.0" +memoize-one@^4.0.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/memoize-one/-/memoize-one-4.1.0.tgz#a2387c58c03fff27ca390c31b764a79addf3f906" + integrity sha512-2GApq0yI/b22J2j9rhbrAlsHb0Qcz+7yWxeLG8h+95sl1XPUgeLimQSOdur4Vw7cUhrBHwaUZxWFZueojqNRzA== + memory-fs@^0.4.0, memory-fs@^0.4.1: version "0.4.1" resolved "https://registry.yarnpkg.com/memory-fs/-/memory-fs-0.4.1.tgz#3a9a20b8462523e447cfbc7e8bb80ed667bfc552" @@ -8521,6 +8531,15 @@ react-error-overlay@^6.0.1: resolved "https://registry.yarnpkg.com/react-error-overlay/-/react-error-overlay-6.0.1.tgz#b8d3cf9bb991c02883225c48044cb3ee20413e0f" integrity sha512-V9yoTr6MeZXPPd4nV/05eCBvGH9cGzc52FN8fs0O0TVQ3HYYf1n7EgZVtHbldRq5xU9zEzoXIITjYNIfxDDdUw== +react-highlight-words@^0.16.0: + version "0.16.0" + resolved "https://registry.yarnpkg.com/react-highlight-words/-/react-highlight-words-0.16.0.tgz#4b4b9824e3d2b98789d3e3b3aedb5e961ae1b7cf" + integrity sha512-q34TwCSJOL+5pVDv6LUj3amaoyXdNDwd7zRqVAvceOrO9g1haWLAglK6WkGLMNUa3PFN8EgGedLg/k8Gpndxqg== + dependencies: + highlight-words-core "^1.2.0" + memoize-one "^4.0.0" + prop-types "^15.5.8" + react-is@^16.6.0, react-is@^16.7.0, react-is@^16.8.1, react-is@^16.8.4, react-is@^16.8.6, react-is@^16.9.0: version "16.9.0" resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.9.0.tgz#21ca9561399aad0ff1a7701c01683e8ca981edcb" From e79a631d51b0046cafe680009aa39466b5021fde Mon Sep 17 00:00:00 2001 From: Prasanth Ullattil Date: Fri, 18 Oct 2019 10:11:16 +0200 Subject: [PATCH 116/173] Whitespace fixes --- houston/src/components/Search/CustomerList.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/houston/src/components/Search/CustomerList.js b/houston/src/components/Search/CustomerList.js index 7964159fb..eb3a874a5 100644 --- a/houston/src/components/Search/CustomerList.js +++ b/houston/src/components/Search/CustomerList.js @@ -11,7 +11,7 @@ const Highlight = ({ children, highlightIndex }) => ( ); const convertToHighlightedText = (text, query) => { - return ( + return ( Date: Fri, 18 Oct 2019 10:56:36 +0200 Subject: [PATCH 117/173] Fix Msisdn search --- .../ostelco/prime/support/resources/HoustonResources.kt | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/customer-support-endpoint/src/main/kotlin/org/ostelco/prime/support/resources/HoustonResources.kt b/customer-support-endpoint/src/main/kotlin/org/ostelco/prime/support/resources/HoustonResources.kt index e2263a84a..a04d51a1f 100644 --- a/customer-support-endpoint/src/main/kotlin/org/ostelco/prime/support/resources/HoustonResources.kt +++ b/customer-support-endpoint/src/main/kotlin/org/ostelco/prime/support/resources/HoustonResources.kt @@ -63,7 +63,7 @@ class ProfilesResource { Response.status(Response.Status.UNAUTHORIZED) } else { val trimmedQuery = query.trim() - if (trimmedQuery.toIntOrNull() != null) { + if (isMsisdn(trimmedQuery)) { logger.info("${token.name} Accessing profile for msisdn: $query") getProfileListForMsisdn(trimmedQuery) .responseBuilder() @@ -152,6 +152,12 @@ class ProfilesResource { return pattern.matcher(email).matches() } + private fun isMsisdn(query: String): Boolean { + val regex = "^[0-9]+$" + val pattern = Pattern.compile(regex) + return pattern.matcher(query).matches() + } + private fun getProfileListForMsisdn(msisdn: String): Either> { return try { storage.getCustomerForMsisdn(msisdn).mapLeft { From 7a405f771b1b42ae69862280b5ddbe211de1bffc Mon Sep 17 00:00:00 2001 From: Prasanth Ullattil Date: Fri, 18 Oct 2019 11:30:55 +0200 Subject: [PATCH 118/173] Bug Fix: Wrong host for the cloud endpoint. --- prime/infra/prod/prime-houston-api.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/prime/infra/prod/prime-houston-api.yaml b/prime/infra/prod/prime-houston-api.yaml index 4efa2b006..46640bf78 100644 --- a/prime/infra/prod/prime-houston-api.yaml +++ b/prime/infra/prod/prime-houston-api.yaml @@ -3,7 +3,7 @@ info: title: "Houston Admin API" description: "The APIs for the Houston Admin Client." version: "2.0.0" -host: "houston-api.dev.oya.world" +host: "houston-api.oya.world" x-google-endpoints: - name: "houston-api.oya.world" allowCors: true From 2b7ce0c3e669620d4029121a7426bbc473457894 Mon Sep 17 00:00:00 2001 From: Vihang Patil Date: Wed, 16 Oct 2019 16:09:10 +0200 Subject: [PATCH 119/173] Moved Rating and Charging logic in OCS to kotlin-scripts. --- ocs-ktc/build.gradle.kts | 1 + .../kotlin/org/ostelco/prime/ocs/OcsModule.kt | 8 +- .../org/ostelco/prime/ocs/core/KtsServices.kt | 21 +++++ .../ostelco/prime/ocs/core/OnlineCharging.kt | 85 ++++++++++--------- .../org/ostelco/prime/ocs/core/Rating.kt | 30 ------- ocs-ktc/src/main/resources/.gitignore | 1 + .../resources/ChargingAndRatingService.kts | 51 +++++++++++ prime/build.gradle.kts | 2 +- prime/config/config.yaml | 27 ++---- prime/config/test.yaml | 18 ++-- prime/script/start.sh | 2 +- .../integration-test/resources/config.yaml | 5 ++ 12 files changed, 137 insertions(+), 114 deletions(-) create mode 100644 ocs-ktc/src/main/kotlin/org/ostelco/prime/ocs/core/KtsServices.kt delete mode 100644 ocs-ktc/src/main/kotlin/org/ostelco/prime/ocs/core/Rating.kt create mode 100644 ocs-ktc/src/main/resources/.gitignore create mode 100644 ocs-ktc/src/main/resources/ChargingAndRatingService.kts diff --git a/ocs-ktc/build.gradle.kts b/ocs-ktc/build.gradle.kts index 1b6b870ec..4dd5cdcc6 100644 --- a/ocs-ktc/build.gradle.kts +++ b/ocs-ktc/build.gradle.kts @@ -7,6 +7,7 @@ plugins { dependencies { implementation(project(":prime-modules")) + api(project(":kts-engine")) implementation("com.google.cloud:google-cloud-pubsub:${Version.googleCloudPubSub}") diff --git a/ocs-ktc/src/main/kotlin/org/ostelco/prime/ocs/OcsModule.kt b/ocs-ktc/src/main/kotlin/org/ostelco/prime/ocs/OcsModule.kt index c1f750b4b..de3e27577 100644 --- a/ocs-ktc/src/main/kotlin/org/ostelco/prime/ocs/OcsModule.kt +++ b/ocs-ktc/src/main/kotlin/org/ostelco/prime/ocs/OcsModule.kt @@ -3,6 +3,7 @@ package org.ostelco.prime.ocs import com.fasterxml.jackson.annotation.JsonProperty import com.fasterxml.jackson.annotation.JsonTypeName import io.dropwizard.setup.Environment +import org.ostelco.prime.kts.engine.KtsServiceFactory import org.ostelco.prime.module.PrimeModule import org.ostelco.prime.ocs.ConfigRegistry.config import org.ostelco.prime.ocs.activation.ActivateEventObservableSingleton @@ -10,7 +11,6 @@ import org.ostelco.prime.ocs.consumption.grpc.OcsGrpcServer import org.ostelco.prime.ocs.consumption.grpc.OcsGrpcService import org.ostelco.prime.ocs.consumption.pubsub.PubSubClient import org.ostelco.prime.ocs.core.OnlineCharging -import org.ostelco.prime.ocs.core.Rating @JsonTypeName("ocs") class OcsModule : PrimeModule { @@ -43,10 +43,6 @@ class OcsModule : PrimeModule { } ) } - - config.rating?.forEach { rate -> - Rating.addRate(rate.serviceId, rate.ratingGroup, rate.rate) - } } } @@ -63,7 +59,7 @@ data class PubSubChannel( data class Config( val lowBalanceThreshold: Long = 0, val pubSubChannel: PubSubChannel? = null, - val rating: ArrayList? = null) + val chargingAndRatingService: KtsServiceFactory) object ConfigRegistry { lateinit var config: Config diff --git a/ocs-ktc/src/main/kotlin/org/ostelco/prime/ocs/core/KtsServices.kt b/ocs-ktc/src/main/kotlin/org/ostelco/prime/ocs/core/KtsServices.kt new file mode 100644 index 000000000..7b8a84eda --- /dev/null +++ b/ocs-ktc/src/main/kotlin/org/ostelco/prime/ocs/core/KtsServices.kt @@ -0,0 +1,21 @@ +package org.ostelco.prime.ocs.core + +import arrow.core.Either +import org.ostelco.ocs.api.MultipleServiceCreditControl +import org.ostelco.prime.storage.ConsumptionResult + +data class ConsumptionRequest( + val msisdn: String, + val usedBytes: Long, + val requestedBytes: Long +) + +interface ChargingAndRatingService { + + fun charge( + msisdn: String, + multipleServiceCreditControl: MultipleServiceCreditControl, + mccMnc: String, + apn: String + ): Either +} diff --git a/ocs-ktc/src/main/kotlin/org/ostelco/prime/ocs/core/OnlineCharging.kt b/ocs-ktc/src/main/kotlin/org/ostelco/prime/ocs/core/OnlineCharging.kt index 6545a2946..40aff5887 100644 --- a/ocs-ktc/src/main/kotlin/org/ostelco/prime/ocs/core/OnlineCharging.kt +++ b/ocs-ktc/src/main/kotlin/org/ostelco/prime/ocs/core/OnlineCharging.kt @@ -1,7 +1,5 @@ package org.ostelco.prime.ocs.core -import arrow.core.Either -import arrow.core.right import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch @@ -14,12 +12,12 @@ import org.ostelco.ocs.api.ResultCode import org.ostelco.ocs.api.ServiceUnit import org.ostelco.prime.getLogger import org.ostelco.prime.module.getResource +import org.ostelco.prime.ocs.ConfigRegistry import org.ostelco.prime.ocs.analytics.AnalyticsReporter import org.ostelco.prime.ocs.consumption.OcsAsyncRequestConsumer import org.ostelco.prime.ocs.notifications.Notifications import org.ostelco.prime.storage.AdminDataSource import org.ostelco.prime.storage.ConsumptionResult -import org.ostelco.prime.storage.StoreError import java.util.concurrent.CountDownLatch import java.util.concurrent.TimeUnit @@ -30,10 +28,13 @@ object OnlineCharging : OcsAsyncRequestConsumer { val keepAliveMsisdn = "keepalive" private val loadAcceptanceTest = System.getenv("LOAD_TESTING") == "true" - private val storage: AdminDataSource = getResource() - private val logger by getLogger() + private val storage: AdminDataSource = getResource() + private val chargingAndRatingService by lazy { + ConfigRegistry.config.chargingAndRatingService.getKtsService() + } + override fun creditControlRequestEvent( request: CreditControlRequestInfo, returnCreditControlAnswer: (CreditControlAnswerInfo) -> Unit) { @@ -50,9 +51,9 @@ object OnlineCharging : OcsAsyncRequestConsumer { } - private fun isKeepAlive(request: CreditControlRequestInfo) : Boolean = request.msisdn == keepAliveMsisdn + private fun isKeepAlive(request: CreditControlRequestInfo): Boolean = request.msisdn == keepAliveMsisdn - private fun handleKeepAlive(request: CreditControlRequestInfo, returnCreditControlAnswer : (CreditControlAnswerInfo) -> Unit) { + private fun handleKeepAlive(request: CreditControlRequestInfo, returnCreditControlAnswer: (CreditControlAnswerInfo) -> Unit) { val responseBuilder = CreditControlAnswerInfo.newBuilder() .setRequestNumber(request.requestNumber) .setRequestId(request.requestId) @@ -76,10 +77,10 @@ object OnlineCharging : OcsAsyncRequestConsumer { if (request.msccCount == 0) { responseBuilder.validityTime = 86400 - storage.consume(msisdn, 0L, 0L) { - storeResult -> storeResult.fold( - {responseBuilder.resultCode = ResultCode.DIAMETER_USER_UNKNOWN}, - {responseBuilder.resultCode = ResultCode.DIAMETER_SUCCESS}) + storage.consume(msisdn, 0L, 0L) { storeResult -> + storeResult.fold( + { responseBuilder.resultCode = ResultCode.DIAMETER_USER_UNKNOWN }, + { responseBuilder.resultCode = ResultCode.DIAMETER_SUCCESS }) } } else { chargeMSCCs(request, msisdn, responseBuilder) @@ -92,29 +93,43 @@ object OnlineCharging : OcsAsyncRequestConsumer { } private suspend fun chargeMSCCs(request: CreditControlRequestInfo, - msisdn: String, - responseBuilder : CreditControlAnswerInfo.Builder) { + msisdn: String, + responseBuilder: CreditControlAnswerInfo.Builder) { + val doneSignal = CountDownLatch(request.msccList.size) + request.msccList.forEach { mscc -> + fun consumptionResultHandler(consumptionResult: ConsumptionResult) { + addGrantedQuota(consumptionResult.granted, mscc, responseBuilder) + addInfo(consumptionResult.balance, mscc, responseBuilder) + reportAnalytics(consumptionResult, request) + Notifications.lowBalanceAlert(msisdn, consumptionResult.granted, consumptionResult.balance) + doneSignal.countDown() + } + val requested = mscc.requested?.totalOctets ?: 0 if (requested > 0) { - charge(msisdn, mscc, request.serviceInformation.psInformation.sgsnMccMnc) { storeResult -> - storeResult.fold( - { - // FixMe : should all store errors be unknown user? - responseBuilder.resultCode = ResultCode.DIAMETER_USER_UNKNOWN - doneSignal.countDown() - }, - { consumptionResult -> - addGrantedQuota(consumptionResult.granted, mscc, responseBuilder) - addInfo(consumptionResult.balance, mscc, responseBuilder) - reportAnalytics(consumptionResult, request) - Notifications.lowBalanceAlert(msisdn, consumptionResult.granted, consumptionResult.balance) - doneSignal.countDown() + chargingAndRatingService.charge( + msisdn = msisdn, + multipleServiceCreditControl = mscc, + mccMnc = request.serviceInformation.psInformation.sgsnMccMnc, + apn = request.serviceInformation.psInformation.calledStationId) + .bimap(::consumptionResultHandler) { consumptionRequest -> + storage.consume( + msisdn = consumptionRequest.msisdn, + usedBytes = consumptionRequest.usedBytes, + requestedBytes = consumptionRequest.requestedBytes) { storeResult -> + + storeResult.swap() + .fold(::consumptionResultHandler) { storeError -> + // FixMe : should all store errors be unknown user? + logger.error(storeError.message) + responseBuilder.resultCode = ResultCode.DIAMETER_USER_UNKNOWN + doneSignal.countDown() + } } - ) - } + } } else { doneSignal.countDown() } @@ -122,7 +137,7 @@ object OnlineCharging : OcsAsyncRequestConsumer { doneSignal.await(2, TimeUnit.SECONDS) } - private fun reportAnalytics(consumptionResult : ConsumptionResult, request: CreditControlRequestInfo) { + private fun reportAnalytics(consumptionResult: ConsumptionResult, request: CreditControlRequestInfo) { if (!loadUnitTest && !loadAcceptanceTest) { CoroutineScope(Dispatchers.Default).launch { AnalyticsReporter.report( @@ -180,16 +195,4 @@ object OnlineCharging : OcsAsyncRequestConsumer { response.addMscc(responseMscc.build()) } } - - private suspend fun charge(msisdn: String, multipleServiceCreditControl: MultipleServiceCreditControl, mccmnc: String, callback: (Either) -> Unit) { - - val requested = multipleServiceCreditControl.requested?.totalOctets ?: 0 - val used = multipleServiceCreditControl.used?.totalOctets ?: 0 - - when (Rating.getRate(msisdn, multipleServiceCreditControl.serviceIdentifier, multipleServiceCreditControl.ratingGroup, mccmnc)) { - Rating.Rate.ZERO -> callback(ConsumptionResult(msisdn, multipleServiceCreditControl.requested.totalOctets, multipleServiceCreditControl.requested.totalOctets * 100).right()) - Rating.Rate.NORMAL -> storage.consume(msisdn, used, requested, callback) - Rating.Rate.BLOCKED -> callback(ConsumptionResult(msisdn, 0L, 0L).right()) - } - } } \ No newline at end of file diff --git a/ocs-ktc/src/main/kotlin/org/ostelco/prime/ocs/core/Rating.kt b/ocs-ktc/src/main/kotlin/org/ostelco/prime/ocs/core/Rating.kt deleted file mode 100644 index 8ee87875e..000000000 --- a/ocs-ktc/src/main/kotlin/org/ostelco/prime/ocs/core/Rating.kt +++ /dev/null @@ -1,30 +0,0 @@ -package org.ostelco.prime.ocs.core - -import org.ostelco.prime.getLogger - -object Rating { - enum class Rate { - BLOCKED, NORMAL, ZERO - } - - private val logger by getLogger() - private val hashMap: HashMap = HashMap() - - /** - * The rate should be set based on the subscription, location, Service-Identifier - * and the Rating-Group. - */ - @Suppress("UNUSED_PARAMETER") - fun getRate(msisdn: String, serviceIdentifier: Long, ratingGroup: Long, mccmnc: String) : Rate { - return hashMap.get(RateIdentifier(serviceIdentifier, ratingGroup)) ?: Rating.Rate.BLOCKED - } - - fun addRate(serviceIdentifier: Long, ratingGroup: Long, rate: String) { - logger.info("Adding rate for {} {} : {}", serviceIdentifier, ratingGroup, rate) - hashMap.put(RateIdentifier(serviceIdentifier, ratingGroup), Rating.Rate.valueOf(rate.toUpperCase())) - } - - data class RateIdentifier( - val serviceId: Long, - val ratingGroup: Long) -} \ No newline at end of file diff --git a/ocs-ktc/src/main/resources/.gitignore b/ocs-ktc/src/main/resources/.gitignore new file mode 100644 index 000000000..93461aabb --- /dev/null +++ b/ocs-ktc/src/main/resources/.gitignore @@ -0,0 +1 @@ +ocs-kts \ No newline at end of file diff --git a/ocs-ktc/src/main/resources/ChargingAndRatingService.kts b/ocs-ktc/src/main/resources/ChargingAndRatingService.kts new file mode 100644 index 000000000..839d816e0 --- /dev/null +++ b/ocs-ktc/src/main/resources/ChargingAndRatingService.kts @@ -0,0 +1,51 @@ +import arrow.core.Either +import arrow.core.left +import arrow.core.right +import org.ostelco.ocs.api.MultipleServiceCreditControl +import org.ostelco.prime.ocs.core.ChargingAndRatingService +import org.ostelco.prime.ocs.core.ConsumptionRequest +import org.ostelco.prime.storage.ConsumptionResult + +private data class ServiceIdRatingGroup( + val serviceId: Long, + val ratingGroup: Long +) + +object : ChargingAndRatingService { + + override fun charge( + msisdn: String, + multipleServiceCreditControl: MultipleServiceCreditControl, + mccMnc: String, + apn: String): Either { + + val requested = multipleServiceCreditControl.requested?.totalOctets ?: 0 + val used = multipleServiceCreditControl.used?.totalOctets ?: 0 + + return when (ServiceIdRatingGroup( + serviceId = multipleServiceCreditControl.serviceIdentifier, + ratingGroup = multipleServiceCreditControl.ratingGroup)) { + + // NORMAL + ServiceIdRatingGroup(1L, 10L), // Test + ServiceIdRatingGroup(2L, 12L), // Test + ServiceIdRatingGroup(4L, 14L), // Test + ServiceIdRatingGroup(-1L, 10L) /* Test */ -> { + ConsumptionRequest( + msisdn = msisdn, + usedBytes = used, + requestedBytes = requested + ).right() + } + + // BLOCKED + else -> { + ConsumptionResult( + msisdnAnalyticsId = msisdn, + granted = 0L, + balance = 0L + ).left() + } + } + } +} diff --git a/prime/build.gradle.kts b/prime/build.gradle.kts index 076665d9a..f574f8b42 100644 --- a/prime/build.gradle.kts +++ b/prime/build.gradle.kts @@ -10,7 +10,7 @@ plugins { } // Update version in [script/start.sh] too. -version = "1.65.1" +version = "1.66.0" dependencies { // interface module between prime and prime-modules diff --git a/prime/config/config.yaml b/prime/config/config.yaml index 03402526e..d06d2d976 100644 --- a/prime/config/config.yaml +++ b/prime/config/config.yaml @@ -96,28 +96,11 @@ modules: projectId: ${GCP_PROJECT_ID} activateTopicId: ${ACTIVATE_TOPIC_ID} ccrSubscriptionId: ${CCR_SUBSCRIPTION_ID} - rating: - - serviceId: 400 - ratingGroup: 400 - rate: Normal - - serviceId: 401 - ratingGroup: 401 - rate: Zero - - serviceId: 402 - ratingGroup: 402 - rate: Zero - - serviceId: 409 - ratingGroup: 409 - rate: Zero - - serviceId: -1 - ratingGroup: 600 - rate: Normal - - serviceId: 1 - ratingGroup: 10 - rate: Normal - - serviceId: -1 - ratingGroup: 102010001 - rate: Normal + chargingAndRatingService: + serviceInterface: org.ostelco.prime.ocs.core.ChargingAndRatingService + textReader: + type: file + filename: /config-data/ChargingAndRatingService.kts - type: api - type: stripe-payment-processor config: diff --git a/prime/config/test.yaml b/prime/config/test.yaml index 1e49ad3b8..428ee59d9 100644 --- a/prime/config/test.yaml +++ b/prime/config/test.yaml @@ -81,19 +81,11 @@ modules: projectId: ${GCP_PROJECT_ID} activateTopicId: ocs-activate ccrSubscriptionId: ocs-ccr-sub - rating: - - serviceId: 1 - ratingGroup: 10 - rate: Normal - - serviceId: 2 - ratingGroup: 12 - rate: Normal - - serviceId: 4 - ratingGroup: 14 - rate: Normal - - serviceId: -1 - ratingGroup: 10 - rate: Normal + chargingAndRatingService: + serviceInterface: org.ostelco.prime.ocs.core.ChargingAndRatingService + textReader: + type: classpathResource + filename: /ChargingAndRatingService.kts - type: api - type: firebase-app-notifier config: diff --git a/prime/script/start.sh b/prime/script/start.sh index 2a0ba1160..a1ae7f117 100755 --- a/prime/script/start.sh +++ b/prime/script/start.sh @@ -5,5 +5,5 @@ exec java \ -Dfile.encoding=UTF-8 \ --add-opens java.base/java.lang=ALL-UNNAMED \ --add-opens java.base/java.io=ALL-UNNAMED \ - -agentpath:/opt/cprof/profiler_java_agent.so=-cprof_service=prime,-cprof_service_version=1.65.1,-logtostderr,-minloglevel=2,-cprof_enable_heap_sampling \ + -agentpath:/opt/cprof/profiler_java_agent.so=-cprof_service=prime,-cprof_service_version=1.66.0,-logtostderr,-minloglevel=2,-cprof_enable_heap_sampling \ -jar /prime.jar server /config/config.yaml diff --git a/prime/src/integration-test/resources/config.yaml b/prime/src/integration-test/resources/config.yaml index b146a9b3b..76472d9cc 100644 --- a/prime/src/integration-test/resources/config.yaml +++ b/prime/src/integration-test/resources/config.yaml @@ -40,6 +40,11 @@ modules: - type: ocs config: lowBalanceThreshold: 0 + chargingAndRatingService: + serviceInterface: org.ostelco.prime.ocs.core.ChargingAndRatingService + textReader: + type: classpathResource + filename: /ChargingAndRatingService.kts - type: api - type: stripe-payment-processor config: From 80f9c6c291edad2fa66cca3efb52134d5c0de30f Mon Sep 17 00:00:00 2001 From: Vihang Patil Date: Fri, 18 Oct 2019 15:09:05 +0200 Subject: [PATCH 120/173] Review comments --- .../kotlin/org/ostelco/prime/ocs/OcsModule.kt | 2 +- .../org/ostelco/prime/ocs/core/KtsServices.kt | 9 +++- .../ostelco/prime/ocs/core/OnlineCharging.kt | 44 +++++++++++-------- ...rvice.kts => ConsumptionPolicyService.kts} | 6 +-- prime/config/config.yaml | 6 +-- prime/config/test.yaml | 6 +-- .../integration-test/resources/config.yaml | 6 +-- 7 files changed, 46 insertions(+), 33 deletions(-) rename ocs-ktc/src/main/resources/{ChargingAndRatingService.kts => ConsumptionPolicyService.kts} (93%) diff --git a/ocs-ktc/src/main/kotlin/org/ostelco/prime/ocs/OcsModule.kt b/ocs-ktc/src/main/kotlin/org/ostelco/prime/ocs/OcsModule.kt index de3e27577..bd8051836 100644 --- a/ocs-ktc/src/main/kotlin/org/ostelco/prime/ocs/OcsModule.kt +++ b/ocs-ktc/src/main/kotlin/org/ostelco/prime/ocs/OcsModule.kt @@ -59,7 +59,7 @@ data class PubSubChannel( data class Config( val lowBalanceThreshold: Long = 0, val pubSubChannel: PubSubChannel? = null, - val chargingAndRatingService: KtsServiceFactory) + val consumptionPolicyService: KtsServiceFactory) object ConfigRegistry { lateinit var config: Config diff --git a/ocs-ktc/src/main/kotlin/org/ostelco/prime/ocs/core/KtsServices.kt b/ocs-ktc/src/main/kotlin/org/ostelco/prime/ocs/core/KtsServices.kt index 7b8a84eda..7f05eaa74 100644 --- a/ocs-ktc/src/main/kotlin/org/ostelco/prime/ocs/core/KtsServices.kt +++ b/ocs-ktc/src/main/kotlin/org/ostelco/prime/ocs/core/KtsServices.kt @@ -10,9 +10,14 @@ data class ConsumptionRequest( val requestedBytes: Long ) -interface ChargingAndRatingService { +interface ConsumptionPolicy { - fun charge( + /** + * This function will either return [ConsumptionResult] as [Either]::Left, which is then to be returned back to PGw. + * Or it will return Consumption Request as [Either]::Right, which is then to be passed to Storage for persistence. + * And then the result from Storage will be [ConsumptionResult], which will be returned back to PGw. + */ + fun checkConsumption( msisdn: String, multipleServiceCreditControl: MultipleServiceCreditControl, mccMnc: String, diff --git a/ocs-ktc/src/main/kotlin/org/ostelco/prime/ocs/core/OnlineCharging.kt b/ocs-ktc/src/main/kotlin/org/ostelco/prime/ocs/core/OnlineCharging.kt index 40aff5887..8baceca2a 100644 --- a/ocs-ktc/src/main/kotlin/org/ostelco/prime/ocs/core/OnlineCharging.kt +++ b/ocs-ktc/src/main/kotlin/org/ostelco/prime/ocs/core/OnlineCharging.kt @@ -31,8 +31,8 @@ object OnlineCharging : OcsAsyncRequestConsumer { private val logger by getLogger() private val storage: AdminDataSource = getResource() - private val chargingAndRatingService by lazy { - ConfigRegistry.config.chargingAndRatingService.getKtsService() + private val consumptionPolicy by lazy { + ConfigRegistry.config.consumptionPolicyService.getKtsService() } override fun creditControlRequestEvent( @@ -108,28 +108,36 @@ object OnlineCharging : OcsAsyncRequestConsumer { doneSignal.countDown() } + suspend fun consumeRequestHandler(consumptionRequest: ConsumptionRequest) { + storage.consume( + msisdn = consumptionRequest.msisdn, + usedBytes = consumptionRequest.usedBytes, + requestedBytes = consumptionRequest.requestedBytes) { storeResult -> + + storeResult + .fold( + { storeError -> + // FixMe : should all store errors be unknown user? + logger.error(storeError.message) + responseBuilder.resultCode = ResultCode.DIAMETER_USER_UNKNOWN + doneSignal.countDown() + }, + { consumptionResult -> consumptionResultHandler(consumptionResult) } + ) + } + } + val requested = mscc.requested?.totalOctets ?: 0 if (requested > 0) { - chargingAndRatingService.charge( + consumptionPolicy.checkConsumption( msisdn = msisdn, multipleServiceCreditControl = mscc, mccMnc = request.serviceInformation.psInformation.sgsnMccMnc, apn = request.serviceInformation.psInformation.calledStationId) - .bimap(::consumptionResultHandler) { consumptionRequest -> - storage.consume( - msisdn = consumptionRequest.msisdn, - usedBytes = consumptionRequest.usedBytes, - requestedBytes = consumptionRequest.requestedBytes) { storeResult -> - - storeResult.swap() - .fold(::consumptionResultHandler) { storeError -> - // FixMe : should all store errors be unknown user? - logger.error(storeError.message) - responseBuilder.resultCode = ResultCode.DIAMETER_USER_UNKNOWN - doneSignal.countDown() - } - } - } + .bimap( + { consumptionResult -> consumptionResultHandler(consumptionResult) }, + { consumptionRequest -> consumeRequestHandler(consumptionRequest) } + ) } else { doneSignal.countDown() } diff --git a/ocs-ktc/src/main/resources/ChargingAndRatingService.kts b/ocs-ktc/src/main/resources/ConsumptionPolicyService.kts similarity index 93% rename from ocs-ktc/src/main/resources/ChargingAndRatingService.kts rename to ocs-ktc/src/main/resources/ConsumptionPolicyService.kts index 839d816e0..b0501d464 100644 --- a/ocs-ktc/src/main/resources/ChargingAndRatingService.kts +++ b/ocs-ktc/src/main/resources/ConsumptionPolicyService.kts @@ -2,7 +2,7 @@ import arrow.core.Either import arrow.core.left import arrow.core.right import org.ostelco.ocs.api.MultipleServiceCreditControl -import org.ostelco.prime.ocs.core.ChargingAndRatingService +import org.ostelco.prime.ocs.core.ConsumptionPolicy import org.ostelco.prime.ocs.core.ConsumptionRequest import org.ostelco.prime.storage.ConsumptionResult @@ -11,9 +11,9 @@ private data class ServiceIdRatingGroup( val ratingGroup: Long ) -object : ChargingAndRatingService { +object : ConsumptionPolicy { - override fun charge( + override fun checkConsumption( msisdn: String, multipleServiceCreditControl: MultipleServiceCreditControl, mccMnc: String, diff --git a/prime/config/config.yaml b/prime/config/config.yaml index d06d2d976..6ae0c95aa 100644 --- a/prime/config/config.yaml +++ b/prime/config/config.yaml @@ -96,11 +96,11 @@ modules: projectId: ${GCP_PROJECT_ID} activateTopicId: ${ACTIVATE_TOPIC_ID} ccrSubscriptionId: ${CCR_SUBSCRIPTION_ID} - chargingAndRatingService: - serviceInterface: org.ostelco.prime.ocs.core.ChargingAndRatingService + consumptionPolicyService: + serviceInterface: org.ostelco.prime.ocs.core.ConsumptionPolicy textReader: type: file - filename: /config-data/ChargingAndRatingService.kts + filename: /config-data/ConsumptionPolicyService.kts - type: api - type: stripe-payment-processor config: diff --git a/prime/config/test.yaml b/prime/config/test.yaml index 428ee59d9..3cc7467b6 100644 --- a/prime/config/test.yaml +++ b/prime/config/test.yaml @@ -81,11 +81,11 @@ modules: projectId: ${GCP_PROJECT_ID} activateTopicId: ocs-activate ccrSubscriptionId: ocs-ccr-sub - chargingAndRatingService: - serviceInterface: org.ostelco.prime.ocs.core.ChargingAndRatingService + consumptionPolicyService: + serviceInterface: org.ostelco.prime.ocs.core.ConsumptionPolicy textReader: type: classpathResource - filename: /ChargingAndRatingService.kts + filename: /ConsumptionPolicyService.kts - type: api - type: firebase-app-notifier config: diff --git a/prime/src/integration-test/resources/config.yaml b/prime/src/integration-test/resources/config.yaml index 76472d9cc..265823672 100644 --- a/prime/src/integration-test/resources/config.yaml +++ b/prime/src/integration-test/resources/config.yaml @@ -40,11 +40,11 @@ modules: - type: ocs config: lowBalanceThreshold: 0 - chargingAndRatingService: - serviceInterface: org.ostelco.prime.ocs.core.ChargingAndRatingService + consumptionPolicyService: + serviceInterface: org.ostelco.prime.ocs.core.ConsumptionPolicy textReader: type: classpathResource - filename: /ChargingAndRatingService.kts + filename: /ConsumptionPolicyService.kts - type: api - type: stripe-payment-processor config: From af509d1664a2c65bc2944fb1c78bdfbdb078096c Mon Sep 17 00:00:00 2001 From: Prasanth Ullattil Date: Mon, 21 Oct 2019 09:32:18 +0200 Subject: [PATCH 121/173] Updated packages using `yarn upgrade --latest` --- houston/package.json | 30 +- houston/yarn.lock | 1786 ++++++++++++++++++++++++------------------ 2 files changed, 1031 insertions(+), 785 deletions(-) diff --git a/houston/package.json b/houston/package.json index 72296c7da..11a6cd617 100644 --- a/houston/package.json +++ b/houston/package.json @@ -3,33 +3,33 @@ "version": "0.1.0", "private": true, "dependencies": { - "@fortawesome/fontawesome-svg-core": "^1.2.19", - "@fortawesome/free-solid-svg-icons": "^5.9.0", - "@fortawesome/react-fontawesome": "^0.1.4", + "@fortawesome/fontawesome-svg-core": "^1.2.25", + "@fortawesome/free-solid-svg-icons": "^5.11.2", + "@fortawesome/react-fontawesome": "^0.1.7", "auth0-js": "^9.8.2", "bootstrap": "^4.3.1", "classnames": "^2.2.6", "enzyme": "^3.10.0", - "enzyme-adapter-react-16": "^1.14.0", + "enzyme-adapter-react-16": "^1.15.1", "humps": "^2.0.1", - "jest": "24.8.0", - "jest-enzyme": "^7.0.2", + "jest": "24.9.0", + "jest-enzyme": "^7.1.1", "jquery": "^3.3.1", "lodash": "^4.17.14", "normalizr": "^3.3.0", - "popper.js": "^1.14.4", + "popper.js": "^1.16.0", "prop-types": "^15.6.2", - "react": "^16.8.6", - "react-dom": "^16.8.6", + "react": "^16.10.2", + "react-dom": "^16.10.2", "react-highlight-words": "^0.16.0", "react-json-view": "^1.19.1", "react-redux": "^7.0.1", - "react-router": "^5.0.0", - "react-router-dom": "^5.0.0", - "react-scripts": "3.1.1", - "react-table": "^6.10.0", - "react-test-renderer": "^16.6.3", - "reactstrap": "^8.0.1", + "react-router": "^5.1.2", + "react-router-dom": "^5.1.2", + "react-scripts": "3.2.0", + "react-table": "^6.10.3", + "react-test-renderer": "^16.10.2", + "reactstrap": "^8.1.1", "redux": "^4.0.1", "redux-actions": "^2.6.4", "redux-devtools-extension": "^2.13.5", diff --git a/houston/yarn.lock b/houston/yarn.lock index 515ed7df0..6e052f94d 100644 --- a/houston/yarn.lock +++ b/houston/yarn.lock @@ -9,18 +9,18 @@ dependencies: "@babel/highlight" "^7.0.0" -"@babel/core@7.5.5", "@babel/core@^7.1.0", "@babel/core@^7.4.5": - version "7.5.5" - resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.5.5.tgz#17b2686ef0d6bc58f963dddd68ab669755582c30" - integrity sha512-i4qoSr2KTtce0DmkuuQBV4AuQgGPUcPXMr9L5MyYAtk06z068lQ10a4O009fe5OB/DfNV+h+qqT7ddNV8UnRjg== +"@babel/core@7.6.0": + version "7.6.0" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.6.0.tgz#9b00f73554edd67bebc86df8303ef678be3d7b48" + integrity sha512-FuRhDRtsd6IptKpHXAa+4WPZYY2ZzgowkbLBecEDDSje1X/apG7jQM33or3NdOmjXBKWGOg4JmSiRfUfuTtHXw== dependencies: "@babel/code-frame" "^7.5.5" - "@babel/generator" "^7.5.5" - "@babel/helpers" "^7.5.5" - "@babel/parser" "^7.5.5" - "@babel/template" "^7.4.4" - "@babel/traverse" "^7.5.5" - "@babel/types" "^7.5.5" + "@babel/generator" "^7.6.0" + "@babel/helpers" "^7.6.0" + "@babel/parser" "^7.6.0" + "@babel/template" "^7.6.0" + "@babel/traverse" "^7.6.0" + "@babel/types" "^7.6.0" convert-source-map "^1.1.0" debug "^4.1.0" json5 "^2.1.0" @@ -29,16 +29,35 @@ semver "^5.4.1" source-map "^0.5.0" -"@babel/generator@^7.4.0", "@babel/generator@^7.5.5": - version "7.5.5" - resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.5.5.tgz#873a7f936a3c89491b43536d12245b626664e3cf" - integrity sha512-ETI/4vyTSxTzGnU2c49XHv2zhExkv9JHLTwDAFz85kmcwuShvYG2H08FwgIguQf4JC75CBnXAUM5PqeF4fj0nQ== +"@babel/core@^7.1.0", "@babel/core@^7.4.5": + version "7.6.4" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.6.4.tgz#6ebd9fe00925f6c3e177bb726a188b5f578088ff" + integrity sha512-Rm0HGw101GY8FTzpWSyRbki/jzq+/PkNQJ+nSulrdY6gFGOsNseCqD6KHRYe2E+EdzuBdr2pxCp6s4Uk6eJ+XQ== dependencies: - "@babel/types" "^7.5.5" + "@babel/code-frame" "^7.5.5" + "@babel/generator" "^7.6.4" + "@babel/helpers" "^7.6.2" + "@babel/parser" "^7.6.4" + "@babel/template" "^7.6.0" + "@babel/traverse" "^7.6.3" + "@babel/types" "^7.6.3" + convert-source-map "^1.1.0" + debug "^4.1.0" + json5 "^2.1.0" + lodash "^4.17.13" + resolve "^1.3.2" + semver "^5.4.1" + source-map "^0.5.0" + +"@babel/generator@^7.4.0", "@babel/generator@^7.6.0", "@babel/generator@^7.6.3", "@babel/generator@^7.6.4": + version "7.6.4" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.6.4.tgz#a4f8437287bf9671b07f483b76e3bb731bc97671" + integrity sha512-jsBuXkFoZxk0yWLyGI9llT9oiQ2FeTASmRFE32U+aaDTfoE92t78eroO7PTpU/OrYq38hlcDM6vbfLDaOLy+7w== + dependencies: + "@babel/types" "^7.6.3" jsesc "^2.5.1" lodash "^4.17.13" source-map "^0.5.0" - trim-right "^1.0.1" "@babel/helper-annotate-as-pure@^7.0.0": version "7.0.0" @@ -72,10 +91,10 @@ "@babel/traverse" "^7.4.4" "@babel/types" "^7.4.4" -"@babel/helper-create-class-features-plugin@^7.4.4", "@babel/helper-create-class-features-plugin@^7.5.5": - version "7.5.5" - resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.5.5.tgz#401f302c8ddbc0edd36f7c6b2887d8fa1122e5a4" - integrity sha512-ZsxkyYiRA7Bg+ZTRpPvB6AbOFKTFFK4LrvTet8lInm0V468MWCaSYJE+I7v2z2r8KNLtYiV+K5kTCnR7dvyZjg== +"@babel/helper-create-class-features-plugin@^7.5.5", "@babel/helper-create-class-features-plugin@^7.6.0": + version "7.6.0" + resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.6.0.tgz#769711acca889be371e9bc2eb68641d55218021f" + integrity sha512-O1QWBko4fzGju6VoVvrZg0RROCVifcLxiApnGP3OWfWzvxRZFCoBD81K5ur5e3bVY2Vf/5rIJm8cqPKn8HUJng== dependencies: "@babel/helper-function-name" "^7.1.0" "@babel/helper-member-expression-to-functions" "^7.5.5" @@ -215,14 +234,14 @@ "@babel/traverse" "^7.1.0" "@babel/types" "^7.2.0" -"@babel/helpers@^7.5.5": - version "7.5.5" - resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.5.5.tgz#63908d2a73942229d1e6685bc2a0e730dde3b75e" - integrity sha512-nRq2BUhxZFnfEn/ciJuhklHvFOqjJUD5wpx+1bxUF2axL9C+v4DE/dmp5sT2dKnpOs4orZWzpAZqlCy8QqE/7g== +"@babel/helpers@^7.6.0", "@babel/helpers@^7.6.2": + version "7.6.2" + resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.6.2.tgz#681ffe489ea4dcc55f23ce469e58e59c1c045153" + integrity sha512-3/bAUL8zZxYs1cdX2ilEE0WobqbCmKWr/889lf2SS0PpDcpEIY8pb1CCyz0pEcX3pEb+MCbks1jIokz2xLtGTA== dependencies: - "@babel/template" "^7.4.4" - "@babel/traverse" "^7.5.5" - "@babel/types" "^7.5.5" + "@babel/template" "^7.6.0" + "@babel/traverse" "^7.6.2" + "@babel/types" "^7.6.0" "@babel/highlight@^7.0.0": version "7.5.0" @@ -233,10 +252,10 @@ esutils "^2.0.2" js-tokens "^4.0.0" -"@babel/parser@^7.0.0", "@babel/parser@^7.1.0", "@babel/parser@^7.4.3", "@babel/parser@^7.4.4", "@babel/parser@^7.5.5": - version "7.5.5" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.5.5.tgz#02f077ac8817d3df4a832ef59de67565e71cca4b" - integrity sha512-E5BN68cqR7dhKan1SfqgPGhQ178bkVKpXTPEXnFJBrEt8/DKRZlybmy+IgYLTeN7tp1R5Ccmbm2rBk17sHYU3g== +"@babel/parser@^7.0.0", "@babel/parser@^7.1.0", "@babel/parser@^7.4.3", "@babel/parser@^7.6.0", "@babel/parser@^7.6.3", "@babel/parser@^7.6.4": + version "7.6.4" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.6.4.tgz#cb9b36a7482110282d5cb6dd424ec9262b473d81" + integrity sha512-D8RHPW5qd0Vbyo3qb+YjO5nvUVRTXFLQ/FsDxJU2Nqz4uB5EnUN0ZQSEYpvTIbRuttig1XbHWU5oMeQwQSAA+A== "@babel/plugin-proposal-async-generator-functions@^7.2.0": version "7.2.0" @@ -255,12 +274,12 @@ "@babel/helper-create-class-features-plugin" "^7.5.5" "@babel/helper-plugin-utils" "^7.0.0" -"@babel/plugin-proposal-decorators@7.4.4": - version "7.4.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-decorators/-/plugin-proposal-decorators-7.4.4.tgz#de9b2a1a8ab0196f378e2a82f10b6e2a36f21cc0" - integrity sha512-z7MpQz3XC/iQJWXH9y+MaWcLPNSMY9RQSthrLzak8R8hCj0fuyNk+Dzi9kfNe/JxxlWQ2g7wkABbgWjW36MTcw== +"@babel/plugin-proposal-decorators@7.6.0": + version "7.6.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-decorators/-/plugin-proposal-decorators-7.6.0.tgz#6659d2572a17d70abd68123e89a12a43d90aa30c" + integrity sha512-ZSyYw9trQI50sES6YxREXKu+4b7MAg6Qx2cvyDDYjP2Hpzd3FleOUwC9cqn1+za8d0A2ZU8SHujxFao956efUg== dependencies: - "@babel/helper-create-class-features-plugin" "^7.4.4" + "@babel/helper-create-class-features-plugin" "^7.6.0" "@babel/helper-plugin-utils" "^7.0.0" "@babel/plugin-syntax-decorators" "^7.2.0" @@ -280,7 +299,7 @@ "@babel/helper-plugin-utils" "^7.0.0" "@babel/plugin-syntax-json-strings" "^7.2.0" -"@babel/plugin-proposal-object-rest-spread@7.5.5", "@babel/plugin-proposal-object-rest-spread@^7.5.5": +"@babel/plugin-proposal-object-rest-spread@7.5.5": version "7.5.5" resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.5.5.tgz#61939744f71ba76a3ae46b5eea18a54c16d22e58" integrity sha512-F2DxJJSQ7f64FyTVl5cw/9MWn6naXGdk3Q3UhDbFEEHv+EilCPoeRD3Zh/Utx1CJz4uyKlQ4uH+bJPbEhMV7Zw== @@ -288,6 +307,14 @@ "@babel/helper-plugin-utils" "^7.0.0" "@babel/plugin-syntax-object-rest-spread" "^7.2.0" +"@babel/plugin-proposal-object-rest-spread@^7.5.5", "@babel/plugin-proposal-object-rest-spread@^7.6.2": + version "7.6.2" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.6.2.tgz#8ffccc8f3a6545e9f78988b6bf4fe881b88e8096" + integrity sha512-LDBXlmADCsMZV1Y9OQwMc0MyGZ8Ta/zlD9N67BfQT8uYwkRswiu2hU6nJKrjrt/58aH/vqfQlR/9yId/7A2gWw== + dependencies: + "@babel/helper-plugin-utils" "^7.0.0" + "@babel/plugin-syntax-object-rest-spread" "^7.2.0" + "@babel/plugin-proposal-optional-catch-binding@^7.2.0": version "7.2.0" resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.2.0.tgz#135d81edb68a081e55e56ec48541ece8065c38f5" @@ -296,14 +323,14 @@ "@babel/helper-plugin-utils" "^7.0.0" "@babel/plugin-syntax-optional-catch-binding" "^7.2.0" -"@babel/plugin-proposal-unicode-property-regex@^7.4.4": - version "7.4.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.4.4.tgz#501ffd9826c0b91da22690720722ac7cb1ca9c78" - integrity sha512-j1NwnOqMG9mFUOH58JTFsA/+ZYzQLUZ/drqWUqxCYLGeu2JFZL8YrNC9hBxKmWtAuOCHPcRpgv7fhap09Fb4kA== +"@babel/plugin-proposal-unicode-property-regex@^7.4.4", "@babel/plugin-proposal-unicode-property-regex@^7.6.2": + version "7.6.2" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.6.2.tgz#05413762894f41bfe42b9a5e80919bd575dcc802" + integrity sha512-NxHETdmpeSCtiatMRYWVJo7266rrvAC3DTeG5exQBIH/fMIUK7ejDNznBbn3HQl/o9peymRRg7Yqkx6PdUXmMw== dependencies: "@babel/helper-plugin-utils" "^7.0.0" "@babel/helper-regex" "^7.4.4" - regexpu-core "^4.5.4" + regexpu-core "^4.6.0" "@babel/plugin-syntax-async-generators@^7.2.0": version "7.2.0" @@ -391,10 +418,10 @@ dependencies: "@babel/helper-plugin-utils" "^7.0.0" -"@babel/plugin-transform-block-scoping@^7.5.5": - version "7.5.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.5.5.tgz#a35f395e5402822f10d2119f6f8e045e3639a2ce" - integrity sha512-82A3CLRRdYubkG85lKwhZB0WZoHxLGsJdux/cOVaJCJpvYFl1LVzAIFyRsa7CvXqW8rBM4Zf3Bfn8PHt5DP0Sg== +"@babel/plugin-transform-block-scoping@^7.6.0", "@babel/plugin-transform-block-scoping@^7.6.3": + version "7.6.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.6.3.tgz#6e854e51fbbaa84351b15d4ddafe342f3a5d542a" + integrity sha512-7hvrg75dubcO3ZI2rjYTzUrEuh1E9IyDEhhB6qfcooxhDA33xx2MasuLVgdxzcP6R/lipAC6n9ub9maNW6RKdw== dependencies: "@babel/helper-plugin-utils" "^7.0.0" lodash "^4.17.13" @@ -420,21 +447,21 @@ dependencies: "@babel/helper-plugin-utils" "^7.0.0" -"@babel/plugin-transform-destructuring@7.5.0", "@babel/plugin-transform-destructuring@^7.5.0": - version "7.5.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.5.0.tgz#f6c09fdfe3f94516ff074fe877db7bc9ef05855a" - integrity sha512-YbYgbd3TryYYLGyC7ZR+Tq8H/+bCmwoaxHfJHupom5ECstzbRLTch6gOQbhEY9Z4hiCNHEURgq06ykFv9JZ/QQ== +"@babel/plugin-transform-destructuring@7.6.0", "@babel/plugin-transform-destructuring@^7.6.0": + version "7.6.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.6.0.tgz#44bbe08b57f4480094d57d9ffbcd96d309075ba6" + integrity sha512-2bGIS5P1v4+sWTCnKNDZDxbGvEqi0ijeqM/YqHtVGrvG2y0ySgnEEhXErvE9dA0bnIzY9bIzdFK0jFA46ASIIQ== dependencies: "@babel/helper-plugin-utils" "^7.0.0" -"@babel/plugin-transform-dotall-regex@^7.4.4": - version "7.4.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.4.4.tgz#361a148bc951444312c69446d76ed1ea8e4450c3" - integrity sha512-P05YEhRc2h53lZDjRPk/OektxCVevFzZs2Gfjd545Wde3k+yFDbXORgl2e0xpbq8mLcKJ7Idss4fAg0zORN/zg== +"@babel/plugin-transform-dotall-regex@^7.4.4", "@babel/plugin-transform-dotall-regex@^7.6.2": + version "7.6.2" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.6.2.tgz#44abb948b88f0199a627024e1508acaf8dc9b2f9" + integrity sha512-KGKT9aqKV+9YMZSkowzYoYEiHqgaDhGmPNZlZxX6UeHC4z30nC1J9IrZuGqbYFB1jaIGdv91ujpze0exiVK8bA== dependencies: "@babel/helper-plugin-utils" "^7.0.0" "@babel/helper-regex" "^7.4.4" - regexpu-core "^4.5.4" + regexpu-core "^4.6.0" "@babel/plugin-transform-duplicate-keys@^7.5.0": version "7.5.0" @@ -497,10 +524,10 @@ "@babel/helper-plugin-utils" "^7.0.0" babel-plugin-dynamic-import-node "^2.3.0" -"@babel/plugin-transform-modules-commonjs@^7.5.0": - version "7.5.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.5.0.tgz#425127e6045231360858eeaa47a71d75eded7a74" - integrity sha512-xmHq0B+ytyrWJvQTc5OWAC4ii6Dhr0s22STOoydokG51JjWhyYo5mRPXoi+ZmtHQhZZwuXNN+GG5jy5UZZJxIQ== +"@babel/plugin-transform-modules-commonjs@^7.6.0": + version "7.6.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.6.0.tgz#39dfe957de4420445f1fcf88b68a2e4aa4515486" + integrity sha512-Ma93Ix95PNSEngqomy5LSBMAQvYKVe3dy+JlVJSHEXZR5ASL9lQBedMiCyVtmTLraIDVRE3ZjTZvmXXD2Ozw3g== dependencies: "@babel/helper-module-transforms" "^7.4.4" "@babel/helper-plugin-utils" "^7.0.0" @@ -524,12 +551,12 @@ "@babel/helper-module-transforms" "^7.1.0" "@babel/helper-plugin-utils" "^7.0.0" -"@babel/plugin-transform-named-capturing-groups-regex@^7.4.5": - version "7.4.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.4.5.tgz#9d269fd28a370258199b4294736813a60bbdd106" - integrity sha512-z7+2IsWafTBbjNsOxU/Iv5CvTJlr5w4+HGu1HovKYTtgJ362f7kBcQglkfmlspKKZ3bgrbSGvLfNx++ZJgCWsg== +"@babel/plugin-transform-named-capturing-groups-regex@^7.6.0", "@babel/plugin-transform-named-capturing-groups-regex@^7.6.3": + version "7.6.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.6.3.tgz#aaa6e409dd4fb2e50b6e2a91f7e3a3149dbce0cf" + integrity sha512-jTkk7/uE6H2s5w6VlMHeWuH+Pcy2lmdwFoeWCVnvIrDUnB5gQqTVI8WfmEAhF2CDEarGrknZcmSFg1+bkfCoSw== dependencies: - regexp-tree "^0.1.6" + regexpu-core "^4.6.0" "@babel/plugin-transform-new-target@^7.4.4": version "7.4.4" @@ -563,9 +590,9 @@ "@babel/helper-plugin-utils" "^7.0.0" "@babel/plugin-transform-react-constant-elements@^7.0.0": - version "7.5.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-constant-elements/-/plugin-transform-react-constant-elements-7.5.0.tgz#4d6ae4033bc38f8a65dfca2b6235c44522a422fc" - integrity sha512-c5Ba8cpybZFp1Izkf2sWGuNjOxoQ32tFgBvvYvwGhi4+9f6vGiSK9Gex4uVuO/Va6YJFu41aAh1MzMjUWkp0IQ== + version "7.6.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-constant-elements/-/plugin-transform-react-constant-elements-7.6.3.tgz#9fc9ea060b983c7c035acbe481cbe1fb1245bfff" + integrity sha512-1/YogSSU7Tby9rq2VCmhuRg+6pxsHy2rI7w/oo8RKoBt6uBUFG+mk6x13kK+FY1/ggN92HAfg7ADd1v1+NCOKg== dependencies: "@babel/helper-annotate-as-pure" "^7.0.0" "@babel/helper-plugin-utils" "^7.0.0" @@ -616,10 +643,10 @@ dependencies: "@babel/helper-plugin-utils" "^7.0.0" -"@babel/plugin-transform-runtime@7.5.5": - version "7.5.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.5.5.tgz#a6331afbfc59189d2135b2e09474457a8e3d28bc" - integrity sha512-6Xmeidsun5rkwnGfMOp6/z9nSzWpHFNVr2Jx7kwoq4mVatQfQx5S56drBgEHF+XQbKOdIaOiMIINvp/kAwMN+w== +"@babel/plugin-transform-runtime@7.6.0": + version "7.6.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.6.0.tgz#85a3cce402b28586138e368fce20ab3019b9713e" + integrity sha512-Da8tMf7uClzwUm/pnJ1S93m/aRXmoYNDD7TkHua8xBDdaAs54uZpTWvEt6NGwmoVMb9mZbntfTqmG2oSzN/7Vg== dependencies: "@babel/helper-module-imports" "^7.0.0" "@babel/helper-plugin-utils" "^7.0.0" @@ -633,10 +660,10 @@ dependencies: "@babel/helper-plugin-utils" "^7.0.0" -"@babel/plugin-transform-spread@^7.2.0": - version "7.2.2" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-spread/-/plugin-transform-spread-7.2.2.tgz#3103a9abe22f742b6d406ecd3cd49b774919b406" - integrity sha512-KWfky/58vubwtS0hLqEnrWJjsMGaOeSBn90Ezn5Jeg9Z8KKHmELbP1yGylMlm5N6TPKeY9A2+UaSYLdxahg01w== +"@babel/plugin-transform-spread@^7.2.0", "@babel/plugin-transform-spread@^7.6.2": + version "7.6.2" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-spread/-/plugin-transform-spread-7.6.2.tgz#fc77cf798b24b10c46e1b51b1b88c2bf661bb8dd" + integrity sha512-DpSvPFryKdK1x+EDJYCy28nmAaIMdxmhot62jAXF/o99iA33Zj2Lmcp3vDmz+MUh0LNYVPvfj5iC3feb3/+PFg== dependencies: "@babel/helper-plugin-utils" "^7.0.0" @@ -663,28 +690,28 @@ dependencies: "@babel/helper-plugin-utils" "^7.0.0" -"@babel/plugin-transform-typescript@^7.3.2": - version "7.5.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.5.5.tgz#6d862766f09b2da1cb1f7d505fe2aedab6b7d4b8" - integrity sha512-pehKf4m640myZu5B2ZviLaiBlxMCjSZ1qTEO459AXKX5GnPueyulJeCqZFs1nz/Ya2dDzXQ1NxZ/kKNWyD4h6w== +"@babel/plugin-transform-typescript@^7.6.0": + version "7.6.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.6.3.tgz#dddb50cf3b8b2ef70b22e5326e9a91f05a1db13b" + integrity sha512-aiWINBrPMSC3xTXRNM/dfmyYuPNKY/aexYqBgh0HBI5Y+WO5oRAqW/oROYeYHrF4Zw12r9rK4fMk/ZlAmqx/FQ== dependencies: - "@babel/helper-create-class-features-plugin" "^7.5.5" + "@babel/helper-create-class-features-plugin" "^7.6.0" "@babel/helper-plugin-utils" "^7.0.0" "@babel/plugin-syntax-typescript" "^7.2.0" -"@babel/plugin-transform-unicode-regex@^7.4.4": - version "7.4.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.4.4.tgz#ab4634bb4f14d36728bf5978322b35587787970f" - integrity sha512-il+/XdNw01i93+M9J9u4T7/e/Ue/vWfNZE4IRUQjplu2Mqb/AFTDimkw2tdEdSH50wuQXZAbXSql0UphQke+vA== +"@babel/plugin-transform-unicode-regex@^7.4.4", "@babel/plugin-transform-unicode-regex@^7.6.2": + version "7.6.2" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.6.2.tgz#b692aad888a7e8d8b1b214be6b9dc03d5031f698" + integrity sha512-orZI6cWlR3nk2YmYdb0gImrgCUwb5cBUwjf6Ks6dvNVvXERkwtJWOQaEOjPiu0Gu1Tq6Yq/hruCZZOOi9F34Dw== dependencies: "@babel/helper-plugin-utils" "^7.0.0" "@babel/helper-regex" "^7.4.4" - regexpu-core "^4.5.4" + regexpu-core "^4.6.0" -"@babel/preset-env@7.5.5", "@babel/preset-env@^7.4.5": - version "7.5.5" - resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.5.5.tgz#bc470b53acaa48df4b8db24a570d6da1fef53c9a" - integrity sha512-GMZQka/+INwsMz1A5UEql8tG015h5j/qjptpKY2gJ7giy8ohzU710YciJB5rcKsWGWHiW3RUnHib0E5/m3Tp3A== +"@babel/preset-env@7.6.0": + version "7.6.0" + resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.6.0.tgz#aae4141c506100bb2bfaa4ac2a5c12b395619e50" + integrity sha512-1efzxFv/TcPsNXlRhMzRnkBFMeIqBBgzwmZwlFDw5Ubj0AGLeufxugirwZmkkX/ayi3owsSqoQ4fw8LkfK9SYg== dependencies: "@babel/helper-module-imports" "^7.0.0" "@babel/helper-plugin-utils" "^7.0.0" @@ -702,10 +729,10 @@ "@babel/plugin-transform-arrow-functions" "^7.2.0" "@babel/plugin-transform-async-to-generator" "^7.5.0" "@babel/plugin-transform-block-scoped-functions" "^7.2.0" - "@babel/plugin-transform-block-scoping" "^7.5.5" + "@babel/plugin-transform-block-scoping" "^7.6.0" "@babel/plugin-transform-classes" "^7.5.5" "@babel/plugin-transform-computed-properties" "^7.2.0" - "@babel/plugin-transform-destructuring" "^7.5.0" + "@babel/plugin-transform-destructuring" "^7.6.0" "@babel/plugin-transform-dotall-regex" "^7.4.4" "@babel/plugin-transform-duplicate-keys" "^7.5.0" "@babel/plugin-transform-exponentiation-operator" "^7.2.0" @@ -714,10 +741,10 @@ "@babel/plugin-transform-literals" "^7.2.0" "@babel/plugin-transform-member-expression-literals" "^7.2.0" "@babel/plugin-transform-modules-amd" "^7.5.0" - "@babel/plugin-transform-modules-commonjs" "^7.5.0" + "@babel/plugin-transform-modules-commonjs" "^7.6.0" "@babel/plugin-transform-modules-systemjs" "^7.5.0" "@babel/plugin-transform-modules-umd" "^7.2.0" - "@babel/plugin-transform-named-capturing-groups-regex" "^7.4.5" + "@babel/plugin-transform-named-capturing-groups-regex" "^7.6.0" "@babel/plugin-transform-new-target" "^7.4.4" "@babel/plugin-transform-object-super" "^7.5.5" "@babel/plugin-transform-parameters" "^7.4.4" @@ -730,14 +757,70 @@ "@babel/plugin-transform-template-literals" "^7.4.4" "@babel/plugin-transform-typeof-symbol" "^7.2.0" "@babel/plugin-transform-unicode-regex" "^7.4.4" - "@babel/types" "^7.5.5" + "@babel/types" "^7.6.0" browserslist "^4.6.0" core-js-compat "^3.1.1" invariant "^2.2.2" js-levenshtein "^1.1.3" semver "^5.5.0" -"@babel/preset-react@7.0.0", "@babel/preset-react@^7.0.0": +"@babel/preset-env@^7.4.5": + version "7.6.3" + resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.6.3.tgz#9e1bf05a2e2d687036d24c40e4639dc46cef2271" + integrity sha512-CWQkn7EVnwzlOdR5NOm2+pfgSNEZmvGjOhlCHBDq0J8/EStr+G+FvPEiz9B56dR6MoiUFjXhfE4hjLoAKKJtIQ== + dependencies: + "@babel/helper-module-imports" "^7.0.0" + "@babel/helper-plugin-utils" "^7.0.0" + "@babel/plugin-proposal-async-generator-functions" "^7.2.0" + "@babel/plugin-proposal-dynamic-import" "^7.5.0" + "@babel/plugin-proposal-json-strings" "^7.2.0" + "@babel/plugin-proposal-object-rest-spread" "^7.6.2" + "@babel/plugin-proposal-optional-catch-binding" "^7.2.0" + "@babel/plugin-proposal-unicode-property-regex" "^7.6.2" + "@babel/plugin-syntax-async-generators" "^7.2.0" + "@babel/plugin-syntax-dynamic-import" "^7.2.0" + "@babel/plugin-syntax-json-strings" "^7.2.0" + "@babel/plugin-syntax-object-rest-spread" "^7.2.0" + "@babel/plugin-syntax-optional-catch-binding" "^7.2.0" + "@babel/plugin-transform-arrow-functions" "^7.2.0" + "@babel/plugin-transform-async-to-generator" "^7.5.0" + "@babel/plugin-transform-block-scoped-functions" "^7.2.0" + "@babel/plugin-transform-block-scoping" "^7.6.3" + "@babel/plugin-transform-classes" "^7.5.5" + "@babel/plugin-transform-computed-properties" "^7.2.0" + "@babel/plugin-transform-destructuring" "^7.6.0" + "@babel/plugin-transform-dotall-regex" "^7.6.2" + "@babel/plugin-transform-duplicate-keys" "^7.5.0" + "@babel/plugin-transform-exponentiation-operator" "^7.2.0" + "@babel/plugin-transform-for-of" "^7.4.4" + "@babel/plugin-transform-function-name" "^7.4.4" + "@babel/plugin-transform-literals" "^7.2.0" + "@babel/plugin-transform-member-expression-literals" "^7.2.0" + "@babel/plugin-transform-modules-amd" "^7.5.0" + "@babel/plugin-transform-modules-commonjs" "^7.6.0" + "@babel/plugin-transform-modules-systemjs" "^7.5.0" + "@babel/plugin-transform-modules-umd" "^7.2.0" + "@babel/plugin-transform-named-capturing-groups-regex" "^7.6.3" + "@babel/plugin-transform-new-target" "^7.4.4" + "@babel/plugin-transform-object-super" "^7.5.5" + "@babel/plugin-transform-parameters" "^7.4.4" + "@babel/plugin-transform-property-literals" "^7.2.0" + "@babel/plugin-transform-regenerator" "^7.4.5" + "@babel/plugin-transform-reserved-words" "^7.2.0" + "@babel/plugin-transform-shorthand-properties" "^7.2.0" + "@babel/plugin-transform-spread" "^7.6.2" + "@babel/plugin-transform-sticky-regex" "^7.2.0" + "@babel/plugin-transform-template-literals" "^7.4.4" + "@babel/plugin-transform-typeof-symbol" "^7.2.0" + "@babel/plugin-transform-unicode-regex" "^7.6.2" + "@babel/types" "^7.6.3" + browserslist "^4.6.0" + core-js-compat "^3.1.1" + invariant "^2.2.2" + js-levenshtein "^1.1.3" + semver "^5.5.0" + +"@babel/preset-react@7.0.0": version "7.0.0" resolved "https://registry.yarnpkg.com/@babel/preset-react/-/preset-react-7.0.0.tgz#e86b4b3d99433c7b3e9e91747e2653958bc6b3c0" integrity sha512-oayxyPS4Zj+hF6Et11BwuBkmpgT/zMxyuZgFrMeZID6Hdh3dGlk4sHCAhdBCpuCKW2ppBfl2uCCetlrUIJRY3w== @@ -748,49 +831,67 @@ "@babel/plugin-transform-react-jsx-self" "^7.0.0" "@babel/plugin-transform-react-jsx-source" "^7.0.0" -"@babel/preset-typescript@7.3.3": - version "7.3.3" - resolved "https://registry.yarnpkg.com/@babel/preset-typescript/-/preset-typescript-7.3.3.tgz#88669911053fa16b2b276ea2ede2ca603b3f307a" - integrity sha512-mzMVuIP4lqtn4du2ynEfdO0+RYcslwrZiJHXu4MGaC1ctJiW2fyaeDrtjJGs7R/KebZ1sgowcIoWf4uRpEfKEg== +"@babel/preset-react@^7.0.0": + version "7.6.3" + resolved "https://registry.yarnpkg.com/@babel/preset-react/-/preset-react-7.6.3.tgz#d5242c828322520205ae4eda5d4f4f618964e2f6" + integrity sha512-07yQhmkZmRAfwREYIQgW0HEwMY9GBJVuPY4Q12UC72AbfaawuupVWa8zQs2tlL+yun45Nv/1KreII/0PLfEsgA== dependencies: "@babel/helper-plugin-utils" "^7.0.0" - "@babel/plugin-transform-typescript" "^7.3.2" + "@babel/plugin-transform-react-display-name" "^7.0.0" + "@babel/plugin-transform-react-jsx" "^7.0.0" + "@babel/plugin-transform-react-jsx-self" "^7.0.0" + "@babel/plugin-transform-react-jsx-source" "^7.0.0" -"@babel/runtime@7.5.5", "@babel/runtime@^7.0.0", "@babel/runtime@^7.1.2", "@babel/runtime@^7.2.0", "@babel/runtime@^7.3.4", "@babel/runtime@^7.4.0", "@babel/runtime@^7.4.2", "@babel/runtime@^7.4.5", "@babel/runtime@^7.5.5": - version "7.5.5" - resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.5.5.tgz#74fba56d35efbeca444091c7850ccd494fd2f132" - integrity sha512-28QvEGyQyNkB0/m2B4FU7IEZGK2NUrcMtT6BZEFALTguLk+AUT6ofsHtPk5QyjAdUkpMJ+/Em+quwz4HOt30AQ== +"@babel/preset-typescript@7.6.0": + version "7.6.0" + resolved "https://registry.yarnpkg.com/@babel/preset-typescript/-/preset-typescript-7.6.0.tgz#25768cb8830280baf47c45ab1a519a9977498c98" + integrity sha512-4xKw3tTcCm0qApyT6PqM9qniseCE79xGHiUnNdKGdxNsGUc2X7WwZybqIpnTmoukg3nhPceI5KPNzNqLNeIJww== + dependencies: + "@babel/helper-plugin-utils" "^7.0.0" + "@babel/plugin-transform-typescript" "^7.6.0" + +"@babel/runtime@7.6.0": + version "7.6.0" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.6.0.tgz#4fc1d642a9fd0299754e8b5de62c631cf5568205" + integrity sha512-89eSBLJsxNxOERC0Op4vd+0Bqm6wRMqMbFtV3i0/fbaWw/mJ8Q3eBvgX0G4SyrOOLCtbu98HspF8o09MRT+KzQ== dependencies: regenerator-runtime "^0.13.2" -"@babel/template@^7.1.0", "@babel/template@^7.4.0", "@babel/template@^7.4.4": - version "7.4.4" - resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.4.4.tgz#f4b88d1225689a08f5bc3a17483545be9e4ed237" - integrity sha512-CiGzLN9KgAvgZsnivND7rkA+AeJ9JB0ciPOD4U59GKbQP2iQl+olF1l76kJOupqidozfZ32ghwBEJDhnk9MEcw== +"@babel/runtime@^7.0.0", "@babel/runtime@^7.1.2", "@babel/runtime@^7.2.0", "@babel/runtime@^7.3.4", "@babel/runtime@^7.4.0", "@babel/runtime@^7.4.2", "@babel/runtime@^7.4.5", "@babel/runtime@^7.5.5": + version "7.6.3" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.6.3.tgz#935122c74c73d2240cafd32ddb5fc2a6cd35cf1f" + integrity sha512-kq6anf9JGjW8Nt5rYfEuGRaEAaH1mkv3Bbu6rYvLOpPh/RusSJXuKPEAoZ7L7gybZkchE8+NV5g9vKF4AGAtsA== + dependencies: + regenerator-runtime "^0.13.2" + +"@babel/template@^7.1.0", "@babel/template@^7.4.0", "@babel/template@^7.4.4", "@babel/template@^7.6.0": + version "7.6.0" + resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.6.0.tgz#7f0159c7f5012230dad64cca42ec9bdb5c9536e6" + integrity sha512-5AEH2EXD8euCk446b7edmgFdub/qfH1SN6Nii3+fyXP807QRx9Q73A2N5hNwRRslC2H9sNzaFhsPubkS4L8oNQ== dependencies: "@babel/code-frame" "^7.0.0" - "@babel/parser" "^7.4.4" - "@babel/types" "^7.4.4" + "@babel/parser" "^7.6.0" + "@babel/types" "^7.6.0" -"@babel/traverse@^7.0.0", "@babel/traverse@^7.1.0", "@babel/traverse@^7.4.3", "@babel/traverse@^7.4.4", "@babel/traverse@^7.5.5": - version "7.5.5" - resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.5.5.tgz#f664f8f368ed32988cd648da9f72d5ca70f165bb" - integrity sha512-MqB0782whsfffYfSjH4TM+LMjrJnhCNEDMDIjeTpl+ASaUvxcjoiVCo/sM1GhS1pHOXYfWVCYneLjMckuUxDaQ== +"@babel/traverse@^7.0.0", "@babel/traverse@^7.1.0", "@babel/traverse@^7.4.3", "@babel/traverse@^7.4.4", "@babel/traverse@^7.5.5", "@babel/traverse@^7.6.0", "@babel/traverse@^7.6.2", "@babel/traverse@^7.6.3": + version "7.6.3" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.6.3.tgz#66d7dba146b086703c0fb10dd588b7364cec47f9" + integrity sha512-unn7P4LGsijIxaAJo/wpoU11zN+2IaClkQAxcJWBNCMS6cmVh802IyLHNkAjQ0iYnRS3nnxk5O3fuXW28IMxTw== dependencies: "@babel/code-frame" "^7.5.5" - "@babel/generator" "^7.5.5" + "@babel/generator" "^7.6.3" "@babel/helper-function-name" "^7.1.0" "@babel/helper-split-export-declaration" "^7.4.4" - "@babel/parser" "^7.5.5" - "@babel/types" "^7.5.5" + "@babel/parser" "^7.6.3" + "@babel/types" "^7.6.3" debug "^4.1.0" globals "^11.1.0" lodash "^4.17.13" -"@babel/types@^7.0.0", "@babel/types@^7.2.0", "@babel/types@^7.3.0", "@babel/types@^7.4.0", "@babel/types@^7.4.4", "@babel/types@^7.5.5": - version "7.5.5" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.5.5.tgz#97b9f728e182785909aa4ab56264f090a028d18a" - integrity sha512-s63F9nJioLqOlW3UkyMd+BYhXt44YuaFm/VV0VwuteqjYwRrObkU7ra9pY4wAJR3oXi8hJrMcrcJdO/HH33vtw== +"@babel/types@^7.0.0", "@babel/types@^7.2.0", "@babel/types@^7.3.0", "@babel/types@^7.4.0", "@babel/types@^7.4.4", "@babel/types@^7.5.5", "@babel/types@^7.6.0", "@babel/types@^7.6.3": + version "7.6.3" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.6.3.tgz#3f07d96f854f98e2fbd45c64b0cb942d11e8ba09" + integrity sha512-CqbcpTxMcpuQTMhjI37ZHVgjBkysg5icREQIEZ0eG1yCNwg3oy+5AaLiOKmjsCj6nqOsa6Hf0ObjRVwokb7srA== dependencies: esutils "^2.0.2" lodash "^4.17.13" @@ -814,47 +915,46 @@ resolved "https://registry.yarnpkg.com/@csstools/normalize.css/-/normalize.css-9.0.1.tgz#c27b391d8457d1e893f1eddeaf5e5412d12ffbb5" integrity sha512-6It2EVfGskxZCQhuykrfnALg7oVeiI6KclWSmGDqB0AiInVrTGB9Jp9i4/Ad21u9Jde/voVQz6eFX/eSg/UsPA== -"@fortawesome/fontawesome-common-types@^0.2.22": - version "0.2.22" - resolved "https://registry.yarnpkg.com/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-0.2.22.tgz#3f1328d232a0fd5de8484d833c8519426f39f016" - integrity sha512-QmEuZsipX5/cR9JOg0fsTN4Yr/9lieYWM8AQpmRa0eIfeOcl/HLYoEa366BCGRSrgNJEexuvOgbq9jnJ22IY5g== +"@fortawesome/fontawesome-common-types@^0.2.25": + version "0.2.25" + resolved "https://registry.yarnpkg.com/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-0.2.25.tgz#6df015905081f2762e5cfddeb7a20d2e9b16c786" + integrity sha512-3RuZPDuuPELd7RXtUqTCfed14fcny9UiPOkdr2i+cYxBoTOfQgxcDoq77fHiiHcgWuo1LoBUpvGxFF1H/y7s3Q== -"@fortawesome/fontawesome-svg-core@^1.2.19": - version "1.2.22" - resolved "https://registry.yarnpkg.com/@fortawesome/fontawesome-svg-core/-/fontawesome-svg-core-1.2.22.tgz#9a6117c96c8b823c7d531000568ac75c3c02e123" - integrity sha512-Q941E4x8UfnMH3308n0qrgoja+GoqyiV846JTLoCcCWAKokLKrixCkq6RDBs8r+TtAWaLUrBpI+JFxQNX/WNPQ== +"@fortawesome/fontawesome-svg-core@^1.2.25": + version "1.2.25" + resolved "https://registry.yarnpkg.com/@fortawesome/fontawesome-svg-core/-/fontawesome-svg-core-1.2.25.tgz#24b03391d14f0c6171e8cad7057c687b74049790" + integrity sha512-MotKnn53JKqbkLQiwcZSBJVYtTgIKFbh7B8+kd05TSnfKYPFmjKKI59o2fpz5t0Hzl35vVGU6+N4twoOpZUrqA== dependencies: - "@fortawesome/fontawesome-common-types" "^0.2.22" + "@fortawesome/fontawesome-common-types" "^0.2.25" -"@fortawesome/free-solid-svg-icons@^5.9.0": - version "5.10.2" - resolved "https://registry.yarnpkg.com/@fortawesome/free-solid-svg-icons/-/free-solid-svg-icons-5.10.2.tgz#61bcecce3aa5001fd154826238dfa840de4aa05a" - integrity sha512-9Os/GRUcy+iVaznlg8GKcPSQFpIQpAg14jF0DWsMdnpJfIftlvfaQCWniR/ex9FoOpSEOrlXqmUCFL+JGeciuA== +"@fortawesome/free-solid-svg-icons@^5.11.2": + version "5.11.2" + resolved "https://registry.yarnpkg.com/@fortawesome/free-solid-svg-icons/-/free-solid-svg-icons-5.11.2.tgz#2f2f1459743a27902b76655a0d0bc5ec4d945631" + integrity sha512-zBue4i0PAZJUXOmLBBvM7L0O7wmsDC8dFv9IhpW5QL4kT9xhhVUsYg/LX1+5KaukWq4/cbDcKT+RT1aRe543sg== dependencies: - "@fortawesome/fontawesome-common-types" "^0.2.22" + "@fortawesome/fontawesome-common-types" "^0.2.25" -"@fortawesome/react-fontawesome@^0.1.4": - version "0.1.4" - resolved "https://registry.yarnpkg.com/@fortawesome/react-fontawesome/-/react-fontawesome-0.1.4.tgz#18d61d9b583ca289a61aa7dccc05bd164d6bc9ad" - integrity sha512-GwmxQ+TK7PEdfSwvxtGnMCqrfEm0/HbRHArbUudsYiy9KzVCwndxa2KMcfyTQ8El0vROrq8gOOff09RF1oQe8g== +"@fortawesome/react-fontawesome@^0.1.7": + version "0.1.7" + resolved "https://registry.yarnpkg.com/@fortawesome/react-fontawesome/-/react-fontawesome-0.1.7.tgz#c004ca75c15c5a1218101e8f042b8da8dec0c4b5" + integrity sha512-AHWSzOsHBe5vqOkrvs+CKw+8eLl+0XZsVixOWhTPpGpOA8WQUbVU6J9cmtAvTaxUU5OIf+rgxxF8ZKc3BVldxg== dependencies: - humps "^2.0.1" prop-types "^15.5.10" "@hapi/address@2.x.x": - version "2.0.0" - resolved "https://registry.yarnpkg.com/@hapi/address/-/address-2.0.0.tgz#9f05469c88cb2fd3dcd624776b54ee95c312126a" - integrity sha512-mV6T0IYqb0xL1UALPFplXYQmR0twnXG0M6jUswpquqT2sD12BOiCiLy3EvMp/Fy7s3DZElC4/aPjEjo2jeZpvw== + version "2.1.2" + resolved "https://registry.yarnpkg.com/@hapi/address/-/address-2.1.2.tgz#1c794cd6dbf2354d1eb1ef10e0303f573e1c7222" + integrity sha512-O4QDrx+JoGKZc6aN64L04vqa7e41tIiLU+OvKdcYaEMP97UttL0f9GIi9/0A4WAMx0uBd6SidDIhktZhgOcN8Q== "@hapi/bourne@1.x.x": version "1.3.2" resolved "https://registry.yarnpkg.com/@hapi/bourne/-/bourne-1.3.2.tgz#0a7095adea067243ce3283e1b56b8a8f453b242a" integrity sha512-1dVNHT76Uu5N3eJNTYcvxee+jzX4Z9lfciqRRHCU27ihbUcYi+iSc2iml5Ke1LXe1SyJCLA0+14Jh4tXJgOppA== -"@hapi/hoek@8.x.x": - version "8.2.1" - resolved "https://registry.yarnpkg.com/@hapi/hoek/-/hoek-8.2.1.tgz#924af04cbb22e17359c620d2a9c946e63f58eb77" - integrity sha512-JPiBy+oSmsq3St7XlipfN5pNA6bDJ1kpa73PrK/zR29CVClDVqy04AanM/M/qx5bSF+I61DdCfAvRrujau+zRg== +"@hapi/hoek@8.x.x", "@hapi/hoek@^8.3.0": + version "8.3.2" + resolved "https://registry.yarnpkg.com/@hapi/hoek/-/hoek-8.3.2.tgz#91e7188edebc5d876f0b91a860f555ff06f0782b" + integrity sha512-NP5SG4bzix+EtSMtcudp8TvI0lB46mXNo8uFpTDw6tqxGx4z5yx+giIunEFA0Z7oUO4DuWrOJV9xqR2tJVEdyA== "@hapi/joi@^15.0.0": version "15.1.1" @@ -867,11 +967,11 @@ "@hapi/topo" "3.x.x" "@hapi/topo@3.x.x": - version "3.1.3" - resolved "https://registry.yarnpkg.com/@hapi/topo/-/topo-3.1.3.tgz#c7a02e0d936596d29f184e6d7fdc07e8b5efce11" - integrity sha512-JmS9/vQK6dcUYn7wc2YZTqzIKubAQcJKu2KCKAru6es482U5RT5fP1EXCPtlXpiK7PR0On/kpQKI4fRKkzpZBQ== + version "3.1.6" + resolved "https://registry.yarnpkg.com/@hapi/topo/-/topo-3.1.6.tgz#68d935fa3eae7fdd5ab0d7f953f3205d8b2bfc29" + integrity sha512-tAag0jEcjwH+P2quUfipd7liWCNX2F8NvYjQp2wtInsZxnMlypdw0FtAOLxtvvkO+GSRRbmNi8m/5y42PQJYCQ== dependencies: - "@hapi/hoek" "8.x.x" + "@hapi/hoek" "^8.3.0" "@jest/console@^24.7.1", "@jest/console@^24.9.0": version "24.9.0" @@ -1012,7 +1112,7 @@ source-map "^0.6.1" write-file-atomic "2.4.1" -"@jest/types@^24.8.0", "@jest/types@^24.9.0": +"@jest/types@^24.9.0": version "24.9.0" resolved "https://registry.yarnpkg.com/@jest/types/-/types-24.9.0.tgz#63cb26cb7500d069e5a389441a7c6ab5e909fc59" integrity sha512-XKK7ze1apu5JWQ5eZjHITP66AX+QsLlbaJRBGYr8pNzwcAE2JVkwnf0yqjHTsDRcjR0mujy/NmZMXw5kl+kGBw== @@ -1054,10 +1154,10 @@ resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-replace-jsx-attribute-value/-/babel-plugin-replace-jsx-attribute-value-4.2.0.tgz#310ec0775de808a6a2e4fd4268c245fd734c1165" integrity sha512-U9m870Kqm0ko8beHawRXLGLvSi/ZMrl89gJ5BNcT452fAjtF2p4uRzXkdzvGJJJYBgx7BmqlDjBN/eCp5AAX2w== -"@svgr/babel-plugin-svg-dynamic-title@^4.3.1": - version "4.3.1" - resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-svg-dynamic-title/-/babel-plugin-svg-dynamic-title-4.3.1.tgz#646c2f5b5770c2fe318d6e51492344c3d62ddb63" - integrity sha512-p6z6JJroP989jHWcuraeWpzdejehTmLUpyC9smhTBWyPN0VVGe2phbYxpPTV7Vh8XzmFrcG55idrnfWn/2oQEw== +"@svgr/babel-plugin-svg-dynamic-title@^4.3.3": + version "4.3.3" + resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-svg-dynamic-title/-/babel-plugin-svg-dynamic-title-4.3.3.tgz#2cdedd747e5b1b29ed4c241e46256aac8110dd93" + integrity sha512-w3Be6xUNdwgParsvxkkeZb545VhXEwjGMwExMVBIdPQJeyMQHqm9Msnb2a1teHBqUYL66qtwfhNkbj1iarCG7w== "@svgr/babel-plugin-svg-em-dimensions@^4.2.0": version "4.2.0" @@ -1074,26 +1174,26 @@ resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-transform-svg-component/-/babel-plugin-transform-svg-component-4.2.0.tgz#5f1e2f886b2c85c67e76da42f0f6be1b1767b697" integrity sha512-hYfYuZhQPCBVotABsXKSCfel2slf/yvJY8heTVX1PCTaq/IgASq1IyxPPKJ0chWREEKewIU/JMSsIGBtK1KKxw== -"@svgr/babel-preset@^4.3.1": - version "4.3.1" - resolved "https://registry.yarnpkg.com/@svgr/babel-preset/-/babel-preset-4.3.1.tgz#62ffcb85d756580e8ce608e9d2ac3b9063be9e28" - integrity sha512-rPFKLmyhlh6oeBv3j2vEAj2nd2QbWqpoJLKzBLjwQVt+d9aeXajVaPNEqrES2spjXKR4OxfgSs7U0NtmAEkr0Q== +"@svgr/babel-preset@^4.3.3": + version "4.3.3" + resolved "https://registry.yarnpkg.com/@svgr/babel-preset/-/babel-preset-4.3.3.tgz#a75d8c2f202ac0e5774e6bfc165d028b39a1316c" + integrity sha512-6PG80tdz4eAlYUN3g5GZiUjg2FMcp+Wn6rtnz5WJG9ITGEF1pmFdzq02597Hn0OmnQuCVaBYQE1OVFAnwOl+0A== dependencies: "@svgr/babel-plugin-add-jsx-attribute" "^4.2.0" "@svgr/babel-plugin-remove-jsx-attribute" "^4.2.0" "@svgr/babel-plugin-remove-jsx-empty-expression" "^4.2.0" "@svgr/babel-plugin-replace-jsx-attribute-value" "^4.2.0" - "@svgr/babel-plugin-svg-dynamic-title" "^4.3.1" + "@svgr/babel-plugin-svg-dynamic-title" "^4.3.3" "@svgr/babel-plugin-svg-em-dimensions" "^4.2.0" "@svgr/babel-plugin-transform-react-native-svg" "^4.2.0" "@svgr/babel-plugin-transform-svg-component" "^4.2.0" "@svgr/core@^4.3.2": - version "4.3.2" - resolved "https://registry.yarnpkg.com/@svgr/core/-/core-4.3.2.tgz#939c89be670ad79b762f4c063f213f0e02535f2e" - integrity sha512-N+tP5CLFd1hP9RpO83QJPZY3NL8AtrdqNbuhRgBkjE/49RnMrrRsFm1wY8pueUfAGvzn6tSXUq29o6ah8RuR5w== + version "4.3.3" + resolved "https://registry.yarnpkg.com/@svgr/core/-/core-4.3.3.tgz#b37b89d5b757dc66e8c74156d00c368338d24293" + integrity sha512-qNuGF1QON1626UCaZamWt5yedpgOytvLj5BQZe2j1k1B8DUG4OyugZyfEwBeXozCUwhLEpsrgPrE+eCu4fY17w== dependencies: - "@svgr/plugin-jsx" "^4.3.2" + "@svgr/plugin-jsx" "^4.3.3" camelcase "^5.3.1" cosmiconfig "^5.2.1" @@ -1104,13 +1204,13 @@ dependencies: "@babel/types" "^7.4.4" -"@svgr/plugin-jsx@^4.3.2": - version "4.3.2" - resolved "https://registry.yarnpkg.com/@svgr/plugin-jsx/-/plugin-jsx-4.3.2.tgz#ce9ddafc8cdd74da884c9f7af014afcf37f93d3c" - integrity sha512-+1GW32RvmNmCsOkMoclA/TppNjHPLMnNZG3/Ecscxawp051XJ2MkO09Hn11VcotdC2EPrDfT8pELGRo+kbZ1Eg== +"@svgr/plugin-jsx@^4.3.2", "@svgr/plugin-jsx@^4.3.3": + version "4.3.3" + resolved "https://registry.yarnpkg.com/@svgr/plugin-jsx/-/plugin-jsx-4.3.3.tgz#e2ba913dbdfbe85252a34db101abc7ebd50992fa" + integrity sha512-cLOCSpNWQnDB1/v+SUENHH7a0XY09bfuMKdq9+gYvtuwzC2rU4I0wKGFEp1i24holdQdwodCtDQdFtJiTCWc+w== dependencies: "@babel/core" "^7.4.5" - "@svgr/babel-preset" "^4.3.1" + "@svgr/babel-preset" "^4.3.3" "@svgr/hast-util-to-babel-ast" "^4.3.2" svg-parser "^2.0.0" @@ -1138,9 +1238,9 @@ loader-utils "^1.2.3" "@types/babel__core@^7.1.0": - version "7.1.2" - resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.1.2.tgz#608c74f55928033fce18b99b213c16be4b3d114f" - integrity sha512-cfCCrFmiGY/yq0NuKNxIQvZFy9kY/1immpSpTngOnyIbD4+eJOG5mxphhHDv3CHL9GltO4GcKr54kGBg3RNdbg== + version "7.1.3" + resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.1.3.tgz#e441ea7df63cd080dfcd02ab199e6d16a735fc30" + integrity sha512-8fBo0UR2CcwWxeX7WIIgJ7lXjasFxoYgRnFHUj+hRvKkpiBJbxhdAPTCY6/ZKM0uxANFVzt4yObSLuTiTnazDA== dependencies: "@babel/parser" "^7.1.0" "@babel/types" "^7.0.0" @@ -1149,9 +1249,9 @@ "@types/babel__traverse" "*" "@types/babel__generator@*": - version "7.0.2" - resolved "https://registry.yarnpkg.com/@types/babel__generator/-/babel__generator-7.0.2.tgz#d2112a6b21fad600d7674274293c85dce0cb47fc" - integrity sha512-NHcOfab3Zw4q5sEE2COkpfXjoE7o+PmqD9DQW4koUT3roNxwziUdXGnRndMat/LJNUtePwn1TlP4do3uoe3KZQ== + version "7.6.0" + resolved "https://registry.yarnpkg.com/@types/babel__generator/-/babel__generator-7.6.0.tgz#f1ec1c104d1bb463556ecb724018ab788d0c172a" + integrity sha512-c1mZUu4up5cp9KROs/QAw0gTeHrw/x7m52LcnvMxxOZ03DmLwPV0MlGmlgzV3cnSdjhJOZsj7E7FHeioai+egw== dependencies: "@babel/types" "^7.0.0" @@ -1201,9 +1301,9 @@ integrity sha512-Il2DtDVRGDcqjDtE+rF8iqg1CArehSK84HZJCT7AMITlyXRBpuPhqGLDQMowraqqu1coEaimg4ZOqggt6L6L+A== "@types/node@*": - version "12.7.2" - resolved "https://registry.yarnpkg.com/@types/node/-/node-12.7.2.tgz#c4e63af5e8823ce9cc3f0b34f7b998c2171f0c44" - integrity sha512-dyYO+f6ihZEtNPDcWNR1fkoTDf3zAK3lAABDze3mz6POyIercH0lEUawUFXlG8xaQZmm1yEBON/4TsYv/laDYg== + version "12.11.1" + resolved "https://registry.yarnpkg.com/@types/node/-/node-12.11.1.tgz#1fd7b821f798b7fa29f667a1be8f3442bb8922a3" + integrity sha512-TJtwsqZ39pqcljJpajeoofYRfeZ7/I/OMUQ5pR4q5wOKf2ocrUvBAZUMhWsOvKx3dVc/aaV5GluBivt0sWqA5A== "@types/q@^1.5.1": version "1.5.2" @@ -1216,54 +1316,57 @@ integrity sha512-l42BggppR6zLmpfU6fq9HEa2oGPEI8yrSPL3GITjfRInppYFahObbIQOQK3UGxEnyQpltZLaPe75046NOZQikw== "@types/yargs-parser@*": - version "13.0.0" - resolved "https://registry.yarnpkg.com/@types/yargs-parser/-/yargs-parser-13.0.0.tgz#453743c5bbf9f1bed61d959baab5b06be029b2d0" - integrity sha512-wBlsw+8n21e6eTd4yVv8YD/E3xq0O6nNnJIquutAsFGE7EyMKz7W6RNT6BRu1SmdgmlCZ9tb0X+j+D6HGr8pZw== + version "13.1.0" + resolved "https://registry.yarnpkg.com/@types/yargs-parser/-/yargs-parser-13.1.0.tgz#c563aa192f39350a1d18da36c5a8da382bbd8228" + integrity sha512-gCubfBUZ6KxzoibJ+SCUc/57Ms1jz5NjHe4+dI2krNmU5zCPAphyLJYyTOg06ueIyfj+SaCUqmzun7ImlxDcKg== "@types/yargs@^13.0.0": - version "13.0.2" - resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-13.0.2.tgz#a64674fc0149574ecd90ba746e932b5a5f7b3653" - integrity sha512-lwwgizwk/bIIU+3ELORkyuOgDjCh7zuWDFqRtPPhhVgq9N1F7CvLNKg1TX4f2duwtKQ0p044Au9r1PLIXHrIzQ== + version "13.0.3" + resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-13.0.3.tgz#76482af3981d4412d65371a318f992d33464a380" + integrity sha512-K8/LfZq2duW33XW/tFwEAfnZlqIfVsoyRB3kfXdPXYhl0nfM8mmh7GS0jg7WrX2Dgq/0Ha/pR1PaR+BvmWwjiQ== dependencies: "@types/yargs-parser" "*" -"@typescript-eslint/eslint-plugin@1.13.0": - version "1.13.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-1.13.0.tgz#22fed9b16ddfeb402fd7bcde56307820f6ebc49f" - integrity sha512-WQHCozMnuNADiqMtsNzp96FNox5sOVpU8Xt4meaT4em8lOG1SrOv92/mUbEHQVh90sldKSfcOc/I0FOb/14G1g== +"@typescript-eslint/eslint-plugin@^2.2.0": + version "2.4.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-2.4.0.tgz#aaf6b542ff75b78f4191a8bf1c519184817caa24" + integrity sha512-se/YCk7PUoyMwSm/u3Ii9E+BgDUc736uw/lXCDpXEqRgPGsoBTtS8Mntue/vZX8EGyzGplYuePBuVyhZDM9EpQ== dependencies: - "@typescript-eslint/experimental-utils" "1.13.0" - eslint-utils "^1.3.1" + "@typescript-eslint/experimental-utils" "2.4.0" + eslint-utils "^1.4.2" functional-red-black-tree "^1.0.1" regexpp "^2.0.1" - tsutils "^3.7.0" + tsutils "^3.17.1" -"@typescript-eslint/experimental-utils@1.13.0": - version "1.13.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-1.13.0.tgz#b08c60d780c0067de2fb44b04b432f540138301e" - integrity sha512-zmpS6SyqG4ZF64ffaJ6uah6tWWWgZ8m+c54XXgwFtUv0jNz8aJAVx8chMCvnk7yl6xwn8d+d96+tWp7fXzTuDg== +"@typescript-eslint/experimental-utils@2.4.0": + version "2.4.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-2.4.0.tgz#dd8f3f466be25c3610a06fed22cfb6e6aa17f6d9" + integrity sha512-2cvhNaJoWavgTtnC7e1jUSPZQ7e4U2X9Yoy5sQmkS7lTESuyuZrlRcaoNuFfYEd6hgrmMU7+QoSp8Ad+kT1nfA== dependencies: "@types/json-schema" "^7.0.3" - "@typescript-eslint/typescript-estree" "1.13.0" - eslint-scope "^4.0.0" + "@typescript-eslint/typescript-estree" "2.4.0" + eslint-scope "^5.0.0" -"@typescript-eslint/parser@1.13.0": - version "1.13.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-1.13.0.tgz#61ac7811ea52791c47dc9fd4dd4a184fae9ac355" - integrity sha512-ITMBs52PCPgLb2nGPoeT4iU3HdQZHcPaZVw+7CsFagRJHUhyeTgorEwHXhFf3e7Evzi8oujKNpHc8TONth8AdQ== +"@typescript-eslint/parser@^2.2.0": + version "2.4.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-2.4.0.tgz#fe43ed5fec14af03d3594fce2c3b7ec4c8df0243" + integrity sha512-IouAKi/grJ4MFrwdXIJ1GHAwbPWYgkT3b/x8Q49F378c9nwgxVkO76e0rZeUVpwHMaUuoKG2sUeK0XGkwdlwkw== dependencies: "@types/eslint-visitor-keys" "^1.0.0" - "@typescript-eslint/experimental-utils" "1.13.0" - "@typescript-eslint/typescript-estree" "1.13.0" - eslint-visitor-keys "^1.0.0" + "@typescript-eslint/experimental-utils" "2.4.0" + "@typescript-eslint/typescript-estree" "2.4.0" + eslint-visitor-keys "^1.1.0" -"@typescript-eslint/typescript-estree@1.13.0": - version "1.13.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-1.13.0.tgz#8140f17d0f60c03619798f1d628b8434913dc32e" - integrity sha512-b5rCmd2e6DCC6tCTN9GSUAuxdYwCM/k/2wdjHGrIRGPSJotWMCe/dGpi66u42bhuh8q3QBzqM4TMA1GUUCJvdw== +"@typescript-eslint/typescript-estree@2.4.0": + version "2.4.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-2.4.0.tgz#722c95493e1b7682893edaaaec0e69f36917feef" + integrity sha512-/DzDAtMqF5d9IlXrrvu/Id/uoKjnSxf/3FbtKK679a/T7lbDM8qQuirtGvFy6Uh+x0hALuCMwnMfUf0P24/+Iw== dependencies: + chokidar "^3.0.2" + glob "^7.1.4" + is-glob "^4.0.1" lodash.unescape "4.0.1" - semver "5.5.0" + semver "^6.3.0" "@webassemblyjs/ast@1.8.5": version "1.8.5" @@ -1422,9 +1525,9 @@ integrity sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ== abab@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/abab/-/abab-2.0.0.tgz#aba0ab4c5eee2d4c79d3487d85450fb2376ebb0f" - integrity sha512-sY5AXXVZv4Y1VACTtR11UJCPHHudgY5i26Qj5TypE6DKlIApbwb5uqhXcJ5UUGbvZNRh7EeIoW+LrJumBsKp7w== + version "2.0.2" + resolved "https://registry.yarnpkg.com/abab/-/abab-2.0.2.tgz#a2fba1b122c69a85caa02d10f9270c7219709a9d" + integrity sha512-2scffjvioEmNz0OyDSLGWDfKCVwaKc6l9Pm9kOIREU13ClXZvHpg/nRL5xyjSSSLhOnXqft2HpsAzNEEA8cFFg== abbrev@1: version "1.1.1" @@ -1440,17 +1543,17 @@ accepts@~1.3.4, accepts@~1.3.5, accepts@~1.3.7: negotiator "0.6.2" acorn-globals@^4.1.0, acorn-globals@^4.3.0: - version "4.3.3" - resolved "https://registry.yarnpkg.com/acorn-globals/-/acorn-globals-4.3.3.tgz#a86f75b69680b8780d30edd21eee4e0ea170c05e" - integrity sha512-vkR40VwS2SYO98AIeFvzWWh+xyc2qi9s7OoXSFEGIP/rOJKzjnhykaZJNnHdoq4BL2gGxI5EZOU16z896EYnOQ== + version "4.3.4" + resolved "https://registry.yarnpkg.com/acorn-globals/-/acorn-globals-4.3.4.tgz#9fa1926addc11c97308c4e66d7add0d40c3272e7" + integrity sha512-clfQEh21R+D0leSbUdWf3OcfqyaCSAQ8Ryq00bofSekfr9W8u1jyYZo6ir0xu9Gtcf7BjcHJpnbZH7JOCpP60A== dependencies: acorn "^6.0.1" acorn-walk "^6.0.1" -acorn-jsx@^5.0.2: - version "5.0.2" - resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.0.2.tgz#84b68ea44b373c4f8686023a551f61a21b7c4a4f" - integrity sha512-tiNTrP1MP0QrChmD2DdupCr6HWSFeKVw5d/dHTu4Y7rkAkRhU/Dt7dphAfIUyxtHpl/eBVip5uTNSpQJHylpAw== +acorn-jsx@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.1.0.tgz#294adb71b57398b0680015f0a38c563ee1db5384" + integrity sha512-tMUqwBWfLFbJbizRmEcWSLw6HnFzfdJs2sOJEOwwtVPMoH/0Ay+E703oZz78VSXZiiDcZrQ5XKjPIUQixhmgVw== acorn-walk@^6.0.1: version "6.2.0" @@ -1467,17 +1570,12 @@ acorn@^6.0.1, acorn@^6.0.4, acorn@^6.2.1: resolved "https://registry.yarnpkg.com/acorn/-/acorn-6.3.0.tgz#0087509119ffa4fc0a0041d1e93a417e68cb856e" integrity sha512-/czfa8BwS88b9gWQVhc8eknunSA2DoJpJyTQkhheIf5E48u1N0R4q/YxxsAeqRrmK9TQ/uYfgLDfZo91UlANIA== -acorn@^7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.0.0.tgz#26b8d1cd9a9b700350b71c0905546f64d1284e7a" - integrity sha512-PaF/MduxijYYt7unVGRuds1vBC9bFxbNf+VWqhOClfdgy7RlVkQqt610ig1/yxTgsDIfW1cWDel5EBbOy3jdtQ== - -address@1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/address/-/address-1.1.0.tgz#ef8e047847fcd2c5b6f50c16965f924fd99fe709" - integrity sha512-4diPfzWbLEIElVG4AnqP+00SULlPzNuyJFNnmMrLgyaxG6tZXJ1sn7mjBu4fHrJE+Yp/jgylOweJn2xsLMFggQ== +acorn@^7.1.0: + version "7.1.0" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.1.0.tgz#949d36f2c292535da602283586c2477c57eb2d6c" + integrity sha512-kL5CuoXA/dgxlBbVrflsflzQ3PAas7RYZB52NOm/6839iVYJgKMJ3cQJD+t2i5+qFa8h3MDpEOJiS64E8JLnSQ== -address@^1.0.1: +address@1.1.2, address@^1.0.1: version "1.1.2" resolved "https://registry.yarnpkg.com/address/-/address-1.1.2.tgz#bf1116c9c758c51b7a933d296b72c221ed9428b6" integrity sha512-aT6camzM4xEA54YVJYSqxz1kv4IHnQZRtThJJHhUMRExaU5spC7jX5ugSwTaTgJliIgs4VhZOk7htClvQ/LmRA== @@ -1493,7 +1591,7 @@ adjust-sourcemap-loader@2.0.0: object-path "0.11.4" regex-parser "2.2.10" -airbnb-prop-types@^2.13.2: +airbnb-prop-types@^2.15.0: version "2.15.0" resolved "https://registry.yarnpkg.com/airbnb-prop-types/-/airbnb-prop-types-2.15.0.tgz#5287820043af1eb469f5b0af0d6f70da6c52aaef" integrity sha512-jUh2/hfKsRjNFC4XONQrxo/n/3GG4Tn6Hl0WlFQN5PY9OMC9loSCoAYKnZsWaP8wEfd5xcrPloK0Zg6iS1xwVA== @@ -1544,6 +1642,13 @@ ansi-escapes@^3.0.0, ansi-escapes@^3.2.0: resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-3.2.0.tgz#8780b98ff9dbf5638152d1f1fe5c1d7b4442976b" integrity sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ== +ansi-escapes@^4.2.1: + version "4.2.1" + resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-4.2.1.tgz#4dccdb846c3eee10f6d64dea66273eab90c37228" + integrity sha512-Cg3ymMAdN10wOk/VYfLV7KCQyv7EDirJ64500sU7n9UlmioEtDuU5Gd+hj73hXSU/ex7tHJSssmyftDdkMLO8Q== + dependencies: + type-fest "^0.5.2" + ansi-html@0.0.7: version "0.0.7" resolved "https://registry.yarnpkg.com/ansi-html/-/ansi-html-0.0.7.tgz#813584021962a9e9e6fd039f940d12f56ca7859e" @@ -1584,6 +1689,14 @@ anymatch@^2.0.0: micromatch "^3.1.4" normalize-path "^2.1.1" +anymatch@~3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.1.tgz#c55ecf02185e2469259399310c173ce31233b142" + integrity sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg== + dependencies: + normalize-path "^3.0.0" + picomatch "^2.0.4" + aproba@^1.0.3, aproba@^1.1.1: version "1.2.0" resolved "https://registry.yarnpkg.com/aproba/-/aproba-1.2.0.tgz#6802e6264efd18c790a1b0d517f0f2627bf2c94a" @@ -1642,11 +1755,6 @@ array-filter@^1.0.0: resolved "https://registry.yarnpkg.com/array-filter/-/array-filter-1.0.0.tgz#baf79e62e6ef4c2a4c0b831232daffec251f9d83" integrity sha1-uveeYubvTCpMC4MSMtr/7CUfnYM= -array-filter@~0.0.0: - version "0.0.1" - resolved "https://registry.yarnpkg.com/array-filter/-/array-filter-0.0.1.tgz#7da8cf2e26628ed732803581fd21f67cacd2eeec" - integrity sha1-fajPLiZijtcygDWB/SH2fKzS7uw= - array-flatten@1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2" @@ -1665,16 +1773,6 @@ array-includes@^3.0.3: define-properties "^1.1.2" es-abstract "^1.7.0" -array-map@~0.0.0: - version "0.0.0" - resolved "https://registry.yarnpkg.com/array-map/-/array-map-0.0.0.tgz#88a2bab73d1cf7bcd5c1b118a003f66f665fa662" - integrity sha1-iKK6tz0c97zVwbEYoAP2b2ZfpmI= - -array-reduce@~0.0.0: - version "0.0.0" - resolved "https://registry.yarnpkg.com/array-reduce/-/array-reduce-0.0.0.tgz#173899d3ffd1c7d9383e4479525dbe278cab5f2b" - integrity sha1-FziZ0//Rx9k4PkR5Ul2+J4yrXys= - array-union@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/array-union/-/array-union-1.0.2.tgz#9a34410e4f4e3da23dea375be5be70f24778ec39" @@ -1701,12 +1799,12 @@ array.prototype.find@^2.1.0: es-abstract "^1.13.0" array.prototype.flat@^1.2.1: - version "1.2.1" - resolved "https://registry.yarnpkg.com/array.prototype.flat/-/array.prototype.flat-1.2.1.tgz#812db8f02cad24d3fab65dd67eabe3b8903494a4" - integrity sha512-rVqIs330nLJvfC7JqYvEWwqVr5QjYF1ib02i3YJtR/fICO6527Tjpc/e4Mvmxh3GIePPreRXMdaGyC99YphWEw== + version "1.2.2" + resolved "https://registry.yarnpkg.com/array.prototype.flat/-/array.prototype.flat-1.2.2.tgz#8f3c71d245ba349b6b64b4078f76f5576f1fd723" + integrity sha512-VXjh7lAL4KXKF2hY4FnEW9eRW6IhdvFW1sN/JwLbmECbCgACCnBHNyP3lFiYuttr0jxRN9Bsc5+G27dMseSWqQ== dependencies: - define-properties "^1.1.2" - es-abstract "^1.10.0" + define-properties "^1.1.3" + es-abstract "^1.15.0" function-bind "^1.1.1" arrify@^1.0.1: @@ -1780,10 +1878,12 @@ async-limiter@~1.0.0: resolved "https://registry.yarnpkg.com/async-limiter/-/async-limiter-1.0.1.tgz#dd379e94f0db8310b08291f9d64c3209766617fd" integrity sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ== -async@^1.5.2: - version "1.5.2" - resolved "https://registry.yarnpkg.com/async/-/async-1.5.2.tgz#ec6a61ae56480c0c3cb241c95618e20892f9672a" - integrity sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo= +async@^2.6.2: + version "2.6.3" + resolved "https://registry.yarnpkg.com/async/-/async-2.6.3.tgz#d72625e2344a3656e3a3ad4fa749fa83299d82ff" + integrity sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg== + dependencies: + lodash "^4.17.14" asynckit@^0.4.0: version "0.4.0" @@ -1809,17 +1909,17 @@ auth0-js@^9.8.2: winchan "^0.2.2" autoprefixer@^9.6.1: - version "9.6.1" - resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-9.6.1.tgz#51967a02d2d2300bb01866c1611ec8348d355a47" - integrity sha512-aVo5WxR3VyvyJxcJC3h4FKfwCQvQWb1tSI5VHNibddCVWrcD1NvlxEweg3TSgiPztMnWfjpy2FURKA2kvDE+Tw== + version "9.6.5" + resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-9.6.5.tgz#98f4afe7e93cccf323287515d426019619775e5e" + integrity sha512-rGd50YV8LgwFQ2WQp4XzOTG69u1qQsXn0amww7tjqV5jJuNazgFKYEVItEBngyyvVITKOg20zr2V+9VsrXJQ2g== dependencies: - browserslist "^4.6.3" - caniuse-lite "^1.0.30000980" + browserslist "^4.7.0" + caniuse-lite "^1.0.30000999" chalk "^2.4.2" normalize-range "^0.1.2" num2fraction "^1.2.2" - postcss "^7.0.17" - postcss-value-parser "^4.0.0" + postcss "^7.0.18" + postcss-value-parser "^4.0.2" aws-sign2@~0.7.0: version "0.7.0" @@ -1847,17 +1947,17 @@ babel-code-frame@^6.22.0: esutils "^2.0.2" js-tokens "^3.0.2" -babel-eslint@10.0.2: - version "10.0.2" - resolved "https://registry.yarnpkg.com/babel-eslint/-/babel-eslint-10.0.2.tgz#182d5ac204579ff0881684b040560fdcc1558456" - integrity sha512-UdsurWPtgiPgpJ06ryUnuaSXC2s0WoSZnQmEpbAH65XZSdwowgN5MvyP7e88nW07FYXv72erVtpBkxyDVKhH1Q== +babel-eslint@10.0.3: + version "10.0.3" + resolved "https://registry.yarnpkg.com/babel-eslint/-/babel-eslint-10.0.3.tgz#81a2c669be0f205e19462fed2482d33e4687a88a" + integrity sha512-z3U7eMY6r/3f3/JB9mTsLjyxrv0Yb1zb8PCWCLpguxfCzBIZUwy23R1t/XKewP+8mEN2Ck8Dtr4q20z6ce6SoA== dependencies: "@babel/code-frame" "^7.0.0" "@babel/parser" "^7.0.0" "@babel/traverse" "^7.0.0" "@babel/types" "^7.0.0" - eslint-scope "3.7.1" eslint-visitor-keys "^1.0.0" + resolve "^1.12.0" babel-extract-comments@^1.0.0: version "1.0.0" @@ -1866,7 +1966,7 @@ babel-extract-comments@^1.0.0: dependencies: babylon "^6.18.0" -babel-jest@^24.8.0, babel-jest@^24.9.0: +babel-jest@^24.9.0: version "24.9.0" resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-24.9.0.tgz#3fc327cb8467b89d14d7bc70e315104a783ccd54" integrity sha512-ntuddfyiN+EhMw58PTNL1ph4C9rECiQXjI4nMMBKBaNjXvqLdkXpPRcMSr4iyBrJg/+wz9brFUD6RhOAT6r4Iw== @@ -1922,10 +2022,10 @@ babel-plugin-macros@2.6.1: cosmiconfig "^5.2.0" resolve "^1.10.0" -babel-plugin-named-asset-import@^0.3.3: - version "0.3.3" - resolved "https://registry.yarnpkg.com/babel-plugin-named-asset-import/-/babel-plugin-named-asset-import-0.3.3.tgz#9ba2f3ac4dc78b042651654f07e847adfe50667c" - integrity sha512-1XDRysF4894BUdMChT+2HHbtJYiO7zx5Be7U6bT8dISy7OdyETMGIAQBMPQCsY1YRf0xcubwnKKaDr5bk15JTA== +babel-plugin-named-asset-import@^0.3.4: + version "0.3.4" + resolved "https://registry.yarnpkg.com/babel-plugin-named-asset-import/-/babel-plugin-named-asset-import-0.3.4.tgz#4a8fc30e9a3e2b1f5ed36883386ab2d84e1089bd" + integrity sha512-S6d+tEzc5Af1tKIMbsf2QirCcPdQ+mKUCY2H1nJj1DyA1ShwpsoxEOAwbWsG5gcXNV/olpvQd9vrUWRx4bnhpw== babel-plugin-syntax-object-rest-spread@^6.8.0: version "6.13.0" @@ -1953,24 +2053,24 @@ babel-preset-jest@^24.9.0: "@babel/plugin-syntax-object-rest-spread" "^7.0.0" babel-plugin-jest-hoist "^24.9.0" -babel-preset-react-app@^9.0.1: - version "9.0.1" - resolved "https://registry.yarnpkg.com/babel-preset-react-app/-/babel-preset-react-app-9.0.1.tgz#16a2cf84363045b530b6a03460527a5c6eac42ba" - integrity sha512-v7MeY+QxdBhM9oU5uOQCIHLsErYkEbbjctXsb10II+KAnttbe0rvprvP785dRxfa9dI4ZbsGXsRU07Qdi5BtOw== +babel-preset-react-app@^9.0.2: + version "9.0.2" + resolved "https://registry.yarnpkg.com/babel-preset-react-app/-/babel-preset-react-app-9.0.2.tgz#247d37e883d6d6f4b4691e5f23711bb2dd80567d" + integrity sha512-aXD+CTH8Chn8sNJr4tO/trWKqe5sSE4hdO76j9fhVezJSzmpWYWUSc5JoPmdSxADwef5kQFNGKXd433vvkd2VQ== dependencies: - "@babel/core" "7.5.5" + "@babel/core" "7.6.0" "@babel/plugin-proposal-class-properties" "7.5.5" - "@babel/plugin-proposal-decorators" "7.4.4" + "@babel/plugin-proposal-decorators" "7.6.0" "@babel/plugin-proposal-object-rest-spread" "7.5.5" "@babel/plugin-syntax-dynamic-import" "7.2.0" - "@babel/plugin-transform-destructuring" "7.5.0" + "@babel/plugin-transform-destructuring" "7.6.0" "@babel/plugin-transform-flow-strip-types" "7.4.4" "@babel/plugin-transform-react-display-name" "7.2.0" - "@babel/plugin-transform-runtime" "7.5.5" - "@babel/preset-env" "7.5.5" + "@babel/plugin-transform-runtime" "7.6.0" + "@babel/preset-env" "7.6.0" "@babel/preset-react" "7.0.0" - "@babel/preset-typescript" "7.3.3" - "@babel/runtime" "7.5.5" + "@babel/preset-typescript" "7.6.0" + "@babel/runtime" "7.6.0" babel-plugin-dynamic-import-node "2.3.0" babel-plugin-macros "2.6.1" babel-plugin-transform-react-remove-prop-types "0.4.24" @@ -2038,10 +2138,15 @@ binary-extensions@^1.0.0: resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-1.13.1.tgz#598afe54755b2868a5330d2aff9d4ebb53209b65" integrity sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw== +binary-extensions@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.0.0.tgz#23c0df14f6a88077f5f986c0d167ec03c3d5537c" + integrity sha512-Phlt0plgpIIBOGTT/ehfFnbNlfsDEiqmzE2KRXoX1bLIlir4X/MR+zSyBEkL05ffWgnRSf/DXv+WrUAVr93/ow== + bluebird@^3.5.5: - version "3.5.5" - resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.5.5.tgz#a8d0afd73251effbbd5fe384a77d73003c17a71f" - integrity sha512-5am6HnnfN+urzt4yfg7IgTbotDjIT/u8AJpEt0sIU9FtXfVeezXAPKswrG+xKUCOYAINpSdgZVDU6QFh+cuH3w== + version "3.7.1" + resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.7.1.tgz#df70e302b471d7473489acf26a93d63b53f874de" + integrity sha512-DdmyoGCleJnkbp3nkbxTLJ18rjDsE4yCggEwKNXkeV123sPNfOCYeDoeuOY+F2FrSjO1YXcTU+dsy96KMy+gcg== bn.js@^4.0.0, bn.js@^4.1.0, bn.js@^4.1.1, bn.js@^4.4.0: version "4.11.8" @@ -2110,6 +2215,13 @@ braces@^2.3.1, braces@^2.3.2: split-string "^3.0.2" to-regex "^3.0.1" +braces@~3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107" + integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A== + dependencies: + fill-range "^7.0.1" + brorand@^1.0.1: version "1.1.0" resolved "https://registry.yarnpkg.com/brorand/-/brorand-1.1.0.tgz#12c25efe40a45e3c323eb8675a0a0ce57b22371f" @@ -2186,14 +2298,23 @@ browserify-zlib@^0.2.0: dependencies: pako "~1.0.5" -browserslist@4.6.6, browserslist@^4.0.0, browserslist@^4.1.1, browserslist@^4.6.0, browserslist@^4.6.3, browserslist@^4.6.4, browserslist@^4.6.6: - version "4.6.6" - resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.6.6.tgz#6e4bf467cde520bc9dbdf3747dafa03531cec453" - integrity sha512-D2Nk3W9JL9Fp/gIcWei8LrERCS+eXu9AM5cfXA8WEZ84lFks+ARnZ0q/R69m2SV3Wjma83QDDPxsNKXUwdIsyA== +browserslist@4.7.0: + version "4.7.0" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.7.0.tgz#9ee89225ffc07db03409f2fee524dc8227458a17" + integrity sha512-9rGNDtnj+HaahxiVV38Gn8n8Lr8REKsel68v1sPFfIGEK6uSXTY3h9acgiT1dZVtOOUtifo/Dn8daDQ5dUgVsA== dependencies: - caniuse-lite "^1.0.30000984" - electron-to-chromium "^1.3.191" - node-releases "^1.1.25" + caniuse-lite "^1.0.30000989" + electron-to-chromium "^1.3.247" + node-releases "^1.1.29" + +browserslist@^4.0.0, browserslist@^4.1.1, browserslist@^4.6.0, browserslist@^4.6.4, browserslist@^4.7.0: + version "4.7.1" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.7.1.tgz#bd400d1aea56538580e8c4d5f1c54ac11b5ab468" + integrity sha512-QtULFqKIAtiyNx7NhZ/p4rB8m3xDozVo/pi5VgTlADLF2tNigz/QH+v0m5qhn7XfHT7u+607NcCNOnC0HZAlMg== + dependencies: + caniuse-lite "^1.0.30000999" + electron-to-chromium "^1.3.284" + node-releases "^1.1.36" bser@^2.0.0: version "2.1.0" @@ -2339,10 +2460,10 @@ caniuse-api@^3.0.0: lodash.memoize "^4.1.2" lodash.uniq "^4.5.0" -caniuse-lite@^1.0.0, caniuse-lite@^1.0.30000980, caniuse-lite@^1.0.30000981, caniuse-lite@^1.0.30000984: - version "1.0.30000989" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30000989.tgz#b9193e293ccf7e4426c5245134b8f2a56c0ac4b9" - integrity sha512-vrMcvSuMz16YY6GSVZ0dWDTJP8jqk3iFQ/Aq5iqblPwxSVVZI+zxDyTX0VPqtQsDnfdrBDcsmhgTEOh5R8Lbpw== +caniuse-lite@^1.0.0, caniuse-lite@^1.0.30000981, caniuse-lite@^1.0.30000989, caniuse-lite@^1.0.30000999: + version "1.0.30001002" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001002.tgz#ba999a737b1abd5bf0fd47efe43a09b9cadbe9b0" + integrity sha512-pRuxPE8wdrWmVPKcDmJJiGBxr6lFJq4ivdSeo9FTmGj5Rb8NX3Mby2pARG57MXF15hYAhZ0nHV5XxT2ig4bz3g== capture-exit@^2.0.0: version "2.0.0" @@ -2417,10 +2538,25 @@ chokidar@^2.0.0, chokidar@^2.0.2, chokidar@^2.0.4: optionalDependencies: fsevents "^1.2.7" +chokidar@^3.0.2: + version "3.2.2" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.2.2.tgz#a433973350021e09f2b853a2287781022c0dc935" + integrity sha512-bw3pm7kZ2Wa6+jQWYP/c7bAZy3i4GwiIiMO2EeRjrE48l8vBqC/WvFhSF0xyM8fQiPEGvwMY/5bqDG7sSEOuhg== + dependencies: + anymatch "~3.1.1" + braces "~3.0.2" + glob-parent "~5.1.0" + is-binary-path "~2.1.0" + is-glob "~4.0.1" + normalize-path "~3.0.0" + readdirp "~3.2.0" + optionalDependencies: + fsevents "~2.1.1" + chownr@^1.1.1: - version "1.1.2" - resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.1.2.tgz#a18f1e0b269c8a6a5d3c86eb298beb14c3dd7bf6" - integrity sha512-GkfeAQh+QNy3wquu9oIZr6SS5x7wGdSgNQvD10X3r+AZr1Oys22HW8kAmDMvNg2+Dm0TeGaEuO8gFwdBXxwO8A== + version "1.1.3" + resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.1.3.tgz#42d837d5239688d55f303003a508230fa6727142" + integrity sha512-i70fVHhmV3DtTl6nqvZOnIjbY0Pe4kAUjwHj8z0zAdgBtYrJyYwLKCCuRBQ5ppkyL0AkN7HKRnETdmdp1zqNXw== chrome-trace-event@^1.0.2: version "1.0.2" @@ -2591,10 +2727,10 @@ commander@2.17.x: resolved "https://registry.yarnpkg.com/commander/-/commander-2.17.1.tgz#bd77ab7de6de94205ceacc72f1716d29f20a77bf" integrity sha512-wPMUt6FnH2yzG95SA6mzjQOEKUU3aLaDEmzs1ti+1E9h+CsrZghRlqEM/EJ4KscsQVG8uNN4uVreUeT8+drlgg== -commander@^2.11.0, commander@^2.19.0, commander@^2.20.0, commander@~2.20.0: - version "2.20.0" - resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.0.tgz#d58bb2b5c1ee8f87b0d340027e9e94e222c5a422" - integrity sha512-7j2y+40w61zy6YC2iRNpUe/NwhNyoXrYpHMrSunaMG64nRnaf96zO/KMQR4OyN/UnE5KLyEBnKHd4aG3rskjpQ== +commander@^2.11.0, commander@^2.19.0, commander@^2.20.0, commander@~2.20.3: + version "2.20.3" + resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" + integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== commander@~2.19.0: version "2.19.0" @@ -2658,10 +2794,10 @@ concat-stream@^1.5.0: readable-stream "^2.2.2" typedarray "^0.0.6" -confusing-browser-globals@^1.0.8: - version "1.0.8" - resolved "https://registry.yarnpkg.com/confusing-browser-globals/-/confusing-browser-globals-1.0.8.tgz#93ffec1f82a6e2bf2bc36769cc3a92fa20e502f3" - integrity sha512-lI7asCibVJ6Qd3FGU7mu4sfG4try4LX3+GVS+Gv8UlrEf2AeW57piecapnog2UHZSbcX/P/1UDWVaTsblowlZg== +confusing-browser-globals@^1.0.9: + version "1.0.9" + resolved "https://registry.yarnpkg.com/confusing-browser-globals/-/confusing-browser-globals-1.0.9.tgz#72bc13b483c0276801681871d4898516f8f54fdd" + integrity sha512-KbS1Y0jMtyPgIxjO7ZzMAuUpAKMt1SzCL9fsrKsX6b0zJPTaT0SiSPmewwVZg9UAO83HVIlEhZF84LIjZ0lmAw== connect-history-api-fallback@^1.3.0: version "1.6.0" @@ -2747,17 +2883,17 @@ copy-descriptor@^0.1.0: integrity sha1-Z29us8OZl8LuGsOpJP1hJHSPV40= core-js-compat@^3.1.1: - version "3.2.1" - resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.2.1.tgz#0cbdbc2e386e8e00d3b85dc81c848effec5b8150" - integrity sha512-MwPZle5CF9dEaMYdDeWm73ao/IflDH+FjeJCWEADcEgFSE9TLimFKwJsfmkwzI8eC0Aj0mgvMDjeQjrElkz4/A== + version "3.3.2" + resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.3.2.tgz#1096c989c1b929ede06b5b6b4768dc4439078c03" + integrity sha512-gfiK4QnNXhnnHVOIZst2XHdFfdMTPxtR0EGs0TdILMlGIft+087oH6/Sw2xTTIjpWXC9vEwsJA8VG3XTGcmO5g== dependencies: - browserslist "^4.6.6" + browserslist "^4.7.0" semver "^6.3.0" -core-js@3.1.4: - version "3.1.4" - resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.1.4.tgz#3a2837fc48e582e1ae25907afcd6cf03b0cc7a07" - integrity sha512-YNZN8lt82XIMLnLirj9MhKDFZHalwzzrL9YLt6eb0T5D0EDl4IQ90IGkua8mHbnxNrkj1d8hbdizMc0Qmg1WnQ== +core-js@3.2.1: + version "3.2.1" + resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.2.1.tgz#cd41f38534da6cc59f7db050fe67307de9868b09" + integrity sha512-Qa5XSVefSVPRxy2XfUC13WbvqkxhkwB3ve+pgCQveNgYzbM/UxZeu1dcOX/xr4UmfUd+muuvsaxilQzCyUurMw== core-js@^1.0.0: version "1.2.7" @@ -2765,9 +2901,9 @@ core-js@^1.0.0: integrity sha1-ZSKUwUZR2yj6k70tX/KYOk8IxjY= core-js@^2.4.0: - version "2.6.9" - resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.6.9.tgz#6b4b214620c834152e179323727fc19741b084f2" - integrity sha512-HOpZf6eXmnl7la+cUdMnLvUxKNqLUzJvgIziQ0DiF3JwSImNphIqdGqzj6hIKyX04MmV0poclQ7+wjWvxQyR2A== + version "2.6.10" + resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.6.10.tgz#8a5b8391f8cc7013da703411ce5b585706300d7f" + integrity sha512-I39t74+4t+zau64EN1fE5v2W31Adtc/REhzWN+gWRRXg6WH5qAsZm62DHpQ1+Yhe4047T55jvzz7MUqF/dBBlA== core-util-is@1.0.2, core-util-is@~1.0.0: version "1.0.2" @@ -3071,12 +3207,12 @@ cssstyle@^1.0.0, cssstyle@^1.1.1: dependencies: cssom "0.3.x" -cyclist@~0.2.2: - version "0.2.2" - resolved "https://registry.yarnpkg.com/cyclist/-/cyclist-0.2.2.tgz#1b33792e11e914a2fd6d6ed6447464444e5fa640" - integrity sha1-GzN5LhHpFKL9bW7WRHRkRE5fpkA= +cyclist@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/cyclist/-/cyclist-1.0.1.tgz#596e9698fd0c80e12038c2b82d6eb1b35b6224d9" + integrity sha1-WW6WmP0MgOEgOMK4LW6xs1tiJNk= -d@1: +d@1, d@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/d/-/d-1.0.1.tgz#8698095372d58dbee346ffd0c7093f99f8f9eb5a" integrity sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA== @@ -3117,7 +3253,7 @@ debug@2.6.9, debug@^2.2.0, debug@^2.3.3, debug@^2.6.0, debug@^2.6.8, debug@^2.6. dependencies: ms "2.0.0" -debug@^3.0.0, debug@^3.1.0, debug@^3.2.5, debug@^3.2.6: +debug@^3.0.0, debug@^3.1.0, debug@^3.1.1, debug@^3.2.5, debug@^3.2.6: version "3.2.6" resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.6.tgz#e83d17de16d8a7efb7717edbe5fb10135eee629b" integrity sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ== @@ -3433,10 +3569,10 @@ dot-prop@^4.1.1: dependencies: is-obj "^1.0.0" -dotenv-expand@4.2.0: - version "4.2.0" - resolved "https://registry.yarnpkg.com/dotenv-expand/-/dotenv-expand-4.2.0.tgz#def1f1ca5d6059d24a766e587942c21106ce1275" - integrity sha1-3vHxyl1gWdJKdm5YeULCEQbOEnU= +dotenv-expand@5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/dotenv-expand/-/dotenv-expand-5.1.0.tgz#3fbaf020bfd794884072ea26b1e9791d45a629f0" + integrity sha512-YXQl1DSa4/PQyRfgrv6aoNjhasp/p4qs9FjJ4q4cQk+8m4r6k4ZSiEyytKG8f8W9gi8WsQtIObNmKd+tMzNTmA== dotenv@6.2.0: version "6.2.0" @@ -3471,15 +3607,15 @@ ee-first@1.1.1: resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" integrity sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0= -electron-to-chromium@^1.3.191: - version "1.3.243" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.243.tgz#32f64f00fa121532d1d49f5c0a15fd77f52ae889" - integrity sha512-+edFdHGxLSmAKftXa5xZIg19rHkkJLiW+tRu0VMVG3RKztyeKX7d3pXf707lS6+BxB9uBun3RShbxCI1PtBAgQ== +electron-to-chromium@^1.3.247, electron-to-chromium@^1.3.284: + version "1.3.289" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.289.tgz#1f85add5d7086ce95d9361348c26aa9de5779906" + integrity sha512-39GEOWgTxtMDk/WjIQLg4W/l1s4FZdiMCqUBLjd92tAXsBPDFLwuwCba5OGhuTdVYm6E128TZIqSnMpeocUlCQ== elliptic@^6.0.0: - version "6.5.0" - resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.5.0.tgz#2b8ed4c891b7de3200e14412a5b8248c7af505ca" - integrity sha512-eFOJTMyCYb7xtE/caJ6JJu+bhi67WCYNbkGSknu20pmM8Ke/bqOfdnZWxyoGN26JgfxTbXrsCkEw4KheCT/KGg== + version "6.5.1" + resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.5.1.tgz#c380f5f909bf1b9b4428d028cd18d3b0efd6b52b" + integrity sha512-xvJINNLbTeWQjrl6X+7eQCrIy/YPv5XCpKW6kB5mKvtnGILoLDcySuwomfdzt0BMdLNVnuRNTuzKNHj0bva1Cg== dependencies: bn.js "^4.4.0" brorand "^1.0.1" @@ -3512,19 +3648,19 @@ encoding@^0.1.11: iconv-lite "~0.4.13" end-of-stream@^1.0.0, end-of-stream@^1.1.0: - version "1.4.1" - resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.1.tgz#ed29634d19baba463b6ce6b80a37213eab71ec43" - integrity sha512-1MkrZNvWTKCaigbn+W15elq2BB/L22nqrSY5DKlo3X6+vclJm8Bb5djXJBmEX6fS3+zCh/F4VBK5Z2KxJt4s2Q== + version "1.4.4" + resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0" + integrity sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q== dependencies: once "^1.4.0" enhanced-resolve@^4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-4.1.0.tgz#41c7e0bfdfe74ac1ffe1e57ad6a5c6c9f3742a7f" - integrity sha512-F/7vkyTtyc/llOIn8oWclcB25KdRaiPBpZYDgJHgh/UHtpgT2p2eldQgtQnLtUvfMKPKxbRaQM/hHkvLHt1Vng== + version "4.1.1" + resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-4.1.1.tgz#2937e2b8066cd0fe7ce0990a98f0d71a35189f66" + integrity sha512-98p2zE+rL7/g/DzMHMTF4zZlCgeVdJ7yr6xzEpJRYwFYrGi9ANdn5DnJURg6RpBkyk60XYDnWIv51VfIhfNGuA== dependencies: graceful-fs "^4.1.2" - memory-fs "^0.4.0" + memory-fs "^0.5.0" tapable "^1.0.0" entities@^1.1.1, entities@~1.1.1: @@ -3537,46 +3673,55 @@ entities@^2.0.0: resolved "https://registry.yarnpkg.com/entities/-/entities-2.0.0.tgz#68d6084cab1b079767540d80e56a39b423e4abf4" integrity sha512-D9f7V0JSRwIxlRI2mjMqufDrRDnx8p+eEOz7aUM9SuvF8gsBzra0/6tbjl1m8eQHrZlYj6PxqE00hZ1SAIKPLw== -enzyme-adapter-react-16@^1.14.0: - version "1.14.0" - resolved "https://registry.yarnpkg.com/enzyme-adapter-react-16/-/enzyme-adapter-react-16-1.14.0.tgz#204722b769172bcf096cb250d33e6795c1f1858f" - integrity sha512-7PcOF7pb4hJUvjY7oAuPGpq3BmlCig3kxXGi2kFx0YzJHppqX1K8IIV9skT1IirxXlu8W7bneKi+oQ10QRnhcA== +enzyme-adapter-react-16@^1.15.1: + version "1.15.1" + resolved "https://registry.yarnpkg.com/enzyme-adapter-react-16/-/enzyme-adapter-react-16-1.15.1.tgz#8ad55332be7091dc53a25d7d38b3485fc2ba50d5" + integrity sha512-yMPxrP3vjJP+4wL/qqfkT6JAIctcwKF+zXO6utlGPgUJT2l4tzrdjMDWGd/Pp1BjHBcljhN24OzNEGRteibJhA== dependencies: - enzyme-adapter-utils "^1.12.0" + enzyme-adapter-utils "^1.12.1" + enzyme-shallow-equal "^1.0.0" has "^1.0.3" object.assign "^4.1.0" object.values "^1.1.0" prop-types "^15.7.2" - react-is "^16.8.6" + react-is "^16.10.2" react-test-renderer "^16.0.0-0" semver "^5.7.0" -enzyme-adapter-utils@^1.12.0: - version "1.12.0" - resolved "https://registry.yarnpkg.com/enzyme-adapter-utils/-/enzyme-adapter-utils-1.12.0.tgz#96e3730d76b872f593e54ce1c51fa3a451422d93" - integrity sha512-wkZvE0VxcFx/8ZsBw0iAbk3gR1d9hK447ebnSYBf95+r32ezBq+XDSAvRErkc4LZosgH8J7et7H7/7CtUuQfBA== +enzyme-adapter-utils@^1.12.1: + version "1.12.1" + resolved "https://registry.yarnpkg.com/enzyme-adapter-utils/-/enzyme-adapter-utils-1.12.1.tgz#e828e0d038e2b1efa4b9619ce896226f85c9dd88" + integrity sha512-KWiHzSjZaLEoDCOxY8Z1RAbUResbqKN5bZvenPbfKtWorJFVETUw754ebkuCQ3JKm0adx1kF8JaiR+PHPiP47g== dependencies: - airbnb-prop-types "^2.13.2" - function.prototype.name "^1.1.0" + airbnb-prop-types "^2.15.0" + function.prototype.name "^1.1.1" object.assign "^4.1.0" - object.fromentries "^2.0.0" + object.fromentries "^2.0.1" prop-types "^15.7.2" - semver "^5.6.0" + semver "^5.7.0" -enzyme-matchers@^7.1.0: - version "7.1.0" - resolved "https://registry.yarnpkg.com/enzyme-matchers/-/enzyme-matchers-7.1.0.tgz#7224298028679af41d224fdff02b9a9a61f2ad86" - integrity sha512-PCfIvyNnZh4ougBaMXKeqNrN6yilzkbtphpi2X74uFeqbPabDUcvNq30Bcw29sSIv7QI1ZCTlQ2ktGep/4jIBw== +enzyme-matchers@^7.1.1: + version "7.1.1" + resolved "https://registry.yarnpkg.com/enzyme-matchers/-/enzyme-matchers-7.1.1.tgz#d1210ce0bd55b55d61af1ff72777bd8b1fa5176e" + integrity sha512-fw/FxwpEg6n1KYpEHnhA44iFduYHDUVVePXSMmf883q/JDMXb+sIU55maSw2oFWqt9zd7rcGqmSV8sHQO5pReg== dependencies: circular-json-es6 "^2.0.1" deep-equal-ident "^1.1.1" +enzyme-shallow-equal@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/enzyme-shallow-equal/-/enzyme-shallow-equal-1.0.0.tgz#d8e4603495e6ea279038eef05a4bf4887b55dc69" + integrity sha512-VUf+q5o1EIv2ZaloNQQtWCJM9gpeux6vudGVH6vLmfPXFLRuxl5+Aq3U260wof9nn0b0i+P5OEUXm1vnxkRpXQ== + dependencies: + has "^1.0.3" + object-is "^1.0.1" + enzyme-to-json@^3.3.0: - version "3.4.0" - resolved "https://registry.yarnpkg.com/enzyme-to-json/-/enzyme-to-json-3.4.0.tgz#2b6330a784a57ba68298e3c0d6cef17ee4fedc0e" - integrity sha512-gbu8P8PMAtb+qtKuGVRdZIYxWHC03q1dGS3EKRmUzmTDIracu3o6cQ0d4xI2YWojbelbxjYOsmqM5EgAL0WgIA== + version "3.4.3" + resolved "https://registry.yarnpkg.com/enzyme-to-json/-/enzyme-to-json-3.4.3.tgz#ed4386f48768ed29e2d1a2910893542c34e7e0af" + integrity sha512-jqNEZlHqLdz7OTpXSzzghArSS3vigj67IU/fWkPyl1c0TCj9P5s6Ze0kRkYZWNEoCqCR79xlQbigYlMx5erh8A== dependencies: - lodash "^4.17.12" + lodash "^4.17.15" enzyme@^3.10.0: version "3.10.0" @@ -3619,17 +3764,21 @@ error-ex@^1.2.0, error-ex@^1.3.1: dependencies: is-arrayish "^0.2.1" -es-abstract@^1.10.0, es-abstract@^1.11.0, es-abstract@^1.12.0, es-abstract@^1.13.0, es-abstract@^1.5.1, es-abstract@^1.7.0: - version "1.13.0" - resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.13.0.tgz#ac86145fdd5099d8dd49558ccba2eaf9b88e24e9" - integrity sha512-vDZfg/ykNxQVwup/8E1BZhVzFfBxs9NqMzGcvIJrqg5k2/5Za2bWo40dK2J1pgLngZ7c+Shh8lwYtLGyrwPutg== +es-abstract@^1.12.0, es-abstract@^1.13.0, es-abstract@^1.15.0, es-abstract@^1.5.1, es-abstract@^1.7.0: + version "1.16.0" + resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.16.0.tgz#d3a26dc9c3283ac9750dca569586e976d9dcc06d" + integrity sha512-xdQnfykZ9JMEiasTAJZJdMWCQ1Vm00NBw79/AWi7ELfZuuPCSOMDZbT9mkOfSctVtfhb+sAAzrm+j//GjjLHLg== dependencies: es-to-primitive "^1.2.0" function-bind "^1.1.1" has "^1.0.3" + has-symbols "^1.0.0" is-callable "^1.1.4" is-regex "^1.0.4" - object-keys "^1.0.12" + object-inspect "^1.6.0" + object-keys "^1.1.1" + string.prototype.trimleft "^2.1.0" + string.prototype.trimright "^2.1.0" es-to-primitive@^1.2.0: version "1.2.0" @@ -3640,10 +3789,10 @@ es-to-primitive@^1.2.0: is-date-object "^1.0.1" is-symbol "^1.0.2" -es5-ext@^0.10.35, es5-ext@^0.10.50, es5-ext@~0.10.14: - version "0.10.50" - resolved "https://registry.yarnpkg.com/es5-ext/-/es5-ext-0.10.50.tgz#6d0e23a0abdb27018e5ac4fd09b412bc5517a778" - integrity sha512-KMzZTPBkeQV/JcSQhI5/z6d9VWJ3EnQ194USTUwIYZ2ZbpN8+SGXQKt1h68EX44+qt+Fzr8DO17vnxrw7c3agw== +es5-ext@^0.10.35, es5-ext@^0.10.50, es5-ext@^0.10.51: + version "0.10.51" + resolved "https://registry.yarnpkg.com/es5-ext/-/es5-ext-0.10.51.tgz#ed2d7d9d48a12df86e0299287e93a09ff478842f" + integrity sha512-oRpWzM2WcLHVKpnrcyB7OW8j/s67Ba04JCm0WnNv3RiABSvs7mrQlutB8DBv793gKcp0XENR8Il8WxGTlZ73gQ== dependencies: es6-iterator "~2.0.3" es6-symbol "~3.1.1" @@ -3659,12 +3808,12 @@ es6-iterator@2.0.3, es6-iterator@~2.0.3: es6-symbol "^3.1.1" es6-symbol@^3.1.1, es6-symbol@~3.1.1: - version "3.1.1" - resolved "https://registry.yarnpkg.com/es6-symbol/-/es6-symbol-3.1.1.tgz#bf00ef4fdab6ba1b46ecb7b629b4c7ed5715cc77" - integrity sha1-vwDvT9q2uhtG7Le2KbTH7VcVzHc= + version "3.1.2" + resolved "https://registry.yarnpkg.com/es6-symbol/-/es6-symbol-3.1.2.tgz#859fdd34f32e905ff06d752e7171ddd4444a7ed1" + integrity sha512-/ZypxQsArlv+KHpGvng52/Iz8by3EQPxhmbuz8yFG89N/caTFBSbcXONDw0aMjy827gQg26XAjP4uXFvnfINmQ== dependencies: - d "1" - es5-ext "~0.10.14" + d "^1.0.1" + es5-ext "^0.10.51" escape-html@~1.0.3: version "1.0.3" @@ -3688,12 +3837,12 @@ escodegen@^1.11.0, escodegen@^1.9.1: optionalDependencies: source-map "~0.6.1" -eslint-config-react-app@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/eslint-config-react-app/-/eslint-config-react-app-5.0.1.tgz#5f3d666ba3ee3cb384eb943e260e868f6c72251b" - integrity sha512-GYXP3F/0PSHlYfGHhahqnJze8rYKxzXgrzXVqRRd4rDO40ga4NA3aHM7/HKbwceDN0/C1Ij3BoAWFawJgRbXEw== +eslint-config-react-app@^5.0.2: + version "5.0.2" + resolved "https://registry.yarnpkg.com/eslint-config-react-app/-/eslint-config-react-app-5.0.2.tgz#df40d73a1402986030680c040bbee520db5a32a4" + integrity sha512-VhlESAQM83uULJ9jsvcKxx2Ab0yrmjUt8kDz5DyhTQufqWE0ssAnejlWri5LXv25xoXfdqOyeDPdfJS9dXKagQ== dependencies: - confusing-browser-globals "^1.0.8" + confusing-browser-globals "^1.0.9" eslint-import-resolver-node@^0.3.2: version "0.3.2" @@ -3703,16 +3852,16 @@ eslint-import-resolver-node@^0.3.2: debug "^2.6.9" resolve "^1.5.0" -eslint-loader@2.2.1: - version "2.2.1" - resolved "https://registry.yarnpkg.com/eslint-loader/-/eslint-loader-2.2.1.tgz#28b9c12da54057af0845e2a6112701a2f6bf8337" - integrity sha512-RLgV9hoCVsMLvOxCuNjdqOrUqIj9oJg8hF44vzJaYqsAHuY9G2YAeN3joQ9nxP0p5Th9iFSIpKo+SD8KISxXRg== +eslint-loader@3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/eslint-loader/-/eslint-loader-3.0.2.tgz#5a627316a51d6f41d357b9f6f0554e91506cdd6e" + integrity sha512-S5VnD+UpVY1PyYRqeBd/4pgsmkvSokbHqTXAQMpvCyRr3XN2tvSLo9spm2nEpqQqh9dezw3os/0zWihLeOg2Rw== dependencies: - loader-fs-cache "^1.0.0" - loader-utils "^1.0.2" - object-assign "^4.0.1" - object-hash "^1.1.4" - rimraf "^2.6.1" + fs-extra "^8.1.0" + loader-fs-cache "^1.0.2" + loader-utils "^1.2.3" + object-hash "^1.3.1" + schema-utils "^2.2.0" eslint-module-utils@^2.4.0: version "2.4.1" @@ -3781,15 +3930,7 @@ eslint-plugin-react@7.14.3: prop-types "^15.7.2" resolve "^1.10.1" -eslint-scope@3.7.1: - version "3.7.1" - resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-3.7.1.tgz#3d63c3edfda02e06e01a452ad88caacc7cdcb6e8" - integrity sha1-PWPD7f2gLgbgGkUq2IyqzHzctug= - dependencies: - esrecurse "^4.1.0" - estraverse "^4.1.1" - -eslint-scope@^4.0.0, eslint-scope@^4.0.3: +eslint-scope@^4.0.3: version "4.0.3" resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-4.0.3.tgz#ca03833310f6889a3264781aa82e63eb9cfe7848" integrity sha512-p7VutNr1O/QrxysMo3E45FjYDTeXBy0iTltPFNSqKAIfjDSXC+4dj+qfyuD8bfAXrW/y6lW3O76VaYNPKfpKrg== @@ -3805,12 +3946,12 @@ eslint-scope@^5.0.0: esrecurse "^4.1.0" estraverse "^4.1.1" -eslint-utils@^1.3.1, eslint-utils@^1.4.2: - version "1.4.2" - resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-1.4.2.tgz#166a5180ef6ab7eb462f162fd0e6f2463d7309ab" - integrity sha512-eAZS2sEUMlIeCjBeubdj45dmBHQwPHWyBcT1VSYB7o9x9WRRqKxyUoiXlRjyAwzN7YEzHJlYg0NmzDRWx6GP4Q== +eslint-utils@^1.4.2: + version "1.4.3" + resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-1.4.3.tgz#74fec7c54d0776b6f67e0251040b5806564e981f" + integrity sha512-fbBN5W2xdY45KulGXmLHZ3c3FHfVYmKg0IrAKGOkT/464PQsx2UeIzfz1RmEci+KLm1bBaAzZAh8+/E+XAeZ8Q== dependencies: - eslint-visitor-keys "^1.0.0" + eslint-visitor-keys "^1.1.0" eslint-visitor-keys@^1.0.0, eslint-visitor-keys@^1.1.0: version "1.1.0" @@ -3818,9 +3959,9 @@ eslint-visitor-keys@^1.0.0, eslint-visitor-keys@^1.1.0: integrity sha512-8y9YjtM1JBJU/A9Kc+SbaOV4y29sSWckBwMHa+FGtVj5gN/sbnKDf6xJUl+8g7FAij9LVaP8C24DUiH/f/2Z9A== eslint@^6.1.0: - version "6.2.2" - resolved "https://registry.yarnpkg.com/eslint/-/eslint-6.2.2.tgz#03298280e7750d81fcd31431f3d333e43d93f24f" - integrity sha512-mf0elOkxHbdyGX1IJEUsNBzCDdyoUgljF3rRlgfyYh0pwGnreLc0jjD6ZuleOibjmnUWZLY2eXwSooeOgGJ2jw== + version "6.5.1" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-6.5.1.tgz#828e4c469697d43bb586144be152198b91e96ed6" + integrity sha512-32h99BoLYStT1iq1v2P9uwpyznQ4M2jRiFB6acitKz52Gqn+vPaMDUTB1bYi1WN4Nquj2w+t+bimYUG83DC55A== dependencies: "@babel/code-frame" "^7.0.0" ajv "^6.10.0" @@ -3861,12 +4002,12 @@ eslint@^6.1.0: v8-compile-cache "^2.0.3" espree@^6.1.1: - version "6.1.1" - resolved "https://registry.yarnpkg.com/espree/-/espree-6.1.1.tgz#7f80e5f7257fc47db450022d723e356daeb1e5de" - integrity sha512-EYbr8XZUhWbYCqQRW0duU5LxzL5bETN6AjKBGy1302qqzPaCH10QbRg3Wvco79Z8x9WbiE8HYB4e75xl6qUYvQ== + version "6.1.2" + resolved "https://registry.yarnpkg.com/espree/-/espree-6.1.2.tgz#6c272650932b4f91c3714e5e7b5f5e2ecf47262d" + integrity sha512-2iUPuuPP+yW1PZaMSDM9eyVf8D5P0Hi8h83YtZ5bPc/zHYjII5khoixIUTMO794NOY8F/ThF1Bo8ncZILarUTA== dependencies: - acorn "^7.0.0" - acorn-jsx "^5.0.2" + acorn "^7.1.0" + acorn-jsx "^5.1.0" eslint-visitor-keys "^1.1.0" esprima@^3.1.3: @@ -3908,10 +4049,10 @@ etag@~1.8.1: resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" integrity sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc= -eventemitter3@^3.0.0: - version "3.1.2" - resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-3.1.2.tgz#2d3d48f9c346698fce83a85d7d664e98535df6e7" - integrity sha512-tvtQIeLVHjDkJYnzf2dgVMxfuSGJeM/7UCG17TT4EumTfNtF+0nebF/4zWOIkCreAbtNqhGEboB6BWrwqNaw4Q== +eventemitter3@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.0.tgz#d65176163887ee59f386d64c82610b696a4a74eb" + integrity sha512-qerSRB0p+UDEssxTtm6EDKcE7W4OaoisfIMl4CngyEhjpYglocpNg6UEqCvemdGhosAsg4sO2dXJOdyBifPGCg== events@^3.0.0: version "3.0.0" @@ -4180,6 +4321,13 @@ fill-range@^4.0.0: repeat-string "^1.6.1" to-regex-range "^2.1.0" +fill-range@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40" + integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ== + dependencies: + to-regex-range "^5.0.1" + finalhandler@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.1.2.tgz#b7e7d000ffd11938d0fdb053506f6ebabe9f587d" @@ -4269,9 +4417,9 @@ flux@^3.1.3: fbjs "^0.8.0" follow-redirects@^1.0.0: - version "1.8.1" - resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.8.1.tgz#24804f9eaab67160b0e840c085885d606371a35b" - integrity sha512-micCIbldHioIegeKs41DoH0KS3AXfFzgS30qVkM6z/XOE/GJgvmsoc839NUqa1B9udYe9dQxgv7KFwng6+p/dw== + version "1.9.0" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.9.0.tgz#8d5bcdc65b7108fe1508649c79c12d732dcedb4f" + integrity sha512-CRcPzsSIbXyVDl0QI01muNDu69S8trU4jArW9LpOt2WtC6LyUJetcIrmfHsRBx7/Jb6GHJUiuqyYxPooFfNt6A== dependencies: debug "^3.0.0" @@ -4312,9 +4460,9 @@ fork-ts-checker-webpack-plugin@1.5.0: worker-rpc "^0.1.0" form-data@^2.3.1: - version "2.5.0" - resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.5.0.tgz#094ec359dc4b55e7d62e0db4acd76e89fe874d37" - integrity sha512-WXieX3G/8side6VIqx44ablyULoGruSde5PNTxoUyo5CeyAMX6nVWUd0rgist/EuX655cjhUhTo1Fo3tRYqbcA== + version "2.5.1" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.5.1.tgz#f2cbec57b5e59e23716e128fe44d4e5dd23895f4" + integrity sha512-m21N3WOmEEURgk6B9GLOE4RuWOFf28Lhh9qGYeNlGq4VDXUlJy2th2slBNU8Gp8EzloYZOibZJ7t5ecIrFSjVA== dependencies: asynckit "^0.4.0" combined-stream "^1.0.6" @@ -4377,12 +4525,21 @@ fs-extra@^4.0.2: jsonfile "^4.0.0" universalify "^0.1.0" +fs-extra@^8.1.0: + version "8.1.0" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-8.1.0.tgz#49d43c45a88cd9677668cb7be1b46efdb8d2e1c0" + integrity sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g== + dependencies: + graceful-fs "^4.2.0" + jsonfile "^4.0.0" + universalify "^0.1.0" + fs-minipass@^1.2.5: - version "1.2.6" - resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-1.2.6.tgz#2c5cc30ded81282bfe8a0d7c7c1853ddeb102c07" - integrity sha512-crhvyXcMejjv3Z5d2Fa9sf5xLYVCF5O1c71QxbVnbLsmYMBEvDAftewesN/HhY03YRoA7zOMxjNGrF5svGaaeQ== + version "1.2.7" + resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-1.2.7.tgz#ccff8570841e7fe4265693da88936c55aed7f7c7" + integrity sha512-GWSSJGFy4e9GUeCcbIkED+bgAoFyj7XF1mV8rma3QW4NIqX9Kyx79N/PF61H5udOV3aY1IaMLs6pGbH71nlCTA== dependencies: - minipass "^2.2.1" + minipass "^2.6.0" fs-write-stream-atomic@^1.0.8: version "1.0.10" @@ -4412,6 +4569,11 @@ fsevents@^1.2.7: nan "^2.12.1" node-pre-gyp "^0.12.0" +fsevents@~2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.1.1.tgz#74c64e21df71721845d0c44fe54b7f56b82995a9" + integrity sha512-4FRPXWETxtigtJW/gxzEDsX1LVbPAM93VleB83kZB+ellqbHMkyt2aJfuzNLRvFPnGi6bcE5SvfxgbXPeKteJw== + function-bind@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" @@ -4433,9 +4595,9 @@ functional-red-black-tree@^1.0.1: integrity sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc= functions-have-names@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/functions-have-names/-/functions-have-names-1.1.1.tgz#79d35927f07b8e7103d819fed475b64ccf7225ea" - integrity sha512-U0kNHUoxwPNPWOJaMG7Z00d4a/qZVrFtzWJRaK8V9goaVOCXBSQSJpt3MYGNtkScKEBKovxLjnNdC9MlXwo5Pw== + version "1.2.0" + resolved "https://registry.yarnpkg.com/functions-have-names/-/functions-have-names-1.2.0.tgz#83da7583e4ea0c9ac5ff530f73394b033e0bf77d" + integrity sha512-zKXyzksTeaCSw5wIX79iCA40YAa6CJMJgNg9wdkU/ERBrIdPSimPICYiLp65lRbSBqtiHql/HZfS2DyI/AH6tQ== gauge@~2.7.3: version "2.7.4" @@ -4462,9 +4624,9 @@ get-caller-file@^2.0.1: integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== get-own-enumerable-property-symbols@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/get-own-enumerable-property-symbols/-/get-own-enumerable-property-symbols-3.0.0.tgz#b877b49a5c16aefac3655f2ed2ea5b684df8d203" - integrity sha512-CIJYJC4GGF06TakLg8z4GQKvDsx9EMspVxOYih7LerEL/WosUnFIww45CGfxfeKHqlg3twgUrYRT1O3WQqjGCg== + version "3.0.1" + resolved "https://registry.yarnpkg.com/get-own-enumerable-property-symbols/-/get-own-enumerable-property-symbols-3.0.1.tgz#6f7764f88ea11e0b514bd9bd860a132259992ca4" + integrity sha512-09/VS4iek66Dh2bctjRkowueRJbY1JDGR1L/zRxO1Qk8Uxs6PnqaNSqalpizPT+CDjre3hnEsuzvhgomz9qYrA== get-stream@^4.0.0: version "4.1.0" @@ -4493,10 +4655,10 @@ glob-parent@^3.1.0: is-glob "^3.1.0" path-dirname "^1.0.0" -glob-parent@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.0.0.tgz#1dc99f0f39b006d3e92c2c284068382f0c20e954" - integrity sha512-Z2RwiujPRGluePM6j699ktJYxmPpJKCfpGA13jz2hmFZC7gKetzrWvg5KN3+OsIFmydGyZ1AVwERCq1w/ZZwRg== +glob-parent@^5.0.0, glob-parent@~5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.0.tgz#5f4c1d1e748d30cd73ad2944b3577a81b081e8c2" + integrity sha512-qjtRgnIVmOfnKUE3NJAQEdk+lKrxfw8t5ke7SXtfMTHcjsBfOfWXCQfdb30zfDoZQ2IRSIiidmjtbHZPZ++Ihw== dependencies: is-glob "^4.0.1" @@ -4562,7 +4724,7 @@ globby@^6.1.0: pify "^2.0.0" pinkie-promise "^2.0.0" -graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1.2, graceful-fs@^4.1.6: +graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0: version "4.2.2" resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.2.tgz#6f0952605d0140c1cfdb138ed005775b92d67b02" integrity sha512-IItsdsea19BoLC7ELy13q1iJFNmd7ofZH5+X/pJr90/nRoPEX0DJo1dHDbgtYWOhJhcCgMDTOw84RZ72q6lB+Q== @@ -4591,9 +4753,9 @@ handle-thing@^2.0.0: integrity sha512-d4sze1JNC454Wdo2fkuyzCr6aHcbL6PGGuFAz0Li/NcOm1tCHGnWDRmJP85dh9IhQErTc2svWFEX5xHIOo//kQ== handlebars@^4.1.2: - version "4.1.2" - resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.1.2.tgz#b6b37c1ced0306b221e094fc7aca3ec23b131b67" - integrity sha512-nvfrjqvt9xQ8Z/w0ijewdD/vvWDTOweBUm96NTr66Wfvo1mJenBLwcYmPs3TIBP5ruzYGD7Hx/DaM9RmhroGPw== + version "4.4.5" + resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.4.5.tgz#1b1f94f9bfe7379adda86a8b73fb570265a0dddd" + integrity sha512-0Ce31oWVB7YidkaTq33ZxEbN+UDxMMgThvCe8ptgQViymL5DPis9uLdTA13MiRPhgvqyxIegugrP97iK3JeBHg== dependencies: neo-async "^2.6.0" optimist "^0.6.1" @@ -4711,16 +4873,16 @@ highlight-words-core@^1.2.0: integrity sha512-BXUKIkUuh6cmmxzi5OIbUJxrG8OAk2MqoL1DtO3Wo9D2faJg2ph5ntyuQeLqaHJmzER6H5tllCDA9ZnNe9BVGg== history@^4.9.0: - version "4.9.0" - resolved "https://registry.yarnpkg.com/history/-/history-4.9.0.tgz#84587c2068039ead8af769e9d6a6860a14fa1bca" - integrity sha512-H2DkjCjXf0Op9OAr6nJ56fcRkTSNrUiv41vNJ6IswJjif6wlpZK0BTfFbi7qK9dXLSYZxkq5lBsj3vUjlYBYZA== + version "4.10.1" + resolved "https://registry.yarnpkg.com/history/-/history-4.10.1.tgz#33371a65e3a83b267434e2b3f3b1b4c58aad4cf3" + integrity sha512-36nwAD620w12kuzPAsyINPWJqlNbij+hpK1k9XRloDtym8mxzGYl2c17LnV6IAGB2Dmg4tEa7G7DlawS0+qjew== dependencies: "@babel/runtime" "^7.1.2" loose-envify "^1.2.0" - resolve-pathname "^2.2.0" + resolve-pathname "^3.0.0" tiny-invariant "^1.0.2" tiny-warning "^1.0.0" - value-equal "^0.4.0" + value-equal "^1.0.1" hmac-drbg@^1.0.0: version "1.0.1" @@ -4739,9 +4901,9 @@ hoist-non-react-statics@^3.1.0, hoist-non-react-statics@^3.3.0: react-is "^16.7.0" hosted-git-info@^2.1.4: - version "2.8.4" - resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.4.tgz#44119abaf4bc64692a16ace34700fed9c03e2546" - integrity sha512-pzXIvANXEFrc5oFFXRMkbLPQ2rXRoDERwDLyrcUxGhaZhgP54BBSl9Oheh7Vv0T090cszWBxPjkQQ5Sq1PbBRQ== + version "2.8.5" + resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.5.tgz#759cfcf2c4d156ade59b0b2dfabddc42a6b9c70c" + integrity sha512-kssjab8CvdXfcXMXVcvsXum4Hwdq9XGtRD3TteMEvEbq0LXyiNQr6AprqKqfeaDXze7SxWvRxdpwE6ku7ikLkg== hpack.js@^2.1.6: version "2.1.6" @@ -4877,11 +5039,11 @@ http-proxy-middleware@^0.19.1: micromatch "^3.1.10" http-proxy@^1.17.0: - version "1.17.0" - resolved "https://registry.yarnpkg.com/http-proxy/-/http-proxy-1.17.0.tgz#7ad38494658f84605e2f6db4436df410f4e5be9a" - integrity sha512-Taqn+3nNvYRfJ3bGvKfBSRwy1v6eePlm3oc/aWVxZp57DQr5Eq3xhKJi7Z4hZpS8PC3H4qI+Yly5EmFacGuA/g== + version "1.18.0" + resolved "https://registry.yarnpkg.com/http-proxy/-/http-proxy-1.18.0.tgz#dbe55f63e75a347db7f3d99974f2692a314a6a3a" + integrity sha512-84I2iJM/n1d4Hdgc6y2+qY5mDaz2PUVjlg9znE9byl+q0uC3DeByqBGReQu5tpLK0TAqTIXScRUV+dg7+bUPpQ== dependencies: - eventemitter3 "^3.0.0" + eventemitter3 "^4.0.0" follow-redirects "^1.0.0" requires-port "^1.0.0" @@ -4953,9 +5115,9 @@ iferr@^0.1.5: integrity sha1-xg7taebY/bazEEofy8ocGS3FtQE= ignore-walk@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/ignore-walk/-/ignore-walk-3.0.1.tgz#a83e62e7d272ac0e3b551aaa82831a19b69f82f8" - integrity sha512-DTVlMx3IYPe0/JJcYP7Gxg7ttZZu3IInhuEhbchuqneY9wWe5Ojy2mXLBaQFUQmo0AW2r3qG7m1mg86js+gnlQ== + version "3.0.3" + resolved "https://registry.yarnpkg.com/ignore-walk/-/ignore-walk-3.0.3.tgz#017e2447184bfeade7c238e4aefdd1e8f95b1e37" + integrity sha512-m7o6xuOaT1aqheYHKf8W6J5pYH85ZI9w077erOzLje3JsB1gkafkAhHHY19dqjulgIZHFm32Cp5uNZgcQqdJKw== dependencies: minimatch "^3.0.4" @@ -5174,6 +5336,13 @@ is-binary-path@^1.0.0: dependencies: binary-extensions "^1.0.0" +is-binary-path@~2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09" + integrity sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw== + dependencies: + binary-extensions "^2.0.0" + is-boolean-object@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-boolean-object/-/is-boolean-object-1.0.0.tgz#98f8b28030684219a95f375cfbd88ce3405dff93" @@ -5291,7 +5460,7 @@ is-glob@^3.1.0: dependencies: is-extglob "^2.1.0" -is-glob@^4.0.0, is-glob@^4.0.1: +is-glob@^4.0.0, is-glob@^4.0.1, is-glob@~4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.1.tgz#7567dbe9f2f5e2467bc77ab83c4a29482407a5dc" integrity sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg== @@ -5310,6 +5479,11 @@ is-number@^3.0.0: dependencies: kind-of "^3.0.2" +is-number@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" + integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== + is-obj@^1.0.0, is-obj@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/is-obj/-/is-obj-1.0.1.tgz#3e4729ac1f5fde025cd7d83a896dab9f4f67db0f" @@ -5334,6 +5508,11 @@ is-path-inside@^1.0.0: dependencies: path-is-inside "^1.0.1" +is-plain-obj@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-1.1.0.tgz#71a50c8429dfca773c92a390a4a03b39fcd51d3e" + integrity sha1-caUMhCnfync8kqOQpKA7OfzVHT4= + is-plain-object@^2.0.1, is-plain-object@^2.0.3, is-plain-object@^2.0.4: version "2.0.4" resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-2.0.4.tgz#2c163b3fafb1b606d9d17928f05c2a1c38e07677" @@ -5506,7 +5685,7 @@ jest-changed-files@^24.9.0: execa "^1.0.0" throat "^4.0.0" -jest-cli@^24.8.0: +jest-cli@^24.9.0: version "24.9.0" resolved "https://registry.yarnpkg.com/jest-cli/-/jest-cli-24.9.0.tgz#ad2de62d07472d419c6abc301fc432b98b10d2af" integrity sha512-+VLRKyitT3BWoMeSUIHRxV/2g8y9gw91Jh5z2UmXZzkZKpbC08CSehVxgHUwTpy+HwGcns/tqafQDJW7imYvGg== @@ -5576,10 +5755,10 @@ jest-each@^24.9.0: jest-util "^24.9.0" pretty-format "^24.9.0" -jest-environment-enzyme@^7.1.0: - version "7.1.0" - resolved "https://registry.yarnpkg.com/jest-environment-enzyme/-/jest-environment-enzyme-7.1.0.tgz#1181d174034a2a4d8ba8fd6ba2123f243ccaf7ec" - integrity sha512-31nPBYx1MZfihsKUgZg16zLS4+f4gBvo4YpYMU4TIvQ2IjbREU9bLFwAszTgcs5mkE7SNOykm/68OjPmgDd4/Q== +jest-environment-enzyme@^7.1.1: + version "7.1.1" + resolved "https://registry.yarnpkg.com/jest-environment-enzyme/-/jest-environment-enzyme-7.1.1.tgz#d9dd7b6b72ed37d213ba77e05ab7f9062aa62402" + integrity sha512-k+QJkK0iRtjWbNfKdtj1QQIs12JbbvPmHW30cSbDoIgOFO7Bd1lLo6qOabM+PdhPCeLWQ1D1ZoTrHPauXdYpzA== dependencies: jest-environment-jsdom "^24.0.0" @@ -5615,14 +5794,14 @@ jest-environment-node@^24.9.0: jest-mock "^24.9.0" jest-util "^24.9.0" -jest-enzyme@^7.0.2: - version "7.1.0" - resolved "https://registry.yarnpkg.com/jest-enzyme/-/jest-enzyme-7.1.0.tgz#4345e989382a6cd70c15c7225dad9c9a9bb79cbc" - integrity sha512-ukL9jFwvQ3xbzhoKniDVTkAgZzGCFJNeJVU058RyY0R46fVhjJlPEV/hYlmJl518qfCmKNW6zXgtmyPcaDgxhA== +jest-enzyme@^7.1.1: + version "7.1.1" + resolved "https://registry.yarnpkg.com/jest-enzyme/-/jest-enzyme-7.1.1.tgz#7684720b795636503c7660e8c6f70bd644a485fd" + integrity sha512-ujMi/2OF16rsjsS2ozdZCukfRZGC/Sb3MoJjINXITTvZM6lTL14lDliJr1kYIlUZVrphw0fmZkTNVTP7DnJ+Xw== dependencies: - enzyme-matchers "^7.1.0" + enzyme-matchers "^7.1.1" enzyme-to-json "^3.3.0" - jest-environment-enzyme "^7.1.0" + jest-environment-enzyme "^7.1.1" jest-get-type@^24.9.0: version "24.9.0" @@ -5728,18 +5907,7 @@ jest-resolve-dependencies@^24.9.0: jest-regex-util "^24.3.0" jest-snapshot "^24.9.0" -jest-resolve@24.8.0: - version "24.8.0" - resolved "https://registry.yarnpkg.com/jest-resolve/-/jest-resolve-24.8.0.tgz#84b8e5408c1f6a11539793e2b5feb1b6e722439f" - integrity sha512-+hjSzi1PoRvnuOICoYd5V/KpIQmkAsfjFO71458hQ2Whi/yf1GDeBOFj8Gxw4LrApHsVJvn5fmjcPdmoUHaVKw== - dependencies: - "@jest/types" "^24.8.0" - browser-resolve "^1.11.3" - chalk "^2.0.1" - jest-pnp-resolver "^1.2.1" - realpath-native "^1.1.0" - -jest-resolve@^24.9.0: +jest-resolve@24.9.0, jest-resolve@^24.9.0: version "24.9.0" resolved "https://registry.yarnpkg.com/jest-resolve/-/jest-resolve-24.9.0.tgz#dff04c7687af34c4dd7e524892d9cf77e5d17321" integrity sha512-TaLeLVL1l08YFZAt3zaPtjiVvyy4oSA6CRe+0AFPPVX3Q/VI0giIWWoAvoS5L96vj9Dqxj4fB5p2qrHCmTU/MQ== @@ -5858,16 +6026,16 @@ jest-validate@^24.9.0: leven "^3.1.0" pretty-format "^24.9.0" -jest-watch-typeahead@0.3.1: - version "0.3.1" - resolved "https://registry.yarnpkg.com/jest-watch-typeahead/-/jest-watch-typeahead-0.3.1.tgz#47701024b64b444aa325d801b4b3a6d61ed70701" - integrity sha512-cDIko96c4Yqg/7mfye1eEYZ6Pvugo9mnOOhGQod3Es7/KptNv1b+9gFVaotzdqNqTlwbkA80BnWHtzV4dc+trA== +jest-watch-typeahead@0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/jest-watch-typeahead/-/jest-watch-typeahead-0.4.0.tgz#4d5356839a85421588ce452d2440bf0d25308397" + integrity sha512-bJR/HPNgOQnkmttg1OkBIrYFAYuxFxExtgQh67N2qPvaWGVC8TCkedRNPKBfmZfVXFD3u2sCH+9OuS5ApBfCgA== dependencies: - ansi-escapes "^3.0.0" + ansi-escapes "^4.2.1" chalk "^2.4.1" jest-watcher "^24.3.0" - slash "^2.0.0" - string-length "^2.0.0" + slash "^3.0.0" + string-length "^3.1.0" strip-ansi "^5.0.0" jest-watcher@^24.3.0, jest-watcher@^24.9.0: @@ -5891,13 +6059,13 @@ jest-worker@^24.6.0, jest-worker@^24.9.0: merge-stream "^2.0.0" supports-color "^6.1.0" -jest@24.8.0: - version "24.8.0" - resolved "https://registry.yarnpkg.com/jest/-/jest-24.8.0.tgz#d5dff1984d0d1002196e9b7f12f75af1b2809081" - integrity sha512-o0HM90RKFRNWmAWvlyV8i5jGZ97pFwkeVoGvPW1EtLTgJc2+jcuqcbbqcSZLE/3f2S5pt0y2ZBETuhpWNl1Reg== +jest@24.9.0: + version "24.9.0" + resolved "https://registry.yarnpkg.com/jest/-/jest-24.9.0.tgz#987d290c05a08b52c56188c1002e368edb007171" + integrity sha512-YvkBL1Zm7d2B1+h5fHEOdyjCG+sGMz4f8D86/0HiqJ6MB4MnDc8FgP5vdWsGnemOQro7lnYo8UakZ3+5A0jxGw== dependencies: import-local "^2.0.0" - jest-cli "^24.8.0" + jest-cli "^24.9.0" jquery@^3.3.1: version "3.4.1" @@ -6056,9 +6224,9 @@ json5@^1.0.1: minimist "^1.2.0" json5@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/json5/-/json5-2.1.0.tgz#e7a0c62c48285c628d20a10b85c89bb807c32850" - integrity sha512-8Mh9h6xViijj36g7Dxi+Y4S6hNGV96vcJZr/SrlHh1LR/pEn/8j/+qIBbs44YKl69Lrfctp4QD+AdWLTMqEZAQ== + version "2.1.1" + resolved "https://registry.yarnpkg.com/json5/-/json5-2.1.1.tgz#81b6cb04e9ba496f1c7005d07b4368a2638f90b6" + integrity sha512-l+3HXD0GEI3huGq1njuqtzYK8OYJyXMkOLtQ53pjWh89tvWS2h6l+1zMkYWqlb57+SiQodKZyvMEFb2X+KrFhQ== dependencies: minimist "^1.2.0" @@ -6201,7 +6369,7 @@ load-json-file@^4.0.0: pify "^3.0.0" strip-bom "^3.0.0" -loader-fs-cache@^1.0.0: +loader-fs-cache@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/loader-fs-cache/-/loader-fs-cache-1.0.2.tgz#54cedf6b727e1779fd8f01205f05f6e88706f086" integrity sha512-70IzT/0/L+M20jUlEqZhZyArTU6VKLRTYRDAYN26g4jfzpJqjipLL3/hgYpySqI9PwsVRHHFja0LfEmsx9X2Cw== @@ -6306,16 +6474,6 @@ lodash.isequal@^4.5.0: resolved "https://registry.yarnpkg.com/lodash.isequal/-/lodash.isequal-4.5.0.tgz#415c4478f2bcc30120c22ce10ed3226f7d3e18e0" integrity sha1-QVxEePK8wwEgwizhDtMib30+GOA= -lodash.isfunction@^3.0.9: - version "3.0.9" - resolved "https://registry.yarnpkg.com/lodash.isfunction/-/lodash.isfunction-3.0.9.tgz#06de25df4db327ac931981d1bdb067e5af68d051" - integrity sha512-AirXNj15uRIMMPihnkInB4i3NHeb4iBtNg9WRWuK2o31S+ePwwNmDPaTL3o7dTJ+VXNZim7rFs4rxN4YU1oUJw== - -lodash.isobject@^3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/lodash.isobject/-/lodash.isobject-3.0.2.tgz#3c8fb8d5b5bf4bf90ae06e14f2a530a4ed935e1d" - integrity sha1-PI+41bW/S/kK4G4U8qUwpO2TXh0= - lodash.istypedarray@^3.0.0: version "3.0.6" resolved "https://registry.yarnpkg.com/lodash.istypedarray/-/lodash.istypedarray-3.0.6.tgz#c9a477498607501d8e8494d283b87c39281cef62" @@ -6355,11 +6513,6 @@ lodash.templatesettings@^4.0.0: dependencies: lodash._reinterpolate "^3.0.0" -lodash.tonumber@^4.0.3: - version "4.0.3" - resolved "https://registry.yarnpkg.com/lodash.tonumber/-/lodash.tonumber-4.0.3.tgz#0b96b31b35672793eb7f5a63ee791f1b9e9025d9" - integrity sha1-C5azGzVnJ5Prf1pj7nkfG56QJdk= - lodash.unescape@4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/lodash.unescape/-/lodash.unescape-4.0.1.tgz#bf2249886ce514cda112fae9218cdc065211fc9c" @@ -6376,9 +6529,9 @@ lodash.uniq@^4.5.0: integrity sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A== loglevel@^1.4.1: - version "1.6.3" - resolved "https://registry.yarnpkg.com/loglevel/-/loglevel-1.6.3.tgz#77f2eb64be55a404c9fd04ad16d57c1d6d6b1280" - integrity sha512-LoEDv5pgpvWgPF4kNYuIp0qqSJVWak/dML0RY74xlzMZiT9w77teNAwKYKWBTYjlokMirg+o3jBwp+vlLrcfAA== + version "1.6.4" + resolved "https://registry.yarnpkg.com/loglevel/-/loglevel-1.6.4.tgz#f408f4f006db8354d0577dcf6d33485b3cb90d56" + integrity sha512-p0b6mOGKcGa+7nnmKbpzR6qloPbrgLcnio++E+14Vo/XffOGwZtRpUhr8dTH/x2oCMmEoIU0Zwm3ZauhvYD17g== loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.2.0, loose-envify@^1.3.1, loose-envify@^1.4.0: version "1.4.0" @@ -6476,7 +6629,7 @@ memoize-one@^4.0.0: resolved "https://registry.yarnpkg.com/memoize-one/-/memoize-one-4.1.0.tgz#a2387c58c03fff27ca390c31b764a79addf3f906" integrity sha512-2GApq0yI/b22J2j9rhbrAlsHb0Qcz+7yWxeLG8h+95sl1XPUgeLimQSOdur4Vw7cUhrBHwaUZxWFZueojqNRzA== -memory-fs@^0.4.0, memory-fs@^0.4.1: +memory-fs@^0.4.1: version "0.4.1" resolved "https://registry.yarnpkg.com/memory-fs/-/memory-fs-0.4.1.tgz#3a9a20b8462523e447cfbc7e8bb80ed667bfc552" integrity sha1-OpoguEYlI+RHz7x+i7gO1me/xVI= @@ -6484,6 +6637,14 @@ memory-fs@^0.4.0, memory-fs@^0.4.1: errno "^0.1.3" readable-stream "^2.0.1" +memory-fs@^0.5.0: + version "0.5.0" + resolved "https://registry.yarnpkg.com/memory-fs/-/memory-fs-0.5.0.tgz#324c01288b88652966d161db77838720845a8e3c" + integrity sha512-jA0rdU5KoQMC0e6ppoNRtpp6vjFq6+NY7r8hywnC7V+1Xj/MtHwGIbB1QaK/dunyjWteJzmkpd7ooeWg10T7GA== + dependencies: + errno "^0.1.3" + readable-stream "^2.0.1" + merge-deep@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/merge-deep/-/merge-deep-3.0.2.tgz#f39fa100a4f1bd34ff29f7d2bf4508fbb8d83ad2" @@ -6504,9 +6665,9 @@ merge-stream@^2.0.0: integrity sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w== merge2@^1.2.3: - version "1.2.4" - resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.2.4.tgz#c9269589e6885a60cf80605d9522d4b67ca646e3" - integrity sha512-FYE8xI+6pjFOhokZu0We3S5NKCirLbCzSh2Usf3qEyr4X8U+0jNg9P8RZ4qz+V2UoECLVwSyzU3LxXBaLGtD3A== + version "1.3.0" + resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.3.0.tgz#5b366ee83b2f1582c48f87e47cf1a9352103ca81" + integrity sha512-2j4DAdlBOkiSZIsaXk4mTE3sRS02yBHAtfy127xRV3bQUFqXkjHCHLW6Scv7DwNRbIWNHH8zpnz9zMaKXIdvYw== methods@^1.1.1, methods@~1.1.2: version "1.1.2" @@ -6545,11 +6706,16 @@ miller-rabin@^4.0.0: bn.js "^4.0.0" brorand "^1.0.1" -mime-db@1.40.0, "mime-db@>= 1.40.0 < 2": +mime-db@1.40.0: version "1.40.0" resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.40.0.tgz#a65057e998db090f732a68f6c276d387d4126c32" integrity sha512-jYdeOMPy9vnxEqFRRo6ZvTZ8d9oPb+k18PKoYNYUe2stVEBPPwsln/qWzdbmaIvnhZ9v2P+CuecK+fpUfsV2mA== +"mime-db@>= 1.40.0 < 2": + version "1.42.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.42.0.tgz#3e252907b4c7adb906597b4b65636272cf9e7bac" + integrity sha512-UbfJCR4UAVRNgMpfImz05smAXK7+c+ZntjaA26ANtkXLlOe947Aag5zdIcKQULAiF9Cq4WxBi9jUs5zkA84bYQ== + mime-types@^2.1.12, mime-types@~2.1.17, mime-types@~2.1.19, mime-types@~2.1.24: version "2.1.24" resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.24.tgz#b6f8d0b3e951efb77dedeca194cff6d16f676f81" @@ -6562,7 +6728,7 @@ mime@1.6.0, mime@^1.4.1: resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1" integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg== -mime@^2.4.2, mime@^2.4.4: +mime@^2.4.4: version "2.4.4" resolved "https://registry.yarnpkg.com/mime/-/mime-2.4.4.tgz#bd7b91135fc6b01cde3e9bae33d659b63d8857e5" integrity sha512-LRxmNwziLPT828z+4YkNzloCFC2YM4wrB99k+AV5ZbEyfGNWfG8SO1FUXLmLDBSo89NrJZ4DIWeLjy1CHGhMGA== @@ -6586,12 +6752,13 @@ mini-create-react-context@^0.3.0: gud "^1.0.0" tiny-warning "^1.0.2" -mini-css-extract-plugin@0.5.0: - version "0.5.0" - resolved "https://registry.yarnpkg.com/mini-css-extract-plugin/-/mini-css-extract-plugin-0.5.0.tgz#ac0059b02b9692515a637115b0cc9fed3a35c7b0" - integrity sha512-IuaLjruM0vMKhUUT51fQdQzBYTX49dLj8w68ALEAe2A4iYNpIC4eMac67mt3NzycvjOlf07/kYxJDc0RTl1Wqw== +mini-css-extract-plugin@0.8.0: + version "0.8.0" + resolved "https://registry.yarnpkg.com/mini-css-extract-plugin/-/mini-css-extract-plugin-0.8.0.tgz#81d41ec4fe58c713a96ad7c723cdb2d0bd4d70e1" + integrity sha512-MNpRGbNA52q6U92i0qbVpQNsgk7LExy41MdAlG84FeytfDOtRIf/mCHdEgG8rpTKOaNKiqUnZdlptF469hxqOw== dependencies: loader-utils "^1.1.0" + normalize-url "1.9.1" schema-utils "^1.0.0" webpack-sources "^1.1.0" @@ -6627,20 +6794,20 @@ minimist@~0.0.1: resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.10.tgz#de3f98543dbf96082be48ad1a0c7cda836301dcf" integrity sha1-3j+YVD2/lggr5IrRoMfNqDYwHc8= -minipass@^2.2.1, minipass@^2.3.5: - version "2.4.0" - resolved "https://registry.yarnpkg.com/minipass/-/minipass-2.4.0.tgz#38f0af94f42fb6f34d3d7d82a90e2c99cd3ff485" - integrity sha512-6PmOuSP4NnZXzs2z6rbwzLJu/c5gdzYg1mRI/WIYdx45iiX7T+a4esOzavD6V/KmBzAaopFSTZPZcUx73bqKWA== +minipass@^2.6.0, minipass@^2.8.6, minipass@^2.9.0: + version "2.9.0" + resolved "https://registry.yarnpkg.com/minipass/-/minipass-2.9.0.tgz#e713762e7d3e32fed803115cf93e04bca9fcc9a6" + integrity sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg== dependencies: safe-buffer "^5.1.2" yallist "^3.0.0" minizlib@^1.2.1: - version "1.2.1" - resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-1.2.1.tgz#dd27ea6136243c7c880684e8672bb3a45fd9b614" - integrity sha512-7+4oTUOWKg7AuL3vloEWekXY2/D20cevzsrNT2kGWm+39J9hGTCBv8VI5Pm5lXZ/o3/mdR4f8rflAPhnQb8mPA== + version "1.3.3" + resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-1.3.3.tgz#2290de96818a34c29551c8a8d301216bd65a861d" + integrity sha512-6ZYMOEnmVsdCeTJVE0W9ZD+pVnE8h9Hma/iOwwRDsdQoePpoX56/8B6z3P9VNwppJuBKNRuFDRNRqRWexT9G9Q== dependencies: - minipass "^2.2.1" + minipass "^2.9.0" mississippi@^3.0.0: version "3.0.0" @@ -6674,7 +6841,7 @@ mixin-object@^2.0.1: for-in "^0.1.3" is-extendable "^0.1.1" -mkdirp@0.5.1, mkdirp@0.5.x, mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@~0.5.1: +mkdirp@0.5.1, mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@~0.5.1: version "0.5.1" resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903" integrity sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM= @@ -6759,9 +6926,9 @@ natural-compare@^1.4.0: integrity sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc= nearley@^2.7.10: - version "2.18.0" - resolved "https://registry.yarnpkg.com/nearley/-/nearley-2.18.0.tgz#a9193612dd6d528a2e47e743b1dc694cfe105223" - integrity sha512-/zQOMCeJcioI0xJtd5RpBiWw2WP7wLe6vq8/3Yu0rEwgus/G/+pViX80oA87JdVgjRt2895mZSv2VfZmy4W1uw== + version "2.19.0" + resolved "https://registry.yarnpkg.com/nearley/-/nearley-2.19.0.tgz#37717781d0fd0f2bfc95e233ebd75678ca4bda46" + integrity sha512-2v52FTw7RPqieZr3Gth1luAXZR7Je6q3KaDHY5bjl/paDUdMu35fZ8ICNgiYJRr3tf3NMvIQQR1r27AvEr9CRA== dependencies: commander "^2.19.0" moo "^0.4.3" @@ -6813,10 +6980,10 @@ node-fetch@^1.0.1: encoding "^0.1.11" is-stream "^1.0.1" -node-forge@0.7.5: - version "0.7.5" - resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-0.7.5.tgz#6c152c345ce11c52f465c2abd957e8639cd674df" - integrity sha512-MmbQJ2MTESTjt3Gi/3yG1wGpIMhUfcIypUCGtTizFR9IiccFwxSpfp0vtIZlkFclEqERemxfnSdZEMR9VqqEFQ== +node-forge@0.9.0: + version "0.9.0" + resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-0.9.0.tgz#d624050edbb44874adca12bb9a52ec63cb782579" + integrity sha512-7ASaDa3pD+lJ3WvXFsxekJQelBKRpne+GOVbLbtHYdd7pFspyeuJHnWfLplGf3SwKGbfs/aYl5V/JCIaHVUKKQ== node-int64@^0.4.0: version "0.4.0" @@ -6884,12 +7051,12 @@ node-pre-gyp@^0.12.0: semver "^5.3.0" tar "^4" -node-releases@^1.1.25: - version "1.1.28" - resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.28.tgz#503c3c70d0e4732b84e7aaa2925fbdde10482d4a" - integrity sha512-AQw4emh6iSXnCpDiFe0phYcThiccmkNWMZnFZ+lDJjAP8J0m2fVd59duvUUyuTirQOhIAajTFkzG6FHCLBO59g== +node-releases@^1.1.29, node-releases@^1.1.36: + version "1.1.36" + resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.36.tgz#44b7cb8254138e87bdbfa47761d0f825e20900b4" + integrity sha512-ggXhX6QGyJSjj3r+6ml2LqqC28XOWmKtpb+a15/Zpr9V3yoNazxJNlcQDS9bYaid5FReEWHEgToH1mwoUceWwg== dependencies: - semver "^5.3.0" + semver "^6.3.0" nopt@^4.0.1: version "4.0.1" @@ -6916,7 +7083,7 @@ normalize-path@^2.1.1: dependencies: remove-trailing-separator "^1.0.1" -normalize-path@^3.0.0: +normalize-path@^3.0.0, normalize-path@~3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== @@ -6926,6 +7093,16 @@ normalize-range@^0.1.2: resolved "https://registry.yarnpkg.com/normalize-range/-/normalize-range-0.1.2.tgz#2d10c06bdfd312ea9777695a4d28439456b75942" integrity sha1-LRDAa9/TEuqXd2laTShDlFa3WUI= +normalize-url@1.9.1: + version "1.9.1" + resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-1.9.1.tgz#2cc0d66b31ea23036458436e3620d85954c66c3c" + integrity sha1-LMDWazHqIwNkWENuNiDYWVTGbDw= + dependencies: + object-assign "^4.0.1" + prepend-http "^1.0.0" + query-string "^4.1.0" + sort-keys "^1.0.0" + normalize-url@^3.0.0: version "3.3.0" resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-3.3.0.tgz#b2e1c4dc4f7c6d57743df733a4f5978d18650559" @@ -6942,9 +7119,9 @@ npm-bundled@^1.0.1: integrity sha512-8/JCaftHwbd//k6y2rEWp6k1wxVfpFzB6t1p825+cUb7Ym2XQfhwIC5KwhrvzZRJu+LtDE585zVaS32+CGtf0g== npm-packlist@^1.1.6: - version "1.4.4" - resolved "https://registry.yarnpkg.com/npm-packlist/-/npm-packlist-1.4.4.tgz#866224233850ac534b63d1a6e76050092b5d2f44" - integrity sha512-zTLo8UcVYtDU3gdeaFu2Xu0n0EvelfHDGuqtNIn5RO7yQj4H1TqNdBc/yZjxnWA0PVB8D3Woyp0i5B43JwQ6Vw== + version "1.4.6" + resolved "https://registry.yarnpkg.com/npm-packlist/-/npm-packlist-1.4.6.tgz#53ba3ed11f8523079f1457376dd379ee4ea42ff4" + integrity sha512-u65uQdb+qwtGvEJh/DgQgW1Xg7sqeNbmxYyrvlNznaVTjV3E5P6F/EFjM+BVHXl7JJlsdG8A64M0XI8FI/IOlg== dependencies: ignore-walk "^3.0.1" npm-bundled "^1.0.1" @@ -7007,7 +7184,7 @@ object-copy@^0.1.0: define-property "^0.2.5" kind-of "^3.0.3" -object-hash@^1.1.4: +object-hash@^1.3.1: version "1.3.1" resolved "https://registry.yarnpkg.com/object-hash/-/object-hash-1.3.1.tgz#fde452098a951cb145f039bb7d455449ddc126df" integrity sha512-OSuu/pU4ENM9kmREg0BdNrUDIl1heYa4mBZacJc+vVWz4GtAwu7jO8s4AIt2aGRUTqxykpWzI3Oqnsm13tTMDA== @@ -7059,15 +7236,15 @@ object.entries@^1.0.4, object.entries@^1.1.0: function-bind "^1.1.1" has "^1.0.3" -object.fromentries@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/object.fromentries/-/object.fromentries-2.0.0.tgz#49a543d92151f8277b3ac9600f1e930b189d30ab" - integrity sha512-9iLiI6H083uiqUuvzyY6qrlmc/Gz8hLQFOcb/Ri/0xXFkSNS3ctV+CbE6yM2+AnkYfOB3dGjdzC0wrMLIhQICA== +object.fromentries@^2.0.0, object.fromentries@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/object.fromentries/-/object.fromentries-2.0.1.tgz#050f077855c7af8ae6649f45c80b16ee2d31e704" + integrity sha512-PUQv8Hbg3j2QX0IQYv3iAGCbGcu4yY4KQ92/dhA4sFSixBmSmp13UpDLs6jGK8rBtbmhNNIK99LD2k293jpiGA== dependencies: - define-properties "^1.1.2" - es-abstract "^1.11.0" + define-properties "^1.1.3" + es-abstract "^1.15.0" function-bind "^1.1.1" - has "^1.0.1" + has "^1.0.3" object.getownpropertydescriptors@^2.0.3: version "2.0.3" @@ -7282,11 +7459,11 @@ pako@~1.0.5: integrity sha512-0DTvPVU3ed8+HNXOu5Bs+o//Mbdj9VNQMUOe9oKCwh8l0GNwpTDMKCWbRjgtD291AWnkAgkqA/LOnQS8AmS1tw== parallel-transform@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/parallel-transform/-/parallel-transform-1.1.0.tgz#d410f065b05da23081fcd10f28854c29bda33b06" - integrity sha1-1BDwZbBdojCB/NEPKIVMKb2jOwY= + version "1.2.0" + resolved "https://registry.yarnpkg.com/parallel-transform/-/parallel-transform-1.2.0.tgz#9049ca37d6cb2182c3b1d2c720be94d14a5814fc" + integrity sha512-P2vSmIu38uIlvdcU7fDkyrxj33gTUy/ABO5ZUbGowxNCopBq/OoD42bP4UmMrJoPyk4Uqf0mu3mtWBhHCZD8yg== dependencies: - cyclist "~0.2.2" + cyclist "^1.0.1" inherits "^2.0.3" readable-stream "^2.1.5" @@ -7305,9 +7482,9 @@ parent-module@^1.0.0: callsites "^3.0.0" parse-asn1@^5.0.0: - version "5.1.4" - resolved "https://registry.yarnpkg.com/parse-asn1/-/parse-asn1-5.1.4.tgz#37f6628f823fbdeb2273b4d540434a22f3ef1fcc" - integrity sha512-Qs5duJcuvNExRfFZ99HDD3z4mAi3r9Wl/FOjEOijlxwCZs7E7mW2vjTpgQ4J8LpTF8x5v+1Vn5UQFejmWT11aw== + version "5.1.5" + resolved "https://registry.yarnpkg.com/parse-asn1/-/parse-asn1-5.1.5.tgz#003271343da58dc94cace494faef3d2147ecea0e" + integrity sha512-jkMYn1dcJqF6d5CpU689bq7w/b5ALS9ROVSpQDPrZsqqesUJii9qutvoT5ltGedNXMO2e16YUWIghG9KxaViTQ== dependencies: asn1.js "^4.0.0" browserify-aes "^1.0.0" @@ -7442,6 +7619,11 @@ performance-now@^2.1.0: resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b" integrity sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns= +picomatch@^2.0.4: + version "2.0.7" + resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.0.7.tgz#514169d8c7cd0bdbeecc8a2609e34a7163de69f6" + integrity sha512-oLHIdio3tZ0qH76NybpeneBhYVj0QFTfXEFTc/B3zKQspYfYYkWYgFsmzo+4kvId/bQRcNkVeguI3y+CD22BtA== + pify@^2.0.0: version "2.3.0" resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c" @@ -7516,19 +7698,19 @@ pnp-webpack-plugin@1.5.0: dependencies: ts-pnp "^1.1.2" -popper.js@^1.14.4: - version "1.15.0" - resolved "https://registry.yarnpkg.com/popper.js/-/popper.js-1.15.0.tgz#5560b99bbad7647e9faa475c6b8056621f5a4ff2" - integrity sha512-w010cY1oCUmI+9KwwlWki+r5jxKfTFDVoadl7MSrIujHU5MJ5OR6HTDj6Xo8aoR/QsA56x8jKjA59qGH4ELtrA== +popper.js@^1.14.4, popper.js@^1.16.0: + version "1.16.0" + resolved "https://registry.yarnpkg.com/popper.js/-/popper.js-1.16.0.tgz#2e1816bcbbaa518ea6c2e15a466f4cb9c6e2fbb3" + integrity sha512-+G+EkOPoE5S/zChTpmBSSDYmhXJ5PsW8eMhH8cP/CQHMFPBG/kC9Y5IIw6qNYgdJ+/COf0ddY2li28iHaZRSjw== portfinder@^1.0.9: - version "1.0.23" - resolved "https://registry.yarnpkg.com/portfinder/-/portfinder-1.0.23.tgz#894db4bcc5daf02b6614517ce89cd21a38226b82" - integrity sha512-B729mL/uLklxtxuiJKfQ84WPxNw5a7Yhx3geQZdcA4GjNjZSTSSMMWyoennMVnTWSmAR0lMdzWYN0JLnHrg1KQ== + version "1.0.25" + resolved "https://registry.yarnpkg.com/portfinder/-/portfinder-1.0.25.tgz#254fd337ffba869f4b9d37edc298059cb4d35eca" + integrity sha512-6ElJnHBbxVA1XSLgBp7G1FiCkQdlqGzuF7DswL5tcea+E8UpuvPU7beVAjjRwCioTS9ZluNbu+ZyRvgTsmqEBg== dependencies: - async "^1.5.2" - debug "^2.2.0" - mkdirp "0.5.x" + async "^2.6.2" + debug "^3.1.1" + mkdirp "^0.5.1" posix-character-classes@^0.1.0: version "0.1.1" @@ -8163,7 +8345,7 @@ postcss-value-parser@^3.0.0, postcss-value-parser@^3.3.0, postcss-value-parser@^ resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz#9ff822547e2893213cf1c30efa51ac5fd1ba8281" integrity sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ== -postcss-value-parser@^4.0.0: +postcss-value-parser@^4.0.2: version "4.0.2" resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-4.0.2.tgz#482282c09a42706d1fc9a069b73f44ec08391dc9" integrity sha512-LmeoohTpp/K4UiyQCwuGWlONxXamGzCMtFxLq4W1nZVGIQLYvMCJx3yAF9qyyuFpflABI9yVdtJAqbihOsCsJQ== @@ -8186,10 +8368,10 @@ postcss@7.0.14: source-map "^0.6.1" supports-color "^6.1.0" -postcss@^7.0.0, postcss@^7.0.1, postcss@^7.0.14, postcss@^7.0.17, postcss@^7.0.2, postcss@^7.0.5, postcss@^7.0.6: - version "7.0.17" - resolved "https://registry.yarnpkg.com/postcss/-/postcss-7.0.17.tgz#4da1bdff5322d4a0acaab4d87f3e782436bad31f" - integrity sha512-546ZowA+KZ3OasvQZHsbuEpysvwTZNGJv9EfyCQdsIDltPSWHAeTQ5fQy/Npi2ZDtLI3zs7Ps/p6wThErhm9fQ== +postcss@^7.0.0, postcss@^7.0.1, postcss@^7.0.14, postcss@^7.0.17, postcss@^7.0.18, postcss@^7.0.2, postcss@^7.0.5, postcss@^7.0.6: + version "7.0.18" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-7.0.18.tgz#4b9cda95ae6c069c67a4d933029eddd4838ac233" + integrity sha512-/7g1QXXgegpF+9GJj4iN7ChGF40sYuGYJ8WZu8DZWnmhQ/G36hfdk3q9LBJmoK+lZ+yzZ5KYpOoxq7LF1BxE8g== dependencies: chalk "^2.4.2" source-map "^0.6.1" @@ -8200,6 +8382,11 @@ prelude-ls@~1.1.2: resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54" integrity sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ= +prepend-http@^1.0.0: + version "1.0.4" + resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-1.0.4.tgz#d4f4562b0ce3696e41ac52d0e002e57a635dc6dc" + integrity sha1-1PRWKwzjaW5BrFLQ4ALlemNdxtw= + pretty-bytes@^5.1.0: version "5.3.0" resolved "https://registry.yarnpkg.com/pretty-bytes/-/pretty-bytes-5.3.0.tgz#f2849e27db79fb4d6cfe24764fc4134f165989f2" @@ -8307,9 +8494,9 @@ prr@~1.0.1: integrity sha1-0/wRS6BplaRexok/SEzrHXj19HY= psl@^1.1.24, psl@^1.1.28: - version "1.3.0" - resolved "https://registry.yarnpkg.com/psl/-/psl-1.3.0.tgz#e1ebf6a3b5564fa8376f3da2275da76d875ca1bd" - integrity sha512-avHdspHO+9rQTLbv1RO+MPYeP/SzsCoxofjVnHanETfQhTJrmB0HlDoW+EiN/R+C0BZ+gERab9NY0lPN2TxNag== + version "1.4.0" + resolved "https://registry.yarnpkg.com/psl/-/psl-1.4.0.tgz#5dd26156cdb69fa1fdb8ab1991667d3f80ced7c2" + integrity sha512-HZzqCGPecFLyoRj5HLfuDSKYTJkAfB5thKBIkRHtGjWwY7p1dAyveIbXIq4tO0KYfDF2tHqPUgY9SDnGm00uFw== public-encrypt@^4.0.0: version "4.0.3" @@ -8379,15 +8566,23 @@ qs@6.7.0: integrity sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ== qs@^6.5.1, qs@^6.7.0: - version "6.8.0" - resolved "https://registry.yarnpkg.com/qs/-/qs-6.8.0.tgz#87b763f0d37ca54200334cd57bb2ef8f68a1d081" - integrity sha512-tPSkj8y92PfZVbinY1n84i1Qdx75lZjMQYx9WZhnkofyxzw2r7Ho39G3/aEvSUdebxpnnM4LZJCtvE/Aq3+s9w== + version "6.9.0" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.9.0.tgz#d1297e2a049c53119cb49cca366adbbacc80b409" + integrity sha512-27RP4UotQORTpmNQDX8BHPukOnBP3p1uUJY5UnDhaJB+rMt9iMsok724XL+UHU23bEFOHRMQ2ZhI99qOWUMGFA== qs@~6.5.2: version "6.5.2" resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.2.tgz#cb3ae806e8740444584ef154ce8ee98d403f3e36" integrity sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA== +query-string@^4.1.0: + version "4.3.4" + resolved "https://registry.yarnpkg.com/query-string/-/query-string-4.3.4.tgz#bbb693b9ca915c232515b228b1a02b609043dbeb" + integrity sha1-u7aTucqRXCMlFbIosaArYJBD2+s= + dependencies: + object-assign "^4.1.0" + strict-uri-encode "^1.0.0" + querystring-es3@^0.2.0: version "0.2.1" resolved "https://registry.yarnpkg.com/querystring-es3/-/querystring-es3-0.2.1.tgz#9ec61f79049875707d69414596fd907a4d711e73" @@ -8463,12 +8658,12 @@ rc@^1.2.7: minimist "^1.2.0" strip-json-comments "~2.0.1" -react-app-polyfill@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/react-app-polyfill/-/react-app-polyfill-1.0.2.tgz#2a51175885c88245a2a356dc46df29f38ec9f060" - integrity sha512-yZcpLnIr0FOIzrOOz9JC37NWAWEuCaQWmYn9EWjEzlCW4cOmA5MkT5L3iP8QuUeFnoqVCTJgjIWYbXEJgNXhGA== +react-app-polyfill@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/react-app-polyfill/-/react-app-polyfill-1.0.4.tgz#4dd2636846b585c2d842b1e44e1bc29044345874" + integrity sha512-5Vte6ki7jpNsNCUKaboyofAhmURmCn2Y6Hu7ydJ6Iu4dct1CIGoh/1FT7gUZKAbowVX2lxVPlijvp1nKxfAl4w== dependencies: - core-js "3.1.4" + core-js "3.2.1" object-assign "4.1.1" promise "8.0.3" raf "3.4.1" @@ -8485,14 +8680,14 @@ react-base16-styling@^0.6.0: lodash.flow "^3.3.0" pure-color "^1.2.0" -react-dev-utils@^9.0.3: - version "9.0.3" - resolved "https://registry.yarnpkg.com/react-dev-utils/-/react-dev-utils-9.0.3.tgz#7607455587abb84599451460eb37cef0b684131a" - integrity sha512-OyInhcwsvycQ3Zr2pQN+HV4gtRXrky5mJXIy4HnqrWa+mI624xfYfqGuC9dYbxp4Qq3YZzP8GSGQjv0AgNU15w== +react-dev-utils@^9.1.0: + version "9.1.0" + resolved "https://registry.yarnpkg.com/react-dev-utils/-/react-dev-utils-9.1.0.tgz#3ad2bb8848a32319d760d0a84c56c14bdaae5e81" + integrity sha512-X2KYF/lIGyGwP/F/oXgGDF24nxDA2KC4b7AFto+eqzc/t838gpSGiaU8trTqHXOohuLxxc5qi1eDzsl9ucPDpg== dependencies: "@babel/code-frame" "7.5.5" - address "1.1.0" - browserslist "4.6.6" + address "1.1.2" + browserslist "4.7.0" chalk "2.4.2" cross-spawn "6.0.5" detect-port-alt "1.1.6" @@ -8509,27 +8704,27 @@ react-dev-utils@^9.0.3: loader-utils "1.2.3" open "^6.3.0" pkg-up "2.0.0" - react-error-overlay "^6.0.1" + react-error-overlay "^6.0.3" recursive-readdir "2.2.2" - shell-quote "1.6.1" - sockjs-client "1.3.0" + shell-quote "1.7.2" + sockjs-client "1.4.0" strip-ansi "5.2.0" text-table "0.2.0" -react-dom@^16.8.6: - version "16.9.0" - resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-16.9.0.tgz#5e65527a5e26f22ae3701131bcccaee9fb0d3962" - integrity sha512-YFT2rxO9hM70ewk9jq0y6sQk8cL02xm4+IzYBz75CQGlClQQ1Bxq0nhHF6OtSbit+AIahujJgb/CPRibFkMNJQ== +react-dom@^16.10.2: + version "16.10.2" + resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-16.10.2.tgz#4840bce5409176bc3a1f2bd8cb10b92db452fda6" + integrity sha512-kWGDcH3ItJK4+6Pl9DZB16BXYAZyrYQItU4OMy0jAkv5aNqc+mAKb4TpFtAteI6TJZu+9ZlNhaeNQSVQDHJzkw== dependencies: loose-envify "^1.1.0" object-assign "^4.1.1" prop-types "^15.6.2" - scheduler "^0.15.0" + scheduler "^0.16.2" -react-error-overlay@^6.0.1: - version "6.0.1" - resolved "https://registry.yarnpkg.com/react-error-overlay/-/react-error-overlay-6.0.1.tgz#b8d3cf9bb991c02883225c48044cb3ee20413e0f" - integrity sha512-V9yoTr6MeZXPPd4nV/05eCBvGH9cGzc52FN8fs0O0TVQ3HYYf1n7EgZVtHbldRq5xU9zEzoXIITjYNIfxDDdUw== +react-error-overlay@^6.0.3: + version "6.0.3" + resolved "https://registry.yarnpkg.com/react-error-overlay/-/react-error-overlay-6.0.3.tgz#c378c4b0a21e88b2e159a3e62b2f531fd63bf60d" + integrity sha512-bOUvMWFQVk5oz8Ded9Xb7WVdEi3QGLC8tH7HmYP0Fdp4Bn3qw0tRFmr5TW6mvahzvmrK4a6bqWGfCevBflP+Xw== react-highlight-words@^0.16.0: version "0.16.0" @@ -8540,10 +8735,10 @@ react-highlight-words@^0.16.0: memoize-one "^4.0.0" prop-types "^15.5.8" -react-is@^16.6.0, react-is@^16.7.0, react-is@^16.8.1, react-is@^16.8.4, react-is@^16.8.6, react-is@^16.9.0: - version "16.9.0" - resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.9.0.tgz#21ca9561399aad0ff1a7701c01683e8ca981edcb" - integrity sha512-tJBzzzIgnnRfEm046qRcURvwQnZVXmuCbscxUO5RWrGTXpon2d4c8mI0D8WE6ydVIm29JiLB6+RslkIvym9Rjw== +react-is@^16.10.2, react-is@^16.6.0, react-is@^16.7.0, react-is@^16.8.1, react-is@^16.8.4, react-is@^16.8.6, react-is@^16.9.0: + version "16.10.2" + resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.10.2.tgz#984120fd4d16800e9a738208ab1fba422d23b5ab" + integrity sha512-INBT1QEgtcCCgvccr5/86CfD71fw9EPmDxgiJX4I2Ddr6ZsV6iFXsuby+qWJPtmNuMY0zByTsG4468P7nHuNWA== react-json-view@^1.19.1: version "1.19.1" @@ -8584,23 +8779,23 @@ react-redux@^7.0.1: prop-types "^15.7.2" react-is "^16.9.0" -react-router-dom@^5.0.0: - version "5.0.1" - resolved "https://registry.yarnpkg.com/react-router-dom/-/react-router-dom-5.0.1.tgz#ee66f4a5d18b6089c361958e443489d6bab714be" - integrity sha512-zaVHSy7NN0G91/Bz9GD4owex5+eop+KvgbxXsP/O+iW1/Ln+BrJ8QiIR5a6xNPtrdTvLkxqlDClx13QO1uB8CA== +react-router-dom@^5.1.2: + version "5.1.2" + resolved "https://registry.yarnpkg.com/react-router-dom/-/react-router-dom-5.1.2.tgz#06701b834352f44d37fbb6311f870f84c76b9c18" + integrity sha512-7BPHAaIwWpZS074UKaw1FjVdZBSVWEk8IuDXdB+OkLb8vd/WRQIpA4ag9WQk61aEfQs47wHyjWUoUGGZxpQXew== dependencies: "@babel/runtime" "^7.1.2" history "^4.9.0" loose-envify "^1.3.1" prop-types "^15.6.2" - react-router "5.0.1" + react-router "5.1.2" tiny-invariant "^1.0.2" tiny-warning "^1.0.0" -react-router@5.0.1, react-router@^5.0.0: - version "5.0.1" - resolved "https://registry.yarnpkg.com/react-router/-/react-router-5.0.1.tgz#04ee77df1d1ab6cb8939f9f01ad5702dbadb8b0f" - integrity sha512-EM7suCPNKb1NxcTZ2LEOWFtQBQRQXecLxVpdsP4DW4PbbqYWeRiLyV/Tt1SdCrvT2jcyXAXmVTmzvSzrPR63Bg== +react-router@5.1.2, react-router@^5.1.2: + version "5.1.2" + resolved "https://registry.yarnpkg.com/react-router/-/react-router-5.1.2.tgz#6ea51d789cb36a6be1ba5f7c0d48dd9e817d3418" + integrity sha512-yjEuMFy1ONK246B+rsa0cUam5OeAQ8pyclRDgpxuSCrAlJ1qN9uZ5IgyKC7gQg0w8OM50NXHEegPh/ks9YuR2A== dependencies: "@babel/runtime" "^7.1.2" history "^4.9.0" @@ -8613,28 +8808,28 @@ react-router@5.0.1, react-router@^5.0.0: tiny-invariant "^1.0.2" tiny-warning "^1.0.0" -react-scripts@3.1.1: - version "3.1.1" - resolved "https://registry.yarnpkg.com/react-scripts/-/react-scripts-3.1.1.tgz#1796bc92447f3a2d3072c3b71ca99f88d099c48d" - integrity sha512-dbjTG9vJC61OI62hIswQYg5xHvwlxDTH6QXz6ICEuA5AqkFQWk1LKl76sk8fVL2WsyumbBc4FErALwKcEV2vNA== +react-scripts@3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/react-scripts/-/react-scripts-3.2.0.tgz#58ccd6b4ffa27f1b4d2986cbdcaa916660e9e33c" + integrity sha512-6LzuKbE2B4eFQG6i1FnTScn9HDcWBfXXnOwW9xKFPJ/E3rK8i1ufbOZ0ocKyRPxJAKdN7iqg3i7lt0+oxkSVOA== dependencies: - "@babel/core" "7.5.5" + "@babel/core" "7.6.0" "@svgr/webpack" "4.3.2" - "@typescript-eslint/eslint-plugin" "1.13.0" - "@typescript-eslint/parser" "1.13.0" - babel-eslint "10.0.2" - babel-jest "^24.8.0" + "@typescript-eslint/eslint-plugin" "^2.2.0" + "@typescript-eslint/parser" "^2.2.0" + babel-eslint "10.0.3" + babel-jest "^24.9.0" babel-loader "8.0.6" - babel-plugin-named-asset-import "^0.3.3" - babel-preset-react-app "^9.0.1" + babel-plugin-named-asset-import "^0.3.4" + babel-preset-react-app "^9.0.2" camelcase "^5.2.0" case-sensitive-paths-webpack-plugin "2.2.0" css-loader "2.1.1" dotenv "6.2.0" - dotenv-expand "4.2.0" + dotenv-expand "5.1.0" eslint "^6.1.0" - eslint-config-react-app "^5.0.1" - eslint-loader "2.2.1" + eslint-config-react-app "^5.0.2" + eslint-loader "3.0.2" eslint-plugin-flowtype "3.13.0" eslint-plugin-import "2.18.2" eslint-plugin-jsx-a11y "6.2.3" @@ -8645,11 +8840,11 @@ react-scripts@3.1.1: html-webpack-plugin "4.0.0-beta.5" identity-obj-proxy "3.0.0" is-wsl "^1.1.0" - jest "24.8.0" + jest "24.9.0" jest-environment-jsdom-fourteen "0.1.0" - jest-resolve "24.8.0" - jest-watch-typeahead "0.3.1" - mini-css-extract-plugin "0.5.0" + jest-resolve "24.9.0" + jest-watch-typeahead "0.4.0" + mini-css-extract-plugin "0.8.0" optimize-css-assets-webpack-plugin "5.0.3" pnp-webpack-plugin "1.5.0" postcss-flexbugs-fixes "4.1.0" @@ -8657,39 +8852,39 @@ react-scripts@3.1.1: postcss-normalize "7.0.1" postcss-preset-env "6.7.0" postcss-safe-parser "4.0.1" - react-app-polyfill "^1.0.2" - react-dev-utils "^9.0.3" + react-app-polyfill "^1.0.4" + react-dev-utils "^9.1.0" resolve "1.12.0" resolve-url-loader "3.1.0" sass-loader "7.2.0" semver "6.3.0" style-loader "1.0.0" terser-webpack-plugin "1.4.1" - ts-pnp "1.1.2" + ts-pnp "1.1.4" url-loader "2.1.0" - webpack "4.39.1" + webpack "4.41.0" webpack-dev-server "3.2.1" - webpack-manifest-plugin "2.0.4" + webpack-manifest-plugin "2.1.1" workbox-webpack-plugin "4.3.1" optionalDependencies: fsevents "2.0.7" -react-table@^6.10.0: - version "6.10.0" - resolved "https://registry.yarnpkg.com/react-table/-/react-table-6.10.0.tgz#20444b19d8ca3c1a08e7544e5c3a93e4ba56690e" - integrity sha512-s/mQLI1+mNvlae45MfAZyZ04YIT3jUzWJqx34s0tfwpDdgJkpeK6vyzwMUkKFCpGODBxpjBOekYZzcEmk+2FiQ== +react-table@^6.10.3: + version "6.10.3" + resolved "https://registry.yarnpkg.com/react-table/-/react-table-6.10.3.tgz#d085487a5a1b18b76486b71cf1d388d87c8c7362" + integrity sha512-sVlq2/rxVaQJywGD95+qGiMr/SMHFIFnXdx619BLOWE/Os5FOGtV6pQJNAjZixbQZiOu7dmBO1kME28uxh6wmA== dependencies: classnames "^2.2.5" -react-test-renderer@^16.0.0-0, react-test-renderer@^16.6.3: - version "16.9.0" - resolved "https://registry.yarnpkg.com/react-test-renderer/-/react-test-renderer-16.9.0.tgz#7ed657a374af47af88f66f33a3ef99c9610c8ae9" - integrity sha512-R62stB73qZyhrJo7wmCW9jgl/07ai+YzvouvCXIJLBkRlRqLx4j9RqcLEAfNfU3OxTGucqR2Whmn3/Aad6L3hQ== +react-test-renderer@^16.0.0-0, react-test-renderer@^16.10.2: + version "16.10.2" + resolved "https://registry.yarnpkg.com/react-test-renderer/-/react-test-renderer-16.10.2.tgz#4d8492f8678c9b43b721a7d79ed0840fdae7c518" + integrity sha512-k9Qzyev6cTIcIfrhgrFlYQAFxh5EEDO6ALNqYqmKsWVA7Q/rUMTay5nD3nthi6COmYsd4ghVYyi8U86aoeMqYQ== dependencies: object-assign "^4.1.1" prop-types "^15.6.2" - react-is "^16.9.0" - scheduler "^0.15.0" + react-is "^16.8.6" + scheduler "^0.16.2" react-textarea-autosize@^6.1.0: version "6.1.0" @@ -8708,25 +8903,22 @@ react-transition-group@^2.3.1: prop-types "^15.6.2" react-lifecycles-compat "^3.0.4" -react@^16.8.6: - version "16.9.0" - resolved "https://registry.yarnpkg.com/react/-/react-16.9.0.tgz#40ba2f9af13bc1a38d75dbf2f4359a5185c4f7aa" - integrity sha512-+7LQnFBwkiw+BobzOF6N//BdoNw0ouwmSJTEm9cglOOmsg/TMiFHZLe2sEoN5M7LgJTj9oHH0gxklfnQe66S1w== +react@^16.10.2: + version "16.10.2" + resolved "https://registry.yarnpkg.com/react/-/react-16.10.2.tgz#a5ede5cdd5c536f745173c8da47bda64797a4cf0" + integrity sha512-MFVIq0DpIhrHFyqLU0S3+4dIcBhhOvBE8bJ/5kHPVOVaGdo0KuiQzpcjCPsf585WvhypqtrMILyoE2th6dT+Lw== dependencies: loose-envify "^1.1.0" object-assign "^4.1.1" prop-types "^15.6.2" -reactstrap@^8.0.1: - version "8.0.1" - resolved "https://registry.yarnpkg.com/reactstrap/-/reactstrap-8.0.1.tgz#0b663c8195f540bc1d6d5dbcbcf73cab56fe7c79" - integrity sha512-GvUWEL+a2+3npK1OxTXcNBMHXX4x6uc1KQRzK7yAOl+8sAHTRWqjunvMUfny3oDh8yKVzgqpqQlWWvs1B2HR9A== +reactstrap@^8.1.1: + version "8.1.1" + resolved "https://registry.yarnpkg.com/reactstrap/-/reactstrap-8.1.1.tgz#3d911aaf773bfa66ac36355cb521f7560599f9de" + integrity sha512-m4IIdTHBT5wtcPts4w4rYngKuqIUC1BpncVxCTeIj4BQDxwjdab0gGyKJKfgXgArn6iI6syiDyez/h2tLWFjsw== dependencies: "@babel/runtime" "^7.2.0" classnames "^2.2.3" - lodash.isfunction "^3.0.9" - lodash.isobject "^3.0.2" - lodash.tonumber "^4.0.3" prop-types "^15.5.8" react-lifecycles-compat "^3.0.4" react-popper "^1.3.3" @@ -8797,6 +8989,13 @@ readdirp@^2.2.1: micromatch "^3.1.10" readable-stream "^2.0.2" +readdirp@~3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.2.0.tgz#c30c33352b12c96dfb4b895421a49fd5a9593839" + integrity sha512-crk4Qu3pmXwgxdSgGhgA/eXiJAPQiX4GMOZZMXnqKxHX7TaoL+3gQVo/WeuAiogr07DpnfjIMpXXa+PAIvwPGQ== + dependencies: + picomatch "^2.0.4" + realpath-native@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/realpath-native/-/realpath-native-1.1.0.tgz#2003294fea23fb0672f2476ebe22fcf498a2d65c" @@ -8899,11 +9098,6 @@ regex-parser@2.2.10: resolved "https://registry.yarnpkg.com/regex-parser/-/regex-parser-2.2.10.tgz#9e66a8f73d89a107616e63b39d4deddfee912b37" integrity sha512-8t6074A68gHfU8Neftl0Le6KTDwfGAj7IyjPIMSfikI2wJUTHDMaIq42bUsfVnj8mhx0R+45rdUXHGpN164avA== -regexp-tree@^0.1.6: - version "0.1.12" - resolved "https://registry.yarnpkg.com/regexp-tree/-/regexp-tree-0.1.12.tgz#28eaaa6e66eeb3527c15108a3ff740d9e574e420" - integrity sha512-TsXZ8+cv2uxMEkLfgwO0E068gsNMLfuYwMMhiUxf0Kw2Vcgzq93vgl6wIlIYuPmfMqMjfQ9zAporiozqCnwLuQ== - regexp.prototype.flags@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.2.0.tgz#6b30724e306a27833eeb171b66ac8890ba37e41c" @@ -8916,10 +9110,10 @@ regexpp@^2.0.1: resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-2.0.1.tgz#8d19d31cf632482b589049f8281f93dbcba4d07f" integrity sha512-lv0M6+TkDVniA3aD1Eg0DVpfU/booSu7Eev3TDO/mZKHBfVjgCGTV4t4buppESEYDtkArYFOxTJWv6S5C+iaNw== -regexpu-core@^4.5.4: - version "4.5.5" - resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-4.5.5.tgz#aaffe61c2af58269b3e516b61a73790376326411" - integrity sha512-FpI67+ky9J+cDizQUJlIlNZFKual/lUkFr1AG6zOCpwZ9cLrg8UUVakyUQJD7fCDIe9Z2nwTQJNPyonatNmDFQ== +regexpu-core@^4.6.0: + version "4.6.0" + resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-4.6.0.tgz#2037c18b327cfce8a6fea2a4ec441f2432afb8b6" + integrity sha512-YlVaefl8P5BnFYOITTNzDvan1ulLOiXJzCNZxduTIosN17b87h3bvG9yHMoHaRuo88H4mQ06Aodj5VtYGGGiTg== dependencies: regenerate "^1.4.0" regenerate-unicode-properties "^8.1.0" @@ -8929,9 +9123,9 @@ regexpu-core@^4.5.4: unicode-match-property-value-ecmascript "^1.1.0" regjsgen@^0.5.0: - version "0.5.0" - resolved "https://registry.yarnpkg.com/regjsgen/-/regjsgen-0.5.0.tgz#a7634dc08f89209c2049adda3525711fb97265dd" - integrity sha512-RnIrLhrXCX5ow/E5/Mh2O4e/oa1/jW0eaBKTSy3LaCj+M3Bqvm97GWDp2yUtzIs4LEn65zR2yiYGFqb2ApnzDA== + version "0.5.1" + resolved "https://registry.yarnpkg.com/regjsgen/-/regjsgen-0.5.1.tgz#48f0bf1a5ea205196929c0d9798b42d1ed98443c" + integrity sha512-5qxzGZjDs9w4tzT3TPhCJqWdCc3RLYwy9J2NB0nm5Lz+S273lvWcpjaTGHsT1dc6Hhfq41uSEOw8wBmxrKOuyg== regjsparser@^0.6.0: version "0.6.0" @@ -9050,10 +9244,10 @@ resolve-from@^4.0.0: resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6" integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g== -resolve-pathname@^2.2.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/resolve-pathname/-/resolve-pathname-2.2.0.tgz#7e9ae21ed815fd63ab189adeee64dc831eefa879" - integrity sha512-bAFz9ld18RzJfddgrO2e/0S2O81710++chRMUxHjXOYKF6jTAMrUNZrEZ1PvV0zlhfjidm08iRPdTLPno1FuRg== +resolve-pathname@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/resolve-pathname/-/resolve-pathname-3.0.0.tgz#99d02224d3cf263689becbb393bc560313025dcd" + integrity sha512-C7rARubxI8bXFNB/hqcp/4iUeIXJhJZvFPFPiSPRnhU5UPxzMFIl+2E6yY6c4k9giDJAhtV+enfA+G89N6Csng== resolve-url-loader@3.1.0: version "3.1.0" @@ -9081,7 +9275,7 @@ resolve@1.1.7: resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.1.7.tgz#203114d82ad2c5ed9e8e0411b3932875e889e97b" integrity sha1-IDEU2CrSxe2ejgQRs5ModeiJ6Xs= -resolve@1.12.0, resolve@^1.10.0, resolve@^1.10.1, resolve@^1.11.0, resolve@^1.3.2, resolve@^1.5.0, resolve@^1.8.1: +resolve@1.12.0, resolve@^1.10.0, resolve@^1.10.1, resolve@^1.11.0, resolve@^1.12.0, resolve@^1.3.2, resolve@^1.5.0, resolve@^1.8.1: version "1.12.0" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.12.0.tgz#3fc644a35c84a48554609ff26ec52b66fa577df6" integrity sha512-B/dOmuoAik5bKcD6s6nXDCjzUKnaDvdkRyAk6rsmsKLipWj4797iothd7jmmUhWTfinVMU+wc56rYKsit2Qy4w== @@ -9174,9 +9368,9 @@ run-queue@^1.0.0, run-queue@^1.0.3: aproba "^1.1.1" rxjs@^6.4.0: - version "6.5.2" - resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.5.2.tgz#2e35ce815cd46d84d02a209fb4e5921e051dbec7" - integrity sha512-HUb7j3kvb7p7eCUHE3FqjoDsC1xfZQ4AHFWfTKSpZ+sAhhz5X1WX0ZuUqWbzB2QhSLp3DoLUG+hMdEDKqWo2Zg== + version "6.5.3" + resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.5.3.tgz#510e26317f4db91a7eb1de77d9dd9ba0a4899a3a" + integrity sha512-wuYsAYYFdWTAnAaPoKGNhfpWwKZbJW+HgAJ+mImp+Epl7BG8oNWBCTyRM8gba9k4lk8BgWdoYm21Mo/RYhhbgA== dependencies: tslib "^1.9.0" @@ -9240,10 +9434,10 @@ saxes@^3.1.9: dependencies: xmlchars "^2.1.1" -scheduler@^0.15.0: - version "0.15.0" - resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.15.0.tgz#6bfcf80ff850b280fed4aeecc6513bc0b4f17f8e" - integrity sha512-xAefmSfN6jqAa7Kuq7LIJY0bwAPG3xlCj0HMEBQk1lxYiDKZscY2xJ5U/61ZTrYbmNQbXa+gc7czPkVo11tnCg== +scheduler@^0.16.2: + version "0.16.2" + resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.16.2.tgz#f74cd9d33eff6fc554edfb79864868e4819132c1" + integrity sha512-BqYVWqwz6s1wZMhjFvLfVR5WXP7ZY32M/wYPo04CcuPM7XZEbV2TBNW7Z0UkguPTl0dWMA59VbNXxK6q+pHItg== dependencies: loose-envify "^1.1.0" object-assign "^4.1.1" @@ -9257,13 +9451,13 @@ schema-utils@^1.0.0: ajv-errors "^1.0.0" ajv-keywords "^3.1.0" -schema-utils@^2.0.0, schema-utils@^2.0.1: - version "2.1.0" - resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-2.1.0.tgz#940363b6b1ec407800a22951bdcc23363c039393" - integrity sha512-g6SViEZAfGNrToD82ZPUjq52KUPDYc+fN5+g6Euo5mLokl/9Yx14z0Cu4RR1m55HtBXejO0sBt+qw79axN+Fiw== +schema-utils@^2.0.0, schema-utils@^2.0.1, schema-utils@^2.2.0: + version "2.5.0" + resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-2.5.0.tgz#8f254f618d402cc80257486213c8970edfd7c22f" + integrity sha512-32ISrwW2scPXHUSusP8qMg5dLUawKkyV+/qIEV9JdXKx+rsM6mi8vZY8khg2M69Qom16rtroWXD3Ybtiws38gQ== dependencies: - ajv "^6.1.0" - ajv-keywords "^3.1.0" + ajv "^6.10.2" + ajv-keywords "^3.4.1" select-hose@^2.0.0: version "2.0.0" @@ -9271,22 +9465,17 @@ select-hose@^2.0.0: integrity sha1-Yl2GWPhlr0Psliv8N2o3NZpJlMo= selfsigned@^1.9.1: - version "1.10.4" - resolved "https://registry.yarnpkg.com/selfsigned/-/selfsigned-1.10.4.tgz#cdd7eccfca4ed7635d47a08bf2d5d3074092e2cd" - integrity sha512-9AukTiDmHXGXWtWjembZ5NDmVvP2695EtpgbCsxCa68w3c88B+alqbmZ4O3hZ4VWGXeGWzEVdvqgAJD8DQPCDw== + version "1.10.7" + resolved "https://registry.yarnpkg.com/selfsigned/-/selfsigned-1.10.7.tgz#da5819fd049d5574f28e88a9bcc6dbc6e6f3906b" + integrity sha512-8M3wBCzeWIJnQfl43IKwOmC4H/RAp50S8DF60znzjW5GVqTcSe2vWclt7hmYVPkKPlHWOu5EaWOMZ2Y6W8ZXTA== dependencies: - node-forge "0.7.5" + node-forge "0.9.0" "semver@2 || 3 || 4 || 5", semver@^5.3.0, semver@^5.4.1, semver@^5.5.0, semver@^5.5.1, semver@^5.6.0, semver@^5.7.0: version "5.7.1" resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== -semver@5.5.0: - version "5.5.0" - resolved "https://registry.yarnpkg.com/semver/-/semver-5.5.0.tgz#dc4bbc7a6ca9d916dee5d43516f0092b58f7b8ab" - integrity sha512-4SJ3dm0WAwWy/NVeioZh5AntkdJoWKxHxcmyP622fOkgHa4z3R0TdBJICINyaSDE6uNwVc8gZr+ZinwZAH4xIA== - semver@6.3.0, semver@^6.0.0, semver@^6.1.2, semver@^6.2.0, semver@^6.3.0: version "6.3.0" resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" @@ -9312,9 +9501,9 @@ send@0.17.1: statuses "~1.5.0" serialize-javascript@^1.7.0: - version "1.8.0" - resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-1.8.0.tgz#9515fc687232e2321aea1ca7a529476eb34bb480" - integrity sha512-3tHgtF4OzDmeKYj6V9nSyceRS0UJ3C7VqyD2Yj28vC/z2j6jG5FmFGahOKMD9CrglxTm3tETr87jEypaYV8DUg== + version "1.9.1" + resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-1.9.1.tgz#cfc200aef77b600c47da9bb8149c943e798c2fdb" + integrity sha512-0Vb/54WJ6k5v8sSWN09S0ora+Hnr+cX40r9F170nT+mSkaxltoE/7R3OrIdBSUv1OoiobH1QoWQbCnAO+e8J1A== serve-index@^1.7.2: version "1.9.1" @@ -9406,15 +9595,10 @@ shebang-regex@^1.0.0: resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-1.0.0.tgz#da42f49740c0b42db2ca9728571cb190c98efea3" integrity sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM= -shell-quote@1.6.1: - version "1.6.1" - resolved "https://registry.yarnpkg.com/shell-quote/-/shell-quote-1.6.1.tgz#f4781949cce402697127430ea3b3c5476f481767" - integrity sha1-9HgZSczkAmlxJ0MOo7PFR29IF2c= - dependencies: - array-filter "~0.0.0" - array-map "~0.0.0" - array-reduce "~0.0.0" - jsonify "~0.0.0" +shell-quote@1.7.2: + version "1.7.2" + resolved "https://registry.yarnpkg.com/shell-quote/-/shell-quote-1.7.2.tgz#67a7d02c76c9da24f99d20808fcaded0e0e04be2" + integrity sha512-mRz/m/JVscCrkMyPqHc/bczi3OQHkLTqXHEFu0zDhK/qfv3UcOA4SVmRCLmos4bhjr9ekVQubj/R7waKapmiQg== shellwords@^0.1.1: version "0.1.1" @@ -9448,6 +9632,11 @@ slash@^2.0.0: resolved "https://registry.yarnpkg.com/slash/-/slash-2.0.0.tgz#de552851a1759df3a8f206535442f5ec4ddeab44" integrity sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A== +slash@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634" + integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q== + slice-ansi@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-2.1.0.tgz#cacd7693461a637a5788d92a7dd4fba068e81636" @@ -9499,6 +9688,18 @@ sockjs-client@1.3.0: json3 "^3.3.2" url-parse "^1.4.3" +sockjs-client@1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/sockjs-client/-/sockjs-client-1.4.0.tgz#c9f2568e19c8fd8173b4997ea3420e0bb306c7d5" + integrity sha512-5zaLyO8/nri5cua0VtOrFXBPK1jbL4+1cebT/mmKA1E1ZXOvJrII75bPu0l0k843G/+iAbhEqzyKr0w/eCCj7g== + dependencies: + debug "^3.2.5" + eventsource "^1.0.7" + faye-websocket "~0.11.1" + inherits "^2.0.3" + json3 "^3.3.2" + url-parse "^1.4.3" + sockjs@0.3.19: version "0.3.19" resolved "https://registry.yarnpkg.com/sockjs/-/sockjs-0.3.19.tgz#d976bbe800af7bd20ae08598d582393508993c0d" @@ -9507,6 +9708,13 @@ sockjs@0.3.19: faye-websocket "^0.10.0" uuid "^3.0.1" +sort-keys@^1.0.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/sort-keys/-/sort-keys-1.1.2.tgz#441b6d4d346798f1b4e49e8920adfba0e543f9ad" + integrity sha1-RBttTTRnmPG05J6JIK37oOVD+a0= + dependencies: + is-plain-obj "^1.0.0" + source-list-map@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/source-list-map/-/source-list-map-2.0.1.tgz#3993bd873bfc48479cca9ea3a547835c7c154b34" @@ -9689,6 +9897,11 @@ stream-shift@^1.0.0: resolved "https://registry.yarnpkg.com/stream-shift/-/stream-shift-1.0.0.tgz#d5c752825e5367e786f78e18e445ea223a155952" integrity sha1-1cdSgl5TZ+eG944Y5EXqIjoVWVI= +strict-uri-encode@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz#279b225df1d582b1f54e65addd4352e18faa0713" + integrity sha1-J5siXfHVgrH1TmWt3UNS4Y+qBxM= + string-length@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/string-length/-/string-length-2.0.0.tgz#d40dbb686a3ace960c1cffca562bf2c45f8363ed" @@ -9697,6 +9910,14 @@ string-length@^2.0.0: astral-regex "^1.0.0" strip-ansi "^4.0.0" +string-length@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/string-length/-/string-length-3.1.0.tgz#107ef8c23456e187a8abd4a61162ff4ac6e25837" + integrity sha512-Ttp5YvkGm5v9Ijagtaz1BnN+k9ObpvS0eIBblPMp2YWL8FBmi9qblQ9fexc2k/CXFgrTIteU3jAw3payCnwSTA== + dependencies: + astral-regex "^1.0.0" + strip-ansi "^5.2.0" + string-width@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3" @@ -9732,6 +9953,22 @@ string.prototype.trim@^1.1.2: es-abstract "^1.13.0" function-bind "^1.1.1" +string.prototype.trimleft@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/string.prototype.trimleft/-/string.prototype.trimleft-2.1.0.tgz#6cc47f0d7eb8d62b0f3701611715a3954591d634" + integrity sha512-FJ6b7EgdKxxbDxc79cOlok6Afd++TTs5szo+zJTUyow3ycrRfJVE2pq3vcN53XexvKZu/DJMDfeI/qMiZTrjTw== + dependencies: + define-properties "^1.1.3" + function-bind "^1.1.1" + +string.prototype.trimright@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/string.prototype.trimright/-/string.prototype.trimright-2.1.0.tgz#669d164be9df9b6f7559fa8e89945b168a5a6c58" + integrity sha512-fXZTSV55dNBwv16uw+hh5jkghxSnc5oHq+5K/gXgizHwAvMetdAJlHqqoFC1FSDVPYWLkAKl2cxpUT41sV7nSg== + dependencies: + define-properties "^1.1.3" + function-bind "^1.1.1" + string_decoder@^1.0.0, string_decoder@^1.1.1: version "1.3.0" resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e" @@ -9906,13 +10143,13 @@ tapable@^1.0.0, tapable@^1.1.0, tapable@^1.1.3: integrity sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA== tar@^4: - version "4.4.10" - resolved "https://registry.yarnpkg.com/tar/-/tar-4.4.10.tgz#946b2810b9a5e0b26140cf78bea6b0b0d689eba1" - integrity sha512-g2SVs5QIxvo6OLp0GudTqEf05maawKUxXru104iaayWA09551tFCTI8f1Asb4lPfkBr91k07iL4c11XO3/b0tA== + version "4.4.13" + resolved "https://registry.yarnpkg.com/tar/-/tar-4.4.13.tgz#43b364bc52888d555298637b10d60790254ab525" + integrity sha512-w2VwSrBoHa5BsSyH+KxEqeQBAllHhccyMFVHtGtdMpF4W7IRWfZjFiQceJPChOeTsSDVUpER2T8FA93pr0L+QA== dependencies: chownr "^1.1.1" fs-minipass "^1.2.5" - minipass "^2.3.5" + minipass "^2.8.6" minizlib "^1.2.1" mkdirp "^0.5.0" safe-buffer "^5.1.2" @@ -9934,9 +10171,9 @@ terser-webpack-plugin@1.4.1, terser-webpack-plugin@^1.4.1: worker-farm "^1.7.0" terser@^4.1.2: - version "4.2.1" - resolved "https://registry.yarnpkg.com/terser/-/terser-4.2.1.tgz#1052cfe17576c66e7bc70fcc7119f22b155bdac1" - integrity sha512-cGbc5utAcX4a9+2GGVX4DsenG6v0x3glnDi5hx8816X1McEAwPlPgRtXPJzSBsbpILxZ8MQMT0KvArLuE0HP5A== + version "4.3.9" + resolved "https://registry.yarnpkg.com/terser/-/terser-4.3.9.tgz#e4be37f80553d02645668727777687dad26bbca8" + integrity sha512-NFGMpHjlzmyOtPL+fDw3G7+6Ueh/sz4mkaUYa4lJCxOPTNzd0Uj0aZJOmsDYoSQyfuVoWDMSWTPU3huyOm2zdA== dependencies: commander "^2.20.0" source-map "~0.6.1" @@ -9976,9 +10213,9 @@ through@^2.3.6: integrity sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU= thunky@^1.0.2: - version "1.0.3" - resolved "https://registry.yarnpkg.com/thunky/-/thunky-1.0.3.tgz#f5df732453407b09191dae73e2a8cc73f381a826" - integrity sha512-YwT8pjmNcAXBZqrubu22P4FYsh2D4dxRmnWBOL8Jk8bUcRUtc5326kx32tuTmFDAZtLOGEVNl8POAR8j896Iow== + version "1.1.0" + resolved "https://registry.yarnpkg.com/thunky/-/thunky-1.1.0.tgz#5abaf714a9405db0504732bbccd2cedd9ef9537d" + integrity sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA== timers-browserify@^2.0.4: version "2.0.11" @@ -10051,6 +10288,13 @@ to-regex-range@^2.1.0: is-number "^3.0.0" repeat-string "^1.6.1" +to-regex-range@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" + integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ== + dependencies: + is-number "^7.0.0" + to-regex@^3.0.1, to-regex@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/to-regex/-/to-regex-3.0.2.tgz#13cfdd9b336552f30b51f33a8ae1b42a7a7599ce" @@ -10096,22 +10340,17 @@ tr46@^1.0.1: dependencies: punycode "^2.1.0" -trim-right@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/trim-right/-/trim-right-1.0.1.tgz#cb2e1203067e0c8de1f614094b9fe45704ea6003" - integrity sha1-yy4SAwZ+DI3h9hQJS5/kVwTqYAM= - -ts-pnp@1.1.2, ts-pnp@^1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/ts-pnp/-/ts-pnp-1.1.2.tgz#be8e4bfce5d00f0f58e0666a82260c34a57af552" - integrity sha512-f5Knjh7XCyRIzoC/z1Su1yLLRrPrFCgtUAh/9fCSP6NKbATwpOL1+idQVXQokK9GRFURn/jYPGPfegIctwunoA== +ts-pnp@1.1.4, ts-pnp@^1.1.2: + version "1.1.4" + resolved "https://registry.yarnpkg.com/ts-pnp/-/ts-pnp-1.1.4.tgz#ae27126960ebaefb874c6d7fa4729729ab200d90" + integrity sha512-1J/vefLC+BWSo+qe8OnJQfWTYRS6ingxjwqmHMqaMxXMj7kFtKLgAaYW3JeX3mktjgUL+etlU8/B4VUAUI9QGw== tslib@^1.8.1, tslib@^1.9.0: version "1.10.0" resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.10.0.tgz#c3c19f95973fb0a62973fb09d90d961ee43e5c8a" integrity sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ== -tsutils@^3.7.0: +tsutils@^3.17.1: version "3.17.1" resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-3.17.1.tgz#ed719917f11ca0dee586272b2ac49e015a2dd759" integrity sha512-kzeQ5B8H3w60nFY2g8cJIuH7JDpsALXySGtwGJ0p2LSjLgay3NdIpqq5SoOBe46bKDW2iq25irHCr8wjomUS2g== @@ -10142,6 +10381,11 @@ type-check@~0.3.2: dependencies: prelude-ls "~1.1.2" +type-fest@^0.5.2: + version "0.5.2" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.5.2.tgz#d6ef42a0356c6cd45f49485c3b6281fc148e48a2" + integrity sha512-DWkS49EQKVX//Tbupb9TFa19c7+MK1XmzkrZUR8TAktmE/DizXoaoJV6TZ/tSIPXipqNiRI6CyAe7x69Jb6RSw== + type-is@~1.6.17, type-is@~1.6.18: version "1.6.18" resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.18.tgz#4e552cd05df09467dcbc4ef739de89f2cf37c131" @@ -10151,9 +10395,9 @@ type-is@~1.6.17, type-is@~1.6.18: mime-types "~2.1.24" type@^1.0.1: - version "1.0.3" - resolved "https://registry.yarnpkg.com/type/-/type-1.0.3.tgz#16f5d39f27a2d28d86e48f8981859e9d3296c179" - integrity sha512-51IMtNfVcee8+9GJvj0spSuFcZHe9vSib6Xtgsny1Km9ugyz2mbS08I3rsUIRYgJohFRFU1160sgRodYz378Hg== + version "1.2.0" + resolved "https://registry.yarnpkg.com/type/-/type-1.2.0.tgz#848dd7698dafa3e54a6c479e759c4bc3f18847a0" + integrity sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg== typed-styles@^0.0.7: version "0.0.7" @@ -10179,11 +10423,11 @@ uglify-js@3.4.x: source-map "~0.6.1" uglify-js@^3.1.4: - version "3.6.0" - resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.6.0.tgz#704681345c53a8b2079fb6cec294b05ead242ff5" - integrity sha512-W+jrUHJr3DXKhrsS7NUVxn3zqMOFn0hL/Ei6v0anCIMoKC93TjcflTagwIHLW7SfMFfiQuktQyFVCFHGUE0+yg== + version "3.6.3" + resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.6.3.tgz#1351533bbe22cc698f012589ed6bd4cbd971bff8" + integrity sha512-KfQUgOqTkLp2aZxrMbCuKCDGW9slFYu2A23A36Gs7sGzTLcRBDORdOi5E21KWHFIfkY8kzgi/Pr1cXCh0yIp5g== dependencies: - commander "~2.20.0" + commander "~2.20.3" source-map "~0.6.1" unfetch@^4.1.0: @@ -10272,9 +10516,9 @@ unset-value@^1.0.0: isobject "^3.0.0" upath@^1.1.1: - version "1.1.2" - resolved "https://registry.yarnpkg.com/upath/-/upath-1.1.2.tgz#3db658600edaeeccbe6db5e684d67ee8c2acd068" - integrity sha512-kXpym8nmDmlCBr7nKdIx8P2jNBa+pBpIUFRnKJ4dr8htyYGJFokkr2ZvERRtUN+9SY+JqXouNgUPtv6JQva/2Q== + version "1.2.0" + resolved "https://registry.yarnpkg.com/upath/-/upath-1.2.0.tgz#8f66dbcd55a883acdae4408af8b035a5044c1894" + integrity sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg== upper-case@^1.1.1: version "1.1.3" @@ -10388,10 +10632,10 @@ validate-npm-package-license@^3.0.1: spdx-correct "^3.0.0" spdx-expression-parse "^3.0.0" -value-equal@^0.4.0: - version "0.4.0" - resolved "https://registry.yarnpkg.com/value-equal/-/value-equal-0.4.0.tgz#c5bdd2f54ee093c04839d71ce2e4758a6890abc7" - integrity sha512-x+cYdNnaA3CxvMaTX0INdTCN8m8aF2uY9BvEqmxuYp8bL09cs/kWVQPVGcA35fMktdOsP69IgU7wFj/61dJHEw== +value-equal@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/value-equal/-/value-equal-1.0.1.tgz#1e0b794c734c5c0cade179c437d356d931a34d6c" + integrity sha512-NOJ6JZCAWr0zlxZt+xqCHNTEKOsrks2HQd4MqhP1qy4z1SkbEP467eNx6TgDKXMvUOb+OENfJCZwM+16n7fRfw== vary@~1.1.2: version "1.1.2" @@ -10469,12 +10713,13 @@ webidl-conversions@^4.0.2: integrity sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg== webpack-dev-middleware@^3.5.1: - version "3.7.0" - resolved "https://registry.yarnpkg.com/webpack-dev-middleware/-/webpack-dev-middleware-3.7.0.tgz#ef751d25f4e9a5c8a35da600c5fda3582b5c6cff" - integrity sha512-qvDesR1QZRIAZHOE3iQ4CXLZZSQ1lAUsSpnQmlB1PBfoN/xdRjmge3Dok0W4IdaVLJOGJy3sGI4sZHwjRU0PCA== + version "3.7.2" + resolved "https://registry.yarnpkg.com/webpack-dev-middleware/-/webpack-dev-middleware-3.7.2.tgz#0019c3db716e3fa5cecbf64f2ab88a74bab331f3" + integrity sha512-1xC42LxbYoqLNAhV6YzTYacicgMZQTqRd27Sim9wn5hJrX3I5nxYy1SxSd4+gjUFsz1dQFj+yEe6zEVmSkeJjw== dependencies: memory-fs "^0.4.1" - mime "^2.4.2" + mime "^2.4.4" + mkdirp "^0.5.1" range-parser "^1.2.1" webpack-log "^2.0.0" @@ -10522,13 +10767,14 @@ webpack-log@^2.0.0: ansi-colors "^3.0.0" uuid "^3.3.2" -webpack-manifest-plugin@2.0.4: - version "2.0.4" - resolved "https://registry.yarnpkg.com/webpack-manifest-plugin/-/webpack-manifest-plugin-2.0.4.tgz#e4ca2999b09557716b8ba4475fb79fab5986f0cd" - integrity sha512-nejhOHexXDBKQOj/5v5IZSfCeTO3x1Dt1RZEcGfBSul891X/eLIcIVH31gwxPDdsi2Z8LKKFGpM4w9+oTBOSCg== +webpack-manifest-plugin@2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/webpack-manifest-plugin/-/webpack-manifest-plugin-2.1.1.tgz#6b3e280327815b83152c79f42d0ca13b665773c4" + integrity sha512-2zqJ6mvc3yoiqfDjghAIpljhLSDh/G7vqGrzYcYqqRCd/ZZZCAuc/YPE5xG0LGpLgDJRhUNV1H+znyyhIxahzA== dependencies: fs-extra "^7.0.0" lodash ">=3.5 <5" + object.entries "^1.1.0" tapable "^1.0.0" webpack-sources@^1.1.0, webpack-sources@^1.4.0, webpack-sources@^1.4.1: @@ -10539,10 +10785,10 @@ webpack-sources@^1.1.0, webpack-sources@^1.4.0, webpack-sources@^1.4.1: source-list-map "^2.0.0" source-map "~0.6.1" -webpack@4.39.1: - version "4.39.1" - resolved "https://registry.yarnpkg.com/webpack/-/webpack-4.39.1.tgz#60ed9fb2b72cd60f26ea526c404d2a4cc97a1bd8" - integrity sha512-/LAb2TJ2z+eVwisldp3dqTEoNhzp/TLCZlmZm3GGGAlnfIWDgOEE758j/9atklNLfRyhKbZTCOIoPqLJXeBLbQ== +webpack@4.41.0: + version "4.41.0" + resolved "https://registry.yarnpkg.com/webpack/-/webpack-4.41.0.tgz#db6a254bde671769f7c14e90a1a55e73602fc70b" + integrity sha512-yNV98U4r7wX1VJAj5kyMsu36T8RPPQntcb5fJLOsMz/pt/WrKC0Vp1bAlqPLkA1LegSwQwf6P+kAbyhRKVQ72g== dependencies: "@webassemblyjs/ast" "1.8.5" "@webassemblyjs/helper-module-context" "1.8.5" @@ -10858,9 +11104,9 @@ xml-name-validator@^3.0.0: integrity sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw== xmlchars@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/xmlchars/-/xmlchars-2.1.1.tgz#ef1a81c05bff629c2280007f12daca21bd6f6c93" - integrity sha512-7hew1RPJ1iIuje/Y01bGD/mXokXxegAgVS+e+E0wSi2ILHQkYAH1+JXARwTjZSM4Z4Z+c73aKspEcqj+zPPL/w== + version "2.2.0" + resolved "https://registry.yarnpkg.com/xmlchars/-/xmlchars-2.2.0.tgz#060fe1bcb7f9c76fe2a17db86a9bc3ab894210cb" + integrity sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw== xregexp@4.0.0: version "4.0.0" @@ -10878,9 +11124,9 @@ xtend@^4.0.0, xtend@~4.0.1: integrity sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w== yallist@^3.0.0, yallist@^3.0.2, yallist@^3.0.3: - version "3.0.3" - resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.0.3.tgz#b4b049e314be545e3ce802236d6cd22cd91c3de9" - integrity sha512-S+Zk8DEWE6oKpV+vI3qWkaK+jSbIK86pCwe2IF/xwIpQ8jEuxpw9NyaGjmp9+BoJv5FV2piqCDcoCtStppiq2A== + version "3.1.1" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd" + integrity sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g== yargs-parser@^10.1.0: version "10.1.0" From 553d31467610251f57f1d8872a794d5b57a969c6 Mon Sep 17 00:00:00 2001 From: Vihang Patil Date: Sun, 20 Oct 2019 12:07:17 +0200 Subject: [PATCH 122/173] Updated dependencies. --- build.gradle.kts | 2 +- .../kotlin/org/ostelco/prime/gradle/Version.kt | 17 ++++++++--------- gradle/wrapper/gradle-wrapper.properties | 2 +- prime/build.gradle.kts | 2 +- prime/script/start.sh | 2 +- .../build.gradle.kts | 2 +- 6 files changed, 13 insertions(+), 14 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index 8fb614791..81c6b2e13 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -8,7 +8,7 @@ plugins { base java id("project-report") - id("com.github.ben-manes.versions") version "0.26.0" + id("com.github.ben-manes.versions") version "0.27.0" jacoco kotlin("jvm") version "1.3.50" apply false id("com.google.protobuf") version "0.8.10" apply false diff --git a/buildSrc/src/main/kotlin/org/ostelco/prime/gradle/Version.kt b/buildSrc/src/main/kotlin/org/ostelco/prime/gradle/Version.kt index 5edfdb53a..b9c27fdef 100644 --- a/buildSrc/src/main/kotlin/org/ostelco/prime/gradle/Version.kt +++ b/buildSrc/src/main/kotlin/org/ostelco/prime/gradle/Version.kt @@ -5,20 +5,19 @@ object Version { const val arrow = "0.8.2" - const val beam = "2.15.0" - const val byteBuddy = "1.10.1" + const val byteBuddy = "1.10.2" const val csv = "1.7" const val cxf = "3.3.3" const val dockerComposeJunitRule = "1.3.0" const val dropwizard = "1.3.15" - const val metrics = "4.1.0" + const val metrics = "4.1.1" const val firebase = "6.10.0" const val googleCloud = "1.91.2" - const val googleCloudDataStore = "1.97.0" - const val googleCloudLogging = "0.115.0-alpha" - const val googleCloudPubSub = "1.97.0" - const val googleCloudStorage = "1.97.0" + const val googleCloudDataStore = "1.98.0" + const val googleCloudLogging = "0.116.0-alpha" + const val googleCloudPubSub = "1.98.0" + const val googleCloudStorage = "1.98.0" const val gson = "2.8.6" const val grpc = "1.24.0" @@ -39,7 +38,7 @@ object Version { const val mockito = "3.1.0" const val mockitoKotlin = "2.2.0" const val neo4jDriver = "1.7.5" - const val neo4j = "3.5.11" + const val neo4j = "3.5.12" const val opencensus = "0.24.0" const val postgresql = "42.2.8" // See comment in ./sim-administration/simmanager/build.gradle const val prometheusDropwizard = "2.2.0" @@ -47,7 +46,7 @@ object Version { const val slf4j = "1.7.28" // IMPORTANT: When Stripe SDK library version is updated, check if the Stripe API version has changed. // If so, then update API version in Stripe Web Console for callback Webhooks. - const val stripe = "13.1.0" + const val stripe = "14.0.1" const val swagger = "2.0.10" const val swaggerCodegen = "2.4.9" const val testcontainers = "1.12.2" diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 770f4bacf..92524eeb7 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ #Mon May 27 14:33:41 CEST 2019 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.2-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-6.0-rc-1-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/prime/build.gradle.kts b/prime/build.gradle.kts index f574f8b42..5002d7961 100644 --- a/prime/build.gradle.kts +++ b/prime/build.gradle.kts @@ -10,7 +10,7 @@ plugins { } // Update version in [script/start.sh] too. -version = "1.66.0" +version = "1.67.0" dependencies { // interface module between prime and prime-modules diff --git a/prime/script/start.sh b/prime/script/start.sh index a1ae7f117..22af9d6f0 100755 --- a/prime/script/start.sh +++ b/prime/script/start.sh @@ -5,5 +5,5 @@ exec java \ -Dfile.encoding=UTF-8 \ --add-opens java.base/java.lang=ALL-UNNAMED \ --add-opens java.base/java.io=ALL-UNNAMED \ - -agentpath:/opt/cprof/profiler_java_agent.so=-cprof_service=prime,-cprof_service_version=1.66.0,-logtostderr,-minloglevel=2,-cprof_enable_heap_sampling \ + -agentpath:/opt/cprof/profiler_java_agent.so=-cprof_service=prime,-cprof_service_version=1.67.0,-logtostderr,-minloglevel=2,-cprof_enable_heap_sampling \ -jar /prime.jar server /config/config.yaml diff --git a/sim-administration/jersey-json-schema-validator/build.gradle.kts b/sim-administration/jersey-json-schema-validator/build.gradle.kts index 439fcce34..9b9c45e1c 100644 --- a/sim-administration/jersey-json-schema-validator/build.gradle.kts +++ b/sim-administration/jersey-json-schema-validator/build.gradle.kts @@ -9,7 +9,7 @@ dependencies { implementation(kotlin("stdlib-jdk8")) implementation("io.dropwizard:dropwizard-core:${Version.dropwizard}") - implementation("com.github.everit-org.json-schema:org.everit.json.schema:1.11.1") + implementation("com.github.everit-org.json-schema:org.everit.json.schema:1.12.0") testImplementation("io.dropwizard:dropwizard-testing:${Version.dropwizard}") } From 1b11fce889d4afd44e1f14bfd59b2ddc86eed54b Mon Sep 17 00:00:00 2001 From: "Kjell M. Myksvoll" Date: Mon, 21 Oct 2019 14:39:28 +0200 Subject: [PATCH 123/173] Refactors application notifier by moving the building of the notification message to one place --- .../prime/appnotifier/FirebaseAppNotifier.kt | 126 +++++++++++------- .../org/ostelco/prime/model/Entities.kt | 2 +- .../ostelco/prime/storage/graph/Neo4jStore.kt | 8 +- .../ostelco/prime/appnotifier/AppNotifier.kt | 27 +++- 4 files changed, 105 insertions(+), 58 deletions(-) diff --git a/app-notifier/src/main/kotlin/org/ostelco/prime/appnotifier/FirebaseAppNotifier.kt b/app-notifier/src/main/kotlin/org/ostelco/prime/appnotifier/FirebaseAppNotifier.kt index ef766fe4a..ccc5ac2c7 100644 --- a/app-notifier/src/main/kotlin/org/ostelco/prime/appnotifier/FirebaseAppNotifier.kt +++ b/app-notifier/src/main/kotlin/org/ostelco/prime/appnotifier/FirebaseAppNotifier.kt @@ -9,76 +9,102 @@ import com.google.firebase.messaging.FirebaseMessagingException import com.google.firebase.messaging.Message import com.google.firebase.messaging.Notification import org.ostelco.prime.getLogger +import org.ostelco.prime.model.ApplicationToken +import org.ostelco.prime.model.FCMStrings import org.ostelco.prime.module.getResource import org.ostelco.prime.storage.ClientDataSource class FirebaseAppNotifier: AppNotifier { private val logger by getLogger() - val listOfFailureCodes = listOf( + /* Ref. to Firebase. */ + private val store = getResource() + + /* Firebase messaging failure cases. */ + private val listOfFailureCodes = listOf( "messaging/invalid-recipient", "messaging/invalid-registration-token", "messaging/registration-token-not-registered" ) - override fun notify(customerId: String, title: String, body: String) { - logger.info("Will try to notify customer with Id : $customerId") - sendNotification(customerId, title, body, data = null) - } + override fun notify(notificationType: NotificationType, customerId: String, data: Map) = + when (notificationType) { + NotificationType.JUMIO_VERIFICATION_SUCCEEDED -> { + logger.info("Notifying customer $customerId of successful JUMIO verification") + sendMessage(customerId = customerId, + title = FCMStrings.JUMIO_NOTIFICATION_TITLE.s, + body = FCMStrings.JUMIO_IDENTITY_VERIFIED.s, + data = data) + } + NotificationType.JUMIO_VERIFICATION_FAILED -> { + logger.info("Notifying customer $customerId of failed JUMIO verification " + + "with data $data") + sendMessage(customerId = customerId, + title = FCMStrings.JUMIO_NOTIFICATION_TITLE.s, + body = FCMStrings.JUMIO_IDENTITY_FAILED.s, + data = data) + } + } - override fun notify(customerId: String, title: String, body: String, data: Map) { - logger.info("Will try to notify-with-data customer with Id : $customerId $body, $data") - sendNotification(customerId, title, body, data) + override fun notify(customerId: String, title: String, body: String, data: Map) { + logger.info("Notifying customer $customerId of message $body with data $data") + sendMessage(customerId, title, body, data) } - private fun sendNotification(customerId: String, title: String, body: String, data: Map?) { - - val store = getResource() - - // This registration token comes from the client FCM SDKs. - val applicationTokens = store.getNotificationTokens(customerId) - - for (applicationToken in applicationTokens) { + private fun sendMessage(customerId: String, title: String, body: String, data: Map) = + store.getNotificationTokens(customerId) + .filter { + it.tokenType == "FCM" + } + .forEach { + sendMessage( + customerId = customerId, + token = it, + message = Message.builder() + .setNotification( + Notification(title, body)) + .setToken(it.token) + .putAllData(data.mapValues { + it.value.toString() + }) + .build() + ) + } - if (applicationToken.tokenType == "FCM") { - // See documentation on defining a message payload. - val builder = Message.builder() - .setNotification(Notification(title, body)) - .setToken(applicationToken.token) - if (data != null) { - builder.putAllData(data) + /* Send a message asynchrounously to the device corresponding to + the provided registration token. */ + private fun sendMessage(customerId: String, + token: ApplicationToken, + message: Message) { + val future = FirebaseMessaging + .getInstance(FirebaseApp.getInstance("fcm")) + .sendAsync(message) + val apiFutureCallback = object : ApiFutureCallback { + override fun onSuccess(result: String) { + logger.info("Notification for $customerId with appId: ${token.applicationID} " + + "completed with result: $result") + if (listOfFailureCodes.contains(result)) { + store.removeNotificationToken(customerId, token.applicationID) } - val message = builder.build() - - // Send a message to the device corresponding to the provided - // registration token. - val future = FirebaseMessaging - .getInstance(FirebaseApp.getInstance("fcm")) - .sendAsync(message) - - val apiFutureCallback = object : ApiFutureCallback { - override fun onSuccess(result: String) { - logger.info("Notification for $customerId with appId: ${applicationToken.applicationID} completed with result: $result") - if (listOfFailureCodes.contains(result)) { - store.removeNotificationToken(customerId, applicationToken.applicationID) - } - } + } - override fun onFailure(t: Throwable) { - if (t is FirebaseMessagingException) { - val errorCode = t.errorCode - logger.warn("Notification for $customerId with appId: ${applicationToken.applicationID} failed with errorCode: $errorCode") - if (listOfFailureCodes.contains(errorCode)) { - logger.warn("Removing failed token for $customerId with appId: ${applicationToken.applicationID} token: $applicationToken.token") - store.removeNotificationToken(customerId, applicationToken.applicationID) - } - } else { - logger.warn("Notification for $customerId with appId: ${applicationToken.applicationID} failed with error: $t") - } + override fun onFailure(t: Throwable) { + if (t is FirebaseMessagingException) { + val errorCode = t.errorCode + logger.warn("Notification for $customerId with appId: ${token.applicationID} " + + "failed with errorCode: $errorCode") + if (listOfFailureCodes.contains(errorCode)) { + logger.warn("Removing failed token for $customerId with appId: ${token.applicationID} " + + "token: $token.token") + store.removeNotificationToken(customerId, token.applicationID) } + } else { + logger.warn("Notification for $customerId with appId: ${token.applicationID} " + + "failed with error: $t") } - addCallback(future, apiFutureCallback, directExecutor()) } } + + addCallback(future, apiFutureCallback, directExecutor()) } } \ No newline at end of file diff --git a/model/src/main/kotlin/org/ostelco/prime/model/Entities.kt b/model/src/main/kotlin/org/ostelco/prime/model/Entities.kt index 296754f50..a1360748a 100644 --- a/model/src/main/kotlin/org/ostelco/prime/model/Entities.kt +++ b/model/src/main/kotlin/org/ostelco/prime/model/Entities.kt @@ -203,7 +203,7 @@ enum class VendorScanData(val s: String) { } enum class FCMStrings(val s: String) { - NOTIFICATION_TITLE("eKYC Status"), + JUMIO_NOTIFICATION_TITLE("eKYC Status"), JUMIO_IDENTITY_VERIFIED("Successfully verified the identity"), JUMIO_IDENTITY_FAILED("Failed to verify the identity") } diff --git a/neo4j-store/src/main/kotlin/org/ostelco/prime/storage/graph/Neo4jStore.kt b/neo4j-store/src/main/kotlin/org/ostelco/prime/storage/graph/Neo4jStore.kt index 29aa2e4d5..f7d1ef2bc 100644 --- a/neo4j-store/src/main/kotlin/org/ostelco/prime/storage/graph/Neo4jStore.kt +++ b/neo4j-store/src/main/kotlin/org/ostelco/prime/storage/graph/Neo4jStore.kt @@ -12,6 +12,7 @@ import arrow.instances.either.monad.monad import org.neo4j.driver.v1.Transaction import org.ostelco.prime.analytics.AnalyticsService import org.ostelco.prime.appnotifier.AppNotifier +import org.ostelco.prime.appnotifier.NotificationType import org.ostelco.prime.auditlog.AuditLog import org.ostelco.prime.dsl.ReadTransaction import org.ostelco.prime.dsl.WriteTransaction @@ -43,7 +44,6 @@ import org.ostelco.prime.model.CustomerRegionStatus import org.ostelco.prime.model.CustomerRegionStatus.APPROVED import org.ostelco.prime.model.CustomerRegionStatus.AVAILABLE import org.ostelco.prime.model.CustomerRegionStatus.PENDING -import org.ostelco.prime.model.FCMStrings import org.ostelco.prime.model.HasId import org.ostelco.prime.model.KycStatus import org.ostelco.prime.model.KycStatus.REJECTED @@ -1661,9 +1661,8 @@ object Neo4jStoreSingleton : GraphStore { scanInformationDatastore.upsertVendorScanInformation(customer.id, scanInformation.countryCode, vendorData) .flatMap { appNotifier.notify( + notificationType = NotificationType.JUMIO_VERIFICATION_SUCCEEDED, customerId = customer.id, - title = FCMStrings.NOTIFICATION_TITLE.s, - body = FCMStrings.JUMIO_IDENTITY_VERIFIED.s, data = extendedStatus ) logger.info(NOTIFY_OPS_MARKER, "Jumio verification succeeded for ${customer.contactEmail} Info: $extendedStatus") @@ -1676,9 +1675,8 @@ object Neo4jStoreSingleton : GraphStore { } else { // TODO: find out what more information can be passed to the client. appNotifier.notify( + notificationType = NotificationType.JUMIO_VERIFICATION_FAILED, customerId = customer.id, - title = FCMStrings.NOTIFICATION_TITLE.s, - body = FCMStrings.JUMIO_IDENTITY_FAILED.s, data = extendedStatus ) logger.info(NOTIFY_OPS_MARKER, "Jumio verification failed for ${customer.contactEmail} Info: $extendedStatus") diff --git a/prime-modules/src/main/kotlin/org/ostelco/prime/appnotifier/AppNotifier.kt b/prime-modules/src/main/kotlin/org/ostelco/prime/appnotifier/AppNotifier.kt index 95b110ecd..aa7cac82c 100644 --- a/prime-modules/src/main/kotlin/org/ostelco/prime/appnotifier/AppNotifier.kt +++ b/prime-modules/src/main/kotlin/org/ostelco/prime/appnotifier/AppNotifier.kt @@ -1,6 +1,29 @@ package org.ostelco.prime.appnotifier +/* Prime specific notification types. */ +enum class NotificationType { + JUMIO_VERIFICATION_SUCCEEDED, + JUMIO_VERIFICATION_FAILED, +} + interface AppNotifier { - fun notify(customerId: String, title: String, body: String) - fun notify(customerId: String, title: String, body: String, data: Map) + + /** + * Prime specific interface for sending notification messages to + * customers/clients. + * @param notificationType Type of notification to be sent. + * @param customerId Id of the receipient/customer. + * @param data Additional data to be added to the message if any. + */ + fun notify(notificationType: NotificationType, customerId: String, data: Map = emptyMap()) + + /** + * Low level interface for sending notification messages to + * customers/clients. + * @param customerId Id of the customer to receive the notification. + * @param title The 'title' part of the notification. + * @param body The message part of the notification. + * @param data Additional data to be added to the message if any. + */ + fun notify(customerId: String, title: String, body: String, data: Map = emptyMap()) } From 17be24eab7ea88ce2f7da7f2474eb41de2a31740 Mon Sep 17 00:00:00 2001 From: Prasanth Ullattil Date: Tue, 22 Oct 2019 10:57:19 +0200 Subject: [PATCH 124/173] Remove duplicate entries in the notification token list. The application id coming from the app is generated using the identifierForVendor iOS API. This can change for the same device depending on varios factors (see the documentation) So we will use the token from FCM to cleanup any duplicate entries. --- .../prime/storage/documentstore/DocumentDataStore.kt | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/document-data-store/src/main/kotlin/org/ostelco/prime/storage/documentstore/DocumentDataStore.kt b/document-data-store/src/main/kotlin/org/ostelco/prime/storage/documentstore/DocumentDataStore.kt index ff754e309..413244f4f 100644 --- a/document-data-store/src/main/kotlin/org/ostelco/prime/storage/documentstore/DocumentDataStore.kt +++ b/document-data-store/src/main/kotlin/org/ostelco/prime/storage/documentstore/DocumentDataStore.kt @@ -36,6 +36,12 @@ object DocumentDataStoreSingleton : DocumentStore { .getOrElse { emptyList() } override fun addNotificationToken(customerId: String, token: ApplicationToken): Boolean { + // Remove any other entries with the same token. + getNotificationTokens(customerId).forEach { + if (it.tokenType == token.tokenType && it.token == token.token && it.applicationID != token.applicationID) { + removeNotificationToken(customerId, it.applicationID) + } + } return notificationTokenStore.put( token, token.applicationID, From d84f1df226b8bdc21104ab6d5aa7c601e07c8c8c Mon Sep 17 00:00:00 2001 From: Prasanth Ullattil Date: Tue, 22 Oct 2019 14:05:37 +0200 Subject: [PATCH 125/173] First attempt to add readiness probe --- .circleci/prime-dev-values.yaml | 4 +++- prime/infra/prime-direct-values.yaml | 6 ++++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/.circleci/prime-dev-values.yaml b/.circleci/prime-dev-values.yaml index a06bdbd14..381860dd3 100644 --- a/.circleci/prime-dev-values.yaml +++ b/.circleci/prime-dev-values.yaml @@ -150,7 +150,9 @@ prime: cpu: 100m memory: 768Mi livenessProbe: {} - readinessProbe: {} + readinessProbe: + - path: '/ping' + port: 8080 annotations: prometheus.io/scrape: 'true' prometheus.io/path: '/prometheus-metrics' diff --git a/prime/infra/prime-direct-values.yaml b/prime/infra/prime-direct-values.yaml index 46befbe66..1cc20ee44 100644 --- a/prime/infra/prime-direct-values.yaml +++ b/prime/infra/prime-direct-values.yaml @@ -135,8 +135,10 @@ prime: cpu: 100m memory: 768Mi livenessProbe: {} - readinessProbe: {} - annotations: + readinessProbe: + - path: '/ping' + port: 8080 + annotations: prometheus.io/scrape: 'true' prometheus.io/path: '/prometheus-metrics' prometheus.io/port: '8081' From adf64fc1f718b2a1f5e5eda5f653c40170eeca10 Mon Sep 17 00:00:00 2001 From: Prasanth Ullattil Date: Tue, 22 Oct 2019 14:10:39 +0200 Subject: [PATCH 126/173] Fix the paramter. --- prime/infra/prime-direct-values.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/prime/infra/prime-direct-values.yaml b/prime/infra/prime-direct-values.yaml index 1cc20ee44..731194964 100644 --- a/prime/infra/prime-direct-values.yaml +++ b/prime/infra/prime-direct-values.yaml @@ -136,8 +136,8 @@ prime: memory: 768Mi livenessProbe: {} readinessProbe: - - path: '/ping' - port: 8080 + path: /ping + port: 8080 annotations: prometheus.io/scrape: 'true' prometheus.io/path: '/prometheus-metrics' From 93a045960884a42eff67abac102417ca53afcafd Mon Sep 17 00:00:00 2001 From: Prasanth Ullattil Date: Tue, 22 Oct 2019 14:30:07 +0200 Subject: [PATCH 127/173] Add readiness probe to prod --- .circleci/prime-dev-values.yaml | 6 +++--- .circleci/prime-prod-values.yaml | 4 +++- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/.circleci/prime-dev-values.yaml b/.circleci/prime-dev-values.yaml index 381860dd3..a8f090b2c 100644 --- a/.circleci/prime-dev-values.yaml +++ b/.circleci/prime-dev-values.yaml @@ -151,9 +151,9 @@ prime: memory: 768Mi livenessProbe: {} readinessProbe: - - path: '/ping' - port: 8080 - annotations: + path: /ping + port: 8080 + annotations: prometheus.io/scrape: 'true' prometheus.io/path: '/prometheus-metrics' prometheus.io/port: '8081' diff --git a/.circleci/prime-prod-values.yaml b/.circleci/prime-prod-values.yaml index da4f2ed96..ac6da9416 100644 --- a/.circleci/prime-prod-values.yaml +++ b/.circleci/prime-prod-values.yaml @@ -150,7 +150,9 @@ prime: cpu: 300m memory: 1Gi livenessProbe: {} - readinessProbe: {} + readinessProbe: + path: /ping + port: 8080 annotations: prometheus.io/scrape: 'true' prometheus.io/path: '/prometheus-metrics' From 622d8473e014aa7b5bfb18982630c72900fd61b9 Mon Sep 17 00:00:00 2001 From: Vihang Patil Date: Tue, 22 Oct 2019 16:00:12 +0200 Subject: [PATCH 128/173] Converted PurchaseRecord which was a relation, to an Entity. --- .../kotlin/org/ostelco/prime/dsl/Syntax.kt | 45 ++++--- .../org/ostelco/prime/dsl/Transactions.kt | 10 -- .../ostelco/prime/storage/graph/Neo4jStore.kt | 118 +++++++++--------- .../org/ostelco/prime/storage/graph/Schema.kt | 31 ----- .../main/resources/OnNewCustomerAction.kts | 4 +- 5 files changed, 88 insertions(+), 120 deletions(-) diff --git a/neo4j-store/src/main/kotlin/org/ostelco/prime/dsl/Syntax.kt b/neo4j-store/src/main/kotlin/org/ostelco/prime/dsl/Syntax.kt index 9e5b4267c..735abed51 100644 --- a/neo4j-store/src/main/kotlin/org/ostelco/prime/dsl/Syntax.kt +++ b/neo4j-store/src/main/kotlin/org/ostelco/prime/dsl/Syntax.kt @@ -12,8 +12,9 @@ import org.ostelco.prime.model.Subscription import org.ostelco.prime.storage.graph.Neo4jStoreSingleton.customerToBundleRelation import org.ostelco.prime.storage.graph.Neo4jStoreSingleton.customerToSegmentRelation import org.ostelco.prime.storage.graph.Neo4jStoreSingleton.customerToSimProfileRelation +import org.ostelco.prime.storage.graph.Neo4jStoreSingleton.forPurchaseByRelation +import org.ostelco.prime.storage.graph.Neo4jStoreSingleton.forPurchaseOfRelation import org.ostelco.prime.storage.graph.Neo4jStoreSingleton.identifiesRelation -import org.ostelco.prime.storage.graph.Neo4jStoreSingleton.purchaseRecordRelation import org.ostelco.prime.storage.graph.Neo4jStoreSingleton.referredRelation import org.ostelco.prime.storage.graph.Neo4jStoreSingleton.scanInformationRelation import org.ostelco.prime.storage.graph.Neo4jStoreSingleton.simProfileRegionRelation @@ -115,11 +116,6 @@ data class CustomerContext(override val id: String) : EntityContext(Cu fromId = id, toId = plan.id) - infix fun purchased(product: ProductContext) = PartialRelationExpression( - relationType = purchaseRecordRelation, - fromId = id, - toId = product.id) - infix fun belongsToSegment(segment: SegmentContext) = RelationExpression( relationType = customerToSegmentRelation, fromId = id, @@ -155,6 +151,21 @@ data class PlanContext(override val id: String) : EntityContext(Plan::clas data class ProductContext(override val id: String) : EntityContext(Product::class, id) data class SegmentContext(override val id: String) : EntityContext(Segment::class, id) +data class PurchaseRecordContext(override val id: String) : EntityContext(PurchaseRecord::class, id) { + + infix fun forPurchaseBy(customer: CustomerContext) = RelationExpression( + relationType = forPurchaseByRelation, + fromId = id, + toId = customer.id + ) + + infix fun forPurchaseOf(product: ProductContext) = RelationExpression( + relationType = forPurchaseOfRelation, + fromId = id, + toId = product.id + ) +} + // // Identity // @@ -281,19 +292,21 @@ infix fun Plan.Companion.forCustomer(customer: CustomerContext) = infix fun Product.Companion.withSku(id: String): ProductContext = ProductContext(id) -infix fun Product.Companion.purchasedBy(customer: CustomerContext) = - RelatedFromClause( - relationType = purchaseRecordRelation, - fromId = customer.id - ) - // // Purchase Record // -infix fun PurchaseRecord.Companion.forPurchasesBy(customer: CustomerContext) = - RelationFromClause( - relationType = purchaseRecordRelation, - fromId = customer.id +infix fun PurchaseRecord.Companion.withId(id: String): PurchaseRecordContext = PurchaseRecordContext(id) + +infix fun PurchaseRecord.Companion.forPurchaseBy(customer: CustomerContext) = + RelatedToClause( + relationType = forPurchaseByRelation, + toId = customer.id + ) + +infix fun PurchaseRecord.Companion.forPurchaseOf(product: ProductContext) = + RelatedToClause( + relationType = forPurchaseOfRelation, + toId = product.id ) // diff --git a/neo4j-store/src/main/kotlin/org/ostelco/prime/dsl/Transactions.kt b/neo4j-store/src/main/kotlin/org/ostelco/prime/dsl/Transactions.kt index cea51cb1d..8953707bf 100644 --- a/neo4j-store/src/main/kotlin/org/ostelco/prime/dsl/Transactions.kt +++ b/neo4j-store/src/main/kotlin/org/ostelco/prime/dsl/Transactions.kt @@ -11,7 +11,6 @@ import org.ostelco.prime.model.HasId import org.ostelco.prime.storage.DatabaseError import org.ostelco.prime.storage.StoreError import org.ostelco.prime.storage.SystemError -import org.ostelco.prime.storage.graph.ChangeableRelationStore import org.ostelco.prime.storage.graph.EntityRegistry import org.ostelco.prime.storage.graph.EntityStore import org.ostelco.prime.storage.graph.Neo4jClient @@ -158,10 +157,6 @@ class WriteTransaction(override val transaction: PrimeTransaction) : ReadTransac ) } } - is ChangeableRelationStore<*, *, *> -> { - logger.error("Using create on ChangeableRelationStore for relation - {}", relationExpression.relationType.name) - SystemError(type = "relationStore", id = relationExpression.relationType.name, message = "Invalid relation store").left() - } null -> { SystemError(type = "relationStore", id = relationExpression.relationType.name, message = "Missing relation store").left() } @@ -218,11 +213,6 @@ class JobContext(private val transaction: PrimeTransaction) { transaction = transaction) } } - is ChangeableRelationStore<*, *, *> -> { - result = result.flatMap { - SystemError(type = relationType.name, id = "", message = "Unable to create changable relation").left() - } - } } } } diff --git a/neo4j-store/src/main/kotlin/org/ostelco/prime/storage/graph/Neo4jStore.kt b/neo4j-store/src/main/kotlin/org/ostelco/prime/storage/graph/Neo4jStore.kt index f7d1ef2bc..010115b33 100644 --- a/neo4j-store/src/main/kotlin/org/ostelco/prime/storage/graph/Neo4jStore.kt +++ b/neo4j-store/src/main/kotlin/org/ostelco/prime/storage/graph/Neo4jStore.kt @@ -17,11 +17,10 @@ import org.ostelco.prime.auditlog.AuditLog import org.ostelco.prime.dsl.ReadTransaction import org.ostelco.prime.dsl.WriteTransaction import org.ostelco.prime.dsl.forCustomer -import org.ostelco.prime.dsl.forPurchasesBy +import org.ostelco.prime.dsl.forPurchaseBy import org.ostelco.prime.dsl.identifiedBy import org.ostelco.prime.dsl.linkedToRegion import org.ostelco.prime.dsl.linkedToSimProfile -import org.ostelco.prime.dsl.purchasedBy import org.ostelco.prime.dsl.readTransaction import org.ostelco.prime.dsl.referred import org.ostelco.prime.dsl.referredBy @@ -105,6 +104,8 @@ import org.ostelco.prime.storage.graph.Graph.read import org.ostelco.prime.storage.graph.Graph.write import org.ostelco.prime.storage.graph.Graph.writeSuspended import org.ostelco.prime.storage.graph.Relation.BELONG_TO_SEGMENT +import org.ostelco.prime.storage.graph.Relation.FOR_PURCHASE_BY +import org.ostelco.prime.storage.graph.Relation.FOR_PURCHASE_OF import org.ostelco.prime.storage.graph.Relation.HAS_BUNDLE import org.ostelco.prime.storage.graph.Relation.HAS_SIM_PROFILE import org.ostelco.prime.storage.graph.Relation.HAS_SUBSCRIPTION @@ -112,7 +113,6 @@ import org.ostelco.prime.storage.graph.Relation.IDENTIFIES import org.ostelco.prime.storage.graph.Relation.LINKED_TO_BUNDLE import org.ostelco.prime.storage.graph.Relation.OFFERED_TO_SEGMENT import org.ostelco.prime.storage.graph.Relation.OFFER_HAS_PRODUCT -import org.ostelco.prime.storage.graph.Relation.PURCHASED import org.ostelco.prime.storage.graph.Relation.REFERRED import org.ostelco.prime.storage.graph.model.CustomerRegion import org.ostelco.prime.storage.graph.model.Identifies @@ -150,7 +150,9 @@ enum class Relation( LINKED_TO_BUNDLE(from = Subscription::class, to = Bundle::class), // (Subscription) -[LINKED_TO_BUNDLE]-> (Bundle) - PURCHASED(from = Customer::class, to = Product::class), // (Customer) -[PURCHASED]-> (Product) + FOR_PURCHASE_BY(from = PurchaseRecord::class, to = Customer::class), // (PurchaseRecord) -[FOR_PURCHASE_BY]-> (Customer) + + FOR_PURCHASE_OF(from = PurchaseRecord::class, to = Product::class), // (PurchaseRecord) -[FOR_PURCHASE_OF]-> (Product) REFERRED(from = Customer::class, to = Customer::class), // (Customer) -[REFERRED]-> (Customer) @@ -187,6 +189,8 @@ object Neo4jStoreSingleton : GraphStore { private val productEntity = Product::class.entityType + private val purchaseRecordEntity = PurchaseRecord::class.entityType + private val subscriptionEntity = Subscription::class.entityType private val bundleEntity = Bundle::class.entityType @@ -238,15 +242,19 @@ object Neo4jStoreSingleton : GraphStore { dataClass = None::class.java) .also { RelationStore(it) } - val purchaseRecordRelation = RelationType( - relation = PURCHASED, - from = customerEntity, + val forPurchaseByRelation = RelationType( + relation = FOR_PURCHASE_BY, + from = purchaseRecordEntity, + to = customerEntity, + dataClass = None::class.java) + .also { UniqueRelationStore(it) } + + val forPurchaseOfRelation = RelationType( + relation = FOR_PURCHASE_OF, + from = purchaseRecordEntity, to = productEntity, - dataClass = PurchaseRecord::class.java) - // TODO vihang there should be 1:1 between relation and store - // NOTE: Keep RelationStore after ChangeableRelationStore - private val changablePurchaseRelationStore = ChangeableRelationStore(purchaseRecordRelation) - private val purchaseRecordRelationStore = RelationStore(purchaseRecordRelation) + dataClass = None::class.java) + .also { UniqueRelationStore(it) } val referredRelation = RelationType( relation = REFERRED, @@ -1082,7 +1090,7 @@ object Neo4jStoreSingleton : GraphStore { /* If this step fails, the previously added 'removeInvoice' call added to the transaction will ensure that the invoice will be voided. */ - createPurchaseRecordRelation(customer.id, purchaseRecord) + createPurchaseRecord(customer.id, purchaseRecord) .mapLeft { logger.error("Failed to save purchase record for customer ${customer.id}, invoice-id $invoiceId, invoice will be voided in Stripe") AuditLog.error(customerId = customer.id, message = "Failed to save purchase record - invoice-id $invoiceId, invoice will be voided in Stripe") @@ -1180,9 +1188,9 @@ object Neo4jStoreSingleton : GraphStore { /* Bail out if subscriber tries to buy an already bought plan. Note: Already verified above that 'customer' (subscriber) exists. */ - get(Product purchasedBy (Customer withId customer.id)) - .map { products -> - if (products.any { x -> x.sku == sku }) { + get(PurchaseRecord forPurchaseBy (Customer withId customer.id)) + .map { purchaseRecords -> + if (purchaseRecords.any { x:PurchaseRecord -> x.product.sku == sku }) { PlanAlredyPurchasedError("A subscription to plan $sku already exists") .left().bind() } @@ -1438,46 +1446,40 @@ object Neo4jStoreSingleton : GraphStore { readTransaction { getCustomerId(identity = identity) .flatMap { customerId -> - get(PurchaseRecord forPurchasesBy (Customer withId customerId)) + get(PurchaseRecord forPurchaseBy (Customer withId customerId)) } } override fun addPurchaseRecord(customerId: String, purchase: PurchaseRecord): Either = writeTransaction { - createPurchaseRecordRelation(customerId, purchase) + createPurchaseRecord(customerId, purchase) .ifFailedThenRollback(transaction) } - fun WriteTransaction.createPurchaseRecordRelation(customerId: String, - purchaseRecord: PurchaseRecord): Either { + fun WriteTransaction.createPurchaseRecord(customerId: String, + purchaseRecord: PurchaseRecord): Either { val invoiceId = purchaseRecord.properties["invoiceId"] - /* Avoid charging for the same invoice twice if invoice information - is present. */ - return if (invoiceId != null) { - getPurchaseRecordUsingInvoiceId(customerId, invoiceId) - .fold({ - get(Customer withId customerId).flatMap { customer -> - get(Product withSku purchaseRecord.product.sku).flatMap { product -> - fact { (Customer withId customerId) purchased (Product withSku product.sku) using purchaseRecord } - .map { purchaseRecord.id } - } - } - }, { - ValidationError(type = purchaseRecordRelation.name, - id = purchaseRecord.id, - message = "A purchase record for ${purchaseRecord.product} for customer $customerId already exists") - .left() - }) - } else { - get(Customer withId customerId).flatMap { customer -> - get(Product withSku purchaseRecord.product.sku).flatMap { product -> - fact { (Customer withId customerId) purchased (Product withSku product.sku) using purchaseRecord } - .map { purchaseRecord.id } + return IO { + Either.monad().binding { + + if (invoiceId != null + && getPurchaseRecordUsingInvoiceId(customerId, invoiceId).isRight()) { + /* Avoid charging for the same invoice twice if invoice information is present. */ + + ValidationError(type = purchaseRecordEntity.name, + id = purchaseRecord.id, + message = "A purchase record for ${purchaseRecord.product} for customer $customerId already exists") + .left() + .bind() } - } - } + create { purchaseRecord }.bind() + fact { (PurchaseRecord withId purchaseRecord.id) forPurchaseBy (Customer withId customerId) }.bind() + fact { (PurchaseRecord withId purchaseRecord.id) forPurchaseOf (Product withSku purchaseRecord.product.sku) }.bind() + purchaseRecord.id + }.fix() + }.unsafeRunSync() } /* As Stripes invoice-id is used as the 'id' of a purchase record, this method @@ -1486,12 +1488,12 @@ object Neo4jStoreSingleton : GraphStore { customerId: String, invoiceId: String): Either = - get(PurchaseRecord forPurchasesBy (Customer withId customerId)) + get(PurchaseRecord forPurchaseBy (Customer withId customerId)) .map { records -> records.find { it.properties["invoiceId"] == invoiceId } - }.leftIfNull { NotFoundError(type = purchaseRecordRelation.name, id = invoiceId) } + }.leftIfNull { NotFoundError(type = purchaseRecordEntity.name, id = invoiceId) } // // Referrals @@ -2057,8 +2059,8 @@ object Neo4jStoreSingleton : GraphStore { override fun getPaidCustomerCount(): Long = readTransaction { read(""" - MATCH (customer:${customerEntity.name})-[:${purchaseRecordRelation.name}]->(product:${productEntity.name}) - WHERE product.`price/amount` > 0 + MATCH (customer:${customerEntity.name})<-[:${forPurchaseByRelation.name}]-(purchaseRecord:${purchaseRecordEntity.name}) + WHERE purchaseRecord.`product/price/amount` > 0 RETURN count(customer) AS count """.trimIndent(), transaction) { result -> result.single().get("count").asLong() @@ -2257,7 +2259,7 @@ object Neo4jStoreSingleton : GraphStore { ) /* Will exit if an existing purchase record matches on 'invoiceId'. */ - createPurchaseRecordRelation(customerId, purchaseRecord) + createPurchaseRecord(customerId, purchaseRecord) .bind() // FIXME Moving customer to new segments should be done only based on productClass. @@ -2286,14 +2288,12 @@ object Neo4jStoreSingleton : GraphStore { override fun getPurchaseTransactions(start: Long, end: Long): Either> = readTransaction { read(""" - MATCH(c)-[r:PURCHASED]->(p) WHERE r.timestamp >= '${start}' AND r.timestamp <= '${end}' - RETURN r + MATCH(:${customerEntity.name})<-[:${forPurchaseByRelation.name}]-(pr:${purchaseRecordEntity.name}) + WHERE pr.timestamp >= '${start}' AND pr.timestamp <= '${end}' AND pr.`product/price/amount` > 0 + RETURN pr """.trimIndent(), transaction) { statementResult -> statementResult.list { record -> - purchaseRecordRelation.createRelation(record["r"].asMap()) - }.filter { - /* Exclude free products. */ - it.product.price.amount > 0 + purchaseRecordEntity.createEntity(record["r"].asMap()) }.right() } } @@ -2399,10 +2399,6 @@ object Neo4jStoreSingleton : GraphStore { return Unit.right() } - private fun updatePurchaseRecord( - purchase: PurchaseRecord, - primeTransaction: PrimeTransaction): Either = changablePurchaseRelationStore.update(purchase, primeTransaction) - override fun refundPurchase( identity: org.ostelco.prime.model.Identity, purchaseRecordId: String, @@ -2415,7 +2411,7 @@ object Neo4jStoreSingleton : GraphStore { NotFoundPaymentError("Failed to find customer with identity - $identity", error = it) }.bind() - val purchaseRecord = changablePurchaseRelationStore.get(purchaseRecordId, transaction) + val purchaseRecord = get(PurchaseRecord withId purchaseRecordId) // If we can't find the record, return not-found .mapLeft { org.ostelco.prime.paymentprocessor.core.NotFoundError("Purchase Record unavailable", @@ -2433,7 +2429,7 @@ object Neo4jStoreSingleton : GraphStore { val changedPurchaseRecord = purchaseRecord.copy( refund = refund ) - updatePurchaseRecord(changedPurchaseRecord, transaction) + update { changedPurchaseRecord } .mapLeft { logger.error("failed to update purchase record, for refund $refund.id, chargeId $purchaseRecordId, payment has been refunded in Stripe") BadGatewayError("Failed to update purchase record for refund ${refund.id}", diff --git a/neo4j-store/src/main/kotlin/org/ostelco/prime/storage/graph/Schema.kt b/neo4j-store/src/main/kotlin/org/ostelco/prime/storage/graph/Schema.kt index 224d4b2ff..0768af777 100644 --- a/neo4j-store/src/main/kotlin/org/ostelco/prime/storage/graph/Schema.kt +++ b/neo4j-store/src/main/kotlin/org/ostelco/prime/storage/graph/Schema.kt @@ -314,37 +314,6 @@ class RelationStore(private val relationType } } -class ChangeableRelationStore(private val relationType: RelationType) : BaseRelationStore() { - - init { - relationType.relationStore = this - } - - fun get(id: String, transaction: Transaction): Either { - return read("""MATCH (from)-[r:${relationType.name}{id:'$id'}]->(to) RETURN r;""", - transaction) { statementResult -> - if (statementResult.hasNext()) { - relationType.createRelation(statementResult.single().get("r").asMap()).right() - } else { - Either.left(NotFoundError(type = relationType.name, id = id)) - } - } - } - - fun update(relation: RELATION, transaction: Transaction): Either { - val properties = getProperties(relation) - // TODO vihang: replace setClause with map based settings written by Kjell - val setClause: String = properties.entries.fold("") { acc, entry -> """$acc SET r.`${entry.key}` = "${entry.value}" """ } - return write("""MATCH (from)-[r:${relationType.name}{id:'${relation.id}'}]->(to) $setClause ;""", - transaction) { statementResult -> - Either.cond( - test = statementResult.summary().counters().containsUpdates(), // TODO vihang: this is not perfect way to check if updates are applied - ifTrue = {}, - ifFalse = { NotUpdatedError(type = relationType.name, id = relation.id) }) - } - } -} - // Removes double apostrophes from key values in a JSON string. // Usage: output = re.replace(input, "$1$2$3") val re = Regex("""([,{])\s*"([^"]+)"\s*(:)""") diff --git a/neo4j-store/src/main/resources/OnNewCustomerAction.kts b/neo4j-store/src/main/resources/OnNewCustomerAction.kts index add8544bc..07bd21947 100644 --- a/neo4j-store/src/main/resources/OnNewCustomerAction.kts +++ b/neo4j-store/src/main/resources/OnNewCustomerAction.kts @@ -10,7 +10,7 @@ import org.ostelco.prime.model.Product import org.ostelco.prime.model.PurchaseRecord import org.ostelco.prime.storage.StoreError import org.ostelco.prime.storage.graph.Neo4jStoreSingleton.applyProduct -import org.ostelco.prime.storage.graph.Neo4jStoreSingleton.createPurchaseRecordRelation +import org.ostelco.prime.storage.graph.Neo4jStoreSingleton.createPurchaseRecord import org.ostelco.prime.storage.graph.OnNewCustomerAction import org.ostelco.prime.storage.graph.PrimeTransaction import java.time.Instant @@ -27,7 +27,7 @@ object : OnNewCustomerAction { Either.monad().binding { WriteTransaction(transaction).apply { val product = get(Product withSku welcomePackProductSku).bind() - createPurchaseRecordRelation( + createPurchaseRecord( customer.id, PurchaseRecord( id = UUID.randomUUID().toString(), From 6321183900c9082f8284ce73f819398a2c6e88c4 Mon Sep 17 00:00:00 2001 From: Vihang Patil Date: Tue, 22 Oct 2019 16:00:42 +0200 Subject: [PATCH 129/173] Updated documetation of data model --- docs/domain-model/classes.puml | 32 +++- docs/domain-model/classes.svg | 56 ++++-- docs/domain-model/graph.svg | 217 ++++++++++++++++++++++ neo4j-store/src/main/cypher/schema.cypher | 109 +++++++++++ 4 files changed, 396 insertions(+), 18 deletions(-) create mode 100644 docs/domain-model/graph.svg create mode 100644 neo4j-store/src/main/cypher/schema.cypher diff --git a/docs/domain-model/classes.puml b/docs/domain-model/classes.puml index 7242090ef..eaf2ee6ee 100644 --- a/docs/domain-model/classes.puml +++ b/docs/domain-model/classes.puml @@ -10,6 +10,15 @@ class CustomerRegion { + kycStatusMap: [KycType, KycStatus] } +class Identity { + + id: String + + type: String +} + +class Identifies { + + provider: String +} + class Customer { + customerId: UUID + nickname: String @@ -55,7 +64,23 @@ class Product { + price: Price } +class PurchaseRecord { + + id: UUID + + timestamp: Long +} + +class Plan { + + id: String + + stripePlanId: String? + + stripeProductId: String? + + interval: String + + intervalCount: Long +} + +Identity "1" -- "1" Identifies +Customer "1" -- "1" Identifies Customer "1" -- "*" Customer + Customer "1" -- "*" CustomerRegion Region "1" -- "*" CustomerRegion Customer "1" -- "*" Bundle @@ -70,8 +95,9 @@ Offer "*" -- "*" Product Product "1" -- "1" Price Product "*" -- "1" ProductClass -Purchase "*" -- "1" Product -Customer "1" -- "*" Purchase -Purchase "1" -- "1" Payment +PurchaseRecord "*" -- "1" Product +Customer "1" -- "*" PurchaseRecord +PurchaseRecord "1" -- "1" Payment +Customer "1" -- "*" Plan @enduml diff --git a/docs/domain-model/classes.svg b/docs/domain-model/classes.svg index d3c810693..654deac71 100644 --- a/docs/domain-model/classes.svg +++ b/docs/domain-model/classes.svg @@ -1,4 +1,4 @@ -RegionregionCode: StringregionName: StringCustomerRegionstatus: CustomerRegionStatuskycStatusMap: [KycType, KycStatus]CustomercustomerId: UUIDnickname: StringcontactEmail: EmailanalyticsId: UUIDreferralId: UUIDgetAvailableProducts():[ProductID]Bundlebalance: LongSimProfileICCID: Stringstatus: Stringalias: StringSubscriptionMSISDN: StringSegmentVisible to Admin OnlyOfferVisible to Admin OnlyProductClassid: UUIDpath: StringPricecurrency: Stringamount: IntProductSKU: UUIDprice: PricePurchasePayment1*1*1*1**11**1********11*1*11*11RegionregionCode: StringregionName: StringCustomerRegionstatus: CustomerRegionStatuskycStatusMap: [KycType, KycStatus]CustomercustomerId: UUIDnickname: StringcontactEmail: EmailanalyticsId: UUIDreferralId: UUIDgetAvailableProducts():[ProductID]Bundlebalance: LongSimProfileICCID: Stringstatus: Stringalias: StringSubscriptionMSISDN: StringSegmentVisible to Admin OnlyOfferVisible to Admin OnlyProductClassid: UUIDpath: StringPricecurrency: Stringamount: IntProductSKU: UUIDprice: PricePurchasePayment1*1*1*1**11**1********11*1*11*11 \ No newline at end of file diff --git a/docs/domain-model/graph.svg b/docs/domain-model/graph.svg new file mode 100644 index 000000000..5fcd5ff7b --- /dev/null +++ b/docs/domain-model/graph.svg @@ -0,0 +1,217 @@ + + Neo4j Graph Visualization + Created using Neo4j (http://www.neo4j.com/) + + + + IDENTIFIES + + + + + FOR_PURCHASE_OF + + + + + FOR_PURCHASE_BY + + + + + SIM_PROFILE_FOR_REGION + + + + + OFFER_HAS_PRODUCT + + + + + OFFERED_TO_SEGMENT + + + + + BELONG_TO_SEGMENT + + + + + SUBSCRIBES_TO_PLAN + + + + + HAS_SUBSCRIPTION + + + + + HAS_SIM_PROFILE + + + + + BELONG_TO_REGION + + + + + EKYC_SCAN + + + + + HAS_BUNDLE + + + + + REFERRED + + + + + LINKED_TO_BUNDLE + + + + + SUBSCRIPTION_UNDER_SIM_PROFILE + + + + + + + + + Segment + + + + + + + Identity + + + + + + + Bundle + + + + + + + Purchas… + + + + + + + SimProfile + + + + + + + Offer + + + + + + + Product + + + + + + + Region + + + + + + + Customer + + + + + + + ScanInf… + + + + + + + Subscrip… + + + + + + + Plan + + + + \ No newline at end of file diff --git a/neo4j-store/src/main/cypher/schema.cypher b/neo4j-store/src/main/cypher/schema.cypher new file mode 100644 index 000000000..ea2418c68 --- /dev/null +++ b/neo4j-store/src/main/cypher/schema.cypher @@ -0,0 +1,109 @@ +// Start +MATCH (n) DETACH DELETE n; +MATCH (n) RETURN n; + +// Segment-Offer-Product + +CREATE (:Segment {id:"Segment"}); + +CREATE (:Offer {id:"Offer"}); + +MATCH (o:Offer), (s:Segment) +CREATE (o)-[:OFFERED_TO_SEGMENT]->(s); + +CREATE (:Product {id:"Product"}); + +MATCH (o:Offer {id:"Offer"}), (p:Product {id:"Product"}) +CREATE (o)-[:OFFER_HAS_PRODUCT]->(p); + +// Region +CREATE (:Region {id:"Region"}); + + + +// Customer is created. + +CREATE (:Identity {id:"Identity"}); + +CREATE (:Customer {id:"Customer"}); + +MATCH (i:Identity {id:"Identity"}), (c:Customer {id:"Customer"}) +CREATE (i)-[:IDENTIFIES]->(c); + +// Customer might be referred. + +MATCH (c1:Customer {id:"Customer"}), (c2:Customer {id:"Customer"}) +CREATE (c2)-[:REFERRED]->(c2); + +// Customer gets assigned a Bundle. + +CREATE (:Bundle {id:"Bundle"}); + +MATCH (c:Customer {id:"Customer"}), (b:Bundle {id:"Bundle"}) +CREATE (c)-[:HAS_BUNDLE]->(b); + + +// Customer gets a welcome pack. + +CREATE (:PurchaseRecord {id:"PurchaseRecord"}); + +MATCH (c:Customer {id:"Customer"}), (p:PurchaseRecord {id:"PurchaseRecord"}) +CREATE (c)<-[:FOR_PURCHASE_BY]-(p); + +MATCH (pr:PurchaseRecord {id:"PurchaseRecord"}), (p:Product {id:"Product"}) +CREATE (pr)-[:FOR_PURCHASE_OF]->(p); + +// Customer performs eKYC + +CREATE (:ScanInformation {id:"ScanInformation"}); + +MATCH (c:Customer {id:"Customer"}), (s:ScanInformation {id:"ScanInformation"}) +CREATE (c)-[:EKYC_SCAN]->(s); + + +// Customer is approved for a Region +MATCH (c:Customer {id:"Customer"}), (r:Region {id:"Region"}) +CREATE (c)-[:BELONG_TO_REGION]->(r); + +// Customer gets a SimProfile for a Region +CREATE (:SimProfile {id:"SimProfile"}); + +MATCH (c:Customer {id:"Customer"}), (s:SimProfile {id:"SimProfile"}) +CREATE (c)-[:HAS_SIM_PROFILE]->(s); + +MATCH (s:SimProfile {id:"SimProfile"}), (r:Region {id:"Region"}) +CREATE (s)-[:SIM_PROFILE_FOR_REGION]->(r); + +// SimProfile has a Subscription. +CREATE (:Subscription {id:"Subscription"}); + +MATCH (sn:Subscription {id:"Subscription"}), (sp:SimProfile {id:"SimProfile"}) +CREATE (sn)-[:SUBSCRIPTION_UNDER_SIM_PROFILE]->(sp); + +MATCH (c:Customer {id:"Customer"}), (s:Subscription {id:"Subscription"}) +CREATE (c)-[:HAS_SUBSCRIPTION]->(s); + + +MATCH (s:Subscription {id:"Subscription"}), (b:Bundle {id:"Bundle"}) +CREATE (s)-[:LINKED_TO_BUNDLE]->(b); + + +// Plan + +CREATE (:Plan {id:"Plan"}); + +MATCH (c:Customer {id:"Customer"}), (p:Plan {id:"Plan"}) +CREATE (c)-[:SUBSCRIBES_TO_PLAN]->(p); + +// Customer is put in a segment. +MATCH (c:Customer {id:"Customer"}), (s:Segment {id:"Segment"}) +CREATE (c)-[:BELONG_TO_SEGMENT]->(s); + + + + + + + + + From 6f62a01bf6f6ed433b8c6fef1b7d0d43f203667c Mon Sep 17 00:00:00 2001 From: Vihang Patil Date: Tue, 22 Oct 2019 16:01:01 +0200 Subject: [PATCH 130/173] Updated prime version. --- prime/build.gradle.kts | 2 +- prime/script/start.sh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/prime/build.gradle.kts b/prime/build.gradle.kts index 5002d7961..70cc71287 100644 --- a/prime/build.gradle.kts +++ b/prime/build.gradle.kts @@ -10,7 +10,7 @@ plugins { } // Update version in [script/start.sh] too. -version = "1.67.0" +version = "1.68.0" dependencies { // interface module between prime and prime-modules diff --git a/prime/script/start.sh b/prime/script/start.sh index 22af9d6f0..05349e2a1 100755 --- a/prime/script/start.sh +++ b/prime/script/start.sh @@ -5,5 +5,5 @@ exec java \ -Dfile.encoding=UTF-8 \ --add-opens java.base/java.lang=ALL-UNNAMED \ --add-opens java.base/java.io=ALL-UNNAMED \ - -agentpath:/opt/cprof/profiler_java_agent.so=-cprof_service=prime,-cprof_service_version=1.67.0,-logtostderr,-minloglevel=2,-cprof_enable_heap_sampling \ + -agentpath:/opt/cprof/profiler_java_agent.so=-cprof_service=prime,-cprof_service_version=1.68.0,-logtostderr,-minloglevel=2,-cprof_enable_heap_sampling \ -jar /prime.jar server /config/config.yaml From 8f72520c4de5208c67ad19e925fc97494dcb103f Mon Sep 17 00:00:00 2001 From: Prasanth Ullattil Date: Wed, 23 Oct 2019 11:20:30 +0200 Subject: [PATCH 131/173] Add the offending code to the list of known failures. The documentation is not clear about the value of error code string. We were assuming "messaging/registration-token-not-registered" but we are seeing "registration-token-not-registered" in the logs. So this is added to the list. --- .../org/ostelco/prime/appnotifier/FirebaseAppNotifier.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app-notifier/src/main/kotlin/org/ostelco/prime/appnotifier/FirebaseAppNotifier.kt b/app-notifier/src/main/kotlin/org/ostelco/prime/appnotifier/FirebaseAppNotifier.kt index ccc5ac2c7..79d8d73bc 100644 --- a/app-notifier/src/main/kotlin/org/ostelco/prime/appnotifier/FirebaseAppNotifier.kt +++ b/app-notifier/src/main/kotlin/org/ostelco/prime/appnotifier/FirebaseAppNotifier.kt @@ -24,7 +24,8 @@ class FirebaseAppNotifier: AppNotifier { private val listOfFailureCodes = listOf( "messaging/invalid-recipient", "messaging/invalid-registration-token", - "messaging/registration-token-not-registered" + "messaging/registration-token-not-registered", + "registration-token-not-registered" ) override fun notify(notificationType: NotificationType, customerId: String, data: Map) = From 53ceeb49194bc585dba278dfc9602003501507be Mon Sep 17 00:00:00 2001 From: Prasanth Ullattil Date: Wed, 23 Oct 2019 11:27:47 +0200 Subject: [PATCH 132/173] Change the warning levels. --- .../org/ostelco/prime/appnotifier/FirebaseAppNotifier.kt | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/app-notifier/src/main/kotlin/org/ostelco/prime/appnotifier/FirebaseAppNotifier.kt b/app-notifier/src/main/kotlin/org/ostelco/prime/appnotifier/FirebaseAppNotifier.kt index 79d8d73bc..4d11c43bb 100644 --- a/app-notifier/src/main/kotlin/org/ostelco/prime/appnotifier/FirebaseAppNotifier.kt +++ b/app-notifier/src/main/kotlin/org/ostelco/prime/appnotifier/FirebaseAppNotifier.kt @@ -92,12 +92,15 @@ class FirebaseAppNotifier: AppNotifier { override fun onFailure(t: Throwable) { if (t is FirebaseMessagingException) { val errorCode = t.errorCode - logger.warn("Notification for $customerId with appId: ${token.applicationID} " + - "failed with errorCode: $errorCode") if (listOfFailureCodes.contains(errorCode)) { - logger.warn("Removing failed token for $customerId with appId: ${token.applicationID} " + + // Known failure, we should remove this token from our list + logger.info("Removing failed token (errorCode: $errorCode) for $customerId with appId: ${token.applicationID} " + "token: $token.token") store.removeNotificationToken(customerId, token.applicationID) + } else { + // Other failures we should look into. + logger.warn("Notification for $customerId with appId: ${token.applicationID} " + + "failed with errorCode: $errorCode") } } else { logger.warn("Notification for $customerId with appId: ${token.applicationID} " + From c7376df62f72faa0e3c465f6af944b5aadbf3a7b Mon Sep 17 00:00:00 2001 From: "Kjell M. Myksvoll" Date: Wed, 23 Oct 2019 13:55:36 +0200 Subject: [PATCH 133/173] Fixes incorrect Cypher query for fetching purchase records --- .../main/kotlin/org/ostelco/prime/storage/graph/Neo4jStore.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/neo4j-store/src/main/kotlin/org/ostelco/prime/storage/graph/Neo4jStore.kt b/neo4j-store/src/main/kotlin/org/ostelco/prime/storage/graph/Neo4jStore.kt index 010115b33..39daa7d46 100644 --- a/neo4j-store/src/main/kotlin/org/ostelco/prime/storage/graph/Neo4jStore.kt +++ b/neo4j-store/src/main/kotlin/org/ostelco/prime/storage/graph/Neo4jStore.kt @@ -2289,7 +2289,7 @@ object Neo4jStoreSingleton : GraphStore { override fun getPurchaseTransactions(start: Long, end: Long): Either> = readTransaction { read(""" MATCH(:${customerEntity.name})<-[:${forPurchaseByRelation.name}]-(pr:${purchaseRecordEntity.name}) - WHERE pr.timestamp >= '${start}' AND pr.timestamp <= '${end}' AND pr.`product/price/amount` > 0 + WHERE toInteger(pr.timestamp) >= ${start} AND toInteger(pr.timestamp) <= ${end} AND toInteger(pr.`product/price/amount`) > 0 RETURN pr """.trimIndent(), transaction) { statementResult -> statementResult.list { record -> From f3a9aba6ce4558ab53d2ba00578e8631295b3f9e Mon Sep 17 00:00:00 2001 From: mpeterss Date: Wed, 23 Oct 2019 14:24:27 +0200 Subject: [PATCH 134/173] Adding more tests Test for CCR-U with no new Requested-Service-Units only Reporting-Reason set to QHT. It is reporting usage on the Quota Holding Timer. --- .../main/kotlin/org/ostelco/at/pgw/OcsTest.kt | 58 +++++++++++++-- .../org/ostelco/diameter/test/TestHelper.kt | 6 +- .../org/ostelco/ocsgw/OcsApplicationTest.java | 72 ++++++++++++++++++- 3 files changed, 125 insertions(+), 11 deletions(-) diff --git a/acceptance-tests/src/main/kotlin/org/ostelco/at/pgw/OcsTest.kt b/acceptance-tests/src/main/kotlin/org/ostelco/at/pgw/OcsTest.kt index 7ce1ec9b2..d68d730f5 100644 --- a/acceptance-tests/src/main/kotlin/org/ostelco/at/pgw/OcsTest.kt +++ b/acceptance-tests/src/main/kotlin/org/ostelco/at/pgw/OcsTest.kt @@ -9,6 +9,7 @@ import org.ostelco.at.common.getLogger import org.ostelco.at.common.randomInt import org.ostelco.at.jersey.get import org.ostelco.diameter.model.FinalUnitAction +import org.ostelco.diameter.model.ReportingReason import org.ostelco.diameter.model.RequestType import org.ostelco.diameter.test.TestClient import org.ostelco.diameter.test.TestHelper @@ -79,7 +80,7 @@ class OcsTest { session ) ?: fail("Failed to create request") - TestHelper.createUpdateRequest(request.avps, msisdn, requestedBucketSize, usedBucketSize, ratingGroup, serviceIdentifier) + TestHelper.createUpdateRequest(request.avps, msisdn, requestedBucketSize, usedBucketSize, ratingGroup, serviceIdentifier, ReportingReason.QUOTA_EXHAUSTED) testClient.sendNextRequest(request, session) @@ -413,7 +414,7 @@ class OcsTest { session ) ?: fail("Failed to create request") - TestHelper.createUpdateRequest(updateRequest.avps, msisdn, BUCKET_SIZE, INITIAL_BALANCE, ratingGroup, serviceIdentifier) + TestHelper.createUpdateRequest(updateRequest.avps, msisdn, BUCKET_SIZE, INITIAL_BALANCE, ratingGroup, serviceIdentifier, ReportingReason.QUOTA_EXHAUSTED) testClient.sendNextRequest(updateRequest, session) @@ -686,7 +687,7 @@ class OcsTest { session ) ?: fail("Failed to create request") - TestHelper.createUpdateRequest(updateRequest.avps, msisdn, INITIAL_BALANCE + BUCKET_SIZE, 0L, ratingGroup, serviceIdentifier) + TestHelper.createUpdateRequest(updateRequest.avps, msisdn, INITIAL_BALANCE + BUCKET_SIZE, 0L, ratingGroup, serviceIdentifier, ReportingReason.QUOTA_EXHAUSTED) testClient.sendNextRequest(updateRequest, session) @@ -713,7 +714,7 @@ class OcsTest { session ) ?: fail("Failed to create request") - TestHelper.createUpdateRequest(updateRequest2.avps, msisdn, INITIAL_BALANCE, INITIAL_BALANCE, ratingGroup, serviceIdentifier) + TestHelper.createUpdateRequest(updateRequest2.avps, msisdn, INITIAL_BALANCE, INITIAL_BALANCE, ratingGroup, serviceIdentifier, ReportingReason.QUOTA_EXHAUSTED) testClient.sendNextRequest(updateRequest2, session) @@ -787,7 +788,7 @@ class OcsTest { session2 ) - TestHelper.createUpdateRequest(updateRequest3!!.avps, msisdn, 0L, -1L, ratingGroup, serviceIdentifier) + TestHelper.createUpdateRequest(updateRequest3!!.avps, msisdn, 0L, -1L, ratingGroup, serviceIdentifier, ReportingReason.QUOTA_EXHAUSTED) testClient.sendNextRequest(updateRequest3, session2) @@ -973,7 +974,7 @@ class OcsTest { session ) - TestHelper.createUpdateRequest(request!!.avps, msisdn, -1L, DEFAULT_REQUESTED_SERVICE_UNIT, ratingGroup, serviceIdentifier) + TestHelper.createUpdateRequest(request!!.avps, msisdn, -1L, DEFAULT_REQUESTED_SERVICE_UNIT, ratingGroup, serviceIdentifier, ReportingReason.QUOTA_EXHAUSTED) testClient.sendNextRequest(request, session) @@ -987,6 +988,51 @@ class OcsTest { } + /** + * Test that CCR-U with Reporting-Reason QHT ( no new Requested-Service-Unit ) works. + */ + + @Test + fun testNoMsccInCcrU() { + + val email = "ocs-${randomInt()}@test.com" + createCustomer(name = "Test OCS User", email = email) + + val msisdn = createSubscription(email = email) + + val ratingGroup = 10 + val serviceIdentifier = -1 + + val session = testClient.createSession(object{}.javaClass.enclosingMethod.name) ?: fail("Failed to create session") + + // This test assume that the default bucket size is set to 4000000L + simpleCreditControlRequestInit(session, msisdn,0L, DEFAULT_REQUESTED_SERVICE_UNIT, ratingGroup, serviceIdentifier) + + val updateRequest = testClient.createRequest( + DEST_REALM, + DEST_HOST, + session + ) + + TestHelper.createUpdateRequest(updateRequest!!.getAvps(), msisdn, -1L, 500_000L, ratingGroup, serviceIdentifier, ReportingReason.QHT) + + testClient.sendNextRequest(updateRequest, session) + + waitForAnswer(session.getSessionId()) + + val result = testClient.getAnswer(session.getSessionId()) + assertEquals(DIAMETER_SUCCESS, result!!.resultCode!!.toLong()) + val resultAvps = result.resultAvps + assertEquals(DEST_HOST, resultAvps.getAvp(Avp.ORIGIN_HOST).getUTF8String()) + assertEquals(DEST_REALM, resultAvps.getAvp(Avp.ORIGIN_REALM).getUTF8String()) + assertEquals(RequestType.UPDATE_REQUEST.toLong(), resultAvps.getAvp(Avp.CC_REQUEST_TYPE).getInteger32().toLong()) + val resultMSCC = resultAvps.getAvp(Avp.MULTIPLE_SERVICES_CREDIT_CONTROL) + assertNull(resultMSCC, "No requested MSCC") + assertEquals(86400, resultAvps.getAvp(Avp.VALIDITY_TIME).getInteger32().toLong()) + + } + + // pubsub answer can take up to 10 seconds on the emulator private fun waitForAnswer(sessionId: String) { diff --git a/diameter-test/src/main/kotlin/org/ostelco/diameter/test/TestHelper.kt b/diameter-test/src/main/kotlin/org/ostelco/diameter/test/TestHelper.kt index bca5c2a8b..0b44eb6ed 100644 --- a/diameter-test/src/main/kotlin/org/ostelco/diameter/test/TestHelper.kt +++ b/diameter-test/src/main/kotlin/org/ostelco/diameter/test/TestHelper.kt @@ -42,7 +42,7 @@ object TestHelper { } } - private fun addBucketRequest(ccrAvps: AvpSet, ratingGroup: Int, serviceIdentifier: Int, requestedBucketSize: Long, usedBucketSize: Long = 0, ccTime: Long = 0, ccServiceSpecificUnits: Long = 0) { + private fun addBucketRequest(ccrAvps: AvpSet, ratingGroup: Int, serviceIdentifier: Int, requestedBucketSize: Long, usedBucketSize: Long = 0, ccTime: Long = 0, ccServiceSpecificUnits: Long = 0, reportingReason: ReportingReason = ReportingReason.QUOTA_EXHAUSTED) { set(ccrAvps) { @@ -69,7 +69,7 @@ object TestHelper { if (usedBucketSize > 0) { group(Avp.USED_SERVICE_UNIT) { avp(Avp.CC_TOTAL_OCTETS, usedBucketSize, pFlag = true) - avp(Avp.REPORTING_REASON, ReportingReason.QUOTA_EXHAUSTED.ordinal, VENDOR_ID_3GPP, mFlag = true, pFlag = true) + avp(Avp.REPORTING_REASON, reportingReason, VENDOR_ID_3GPP, mFlag = true, pFlag = true) } } @@ -176,7 +176,7 @@ object TestHelper { } @JvmStatic - fun createUpdateRequest(ccrAvps: AvpSet, msisdn: String, requestedBucketSize: Long, usedBucketSize: Long, ratingGroup: Int, serviceIdentifier: Int) { + fun createUpdateRequest(ccrAvps: AvpSet, msisdn: String, requestedBucketSize: Long, usedBucketSize: Long, ratingGroup: Int, serviceIdentifier: Int, reportingReason: ReportingReason) { buildBasicRequest(ccrAvps, RequestType.UPDATE_REQUEST, requestNumber = 1) addUser(ccrAvps, msisdn = msisdn, imsi = IMSI) addBucketRequest(ccrAvps, ratingGroup, serviceIdentifier, requestedBucketSize = requestedBucketSize, usedBucketSize = usedBucketSize) diff --git a/ocsgw/src/test/java/org/ostelco/ocsgw/OcsApplicationTest.java b/ocsgw/src/test/java/org/ostelco/ocsgw/OcsApplicationTest.java index 424a5b793..131c9a42b 100644 --- a/ocsgw/src/test/java/org/ostelco/ocsgw/OcsApplicationTest.java +++ b/ocsgw/src/test/java/org/ostelco/ocsgw/OcsApplicationTest.java @@ -7,6 +7,7 @@ import org.jdiameter.api.Session; import org.junit.jupiter.api.*; import org.ostelco.diameter.model.ReAuthRequestType; +import org.ostelco.diameter.model.ReportingReason; import org.ostelco.diameter.model.RequestType; import org.ostelco.diameter.model.SessionContext; import org.ostelco.diameter.test.Result; @@ -22,6 +23,8 @@ import java.nio.charset.StandardCharsets; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; import static org.junit.Assert.fail; @@ -119,7 +122,7 @@ private void simpleCreditControlRequestUpdate(Session session, session ); - TestHelper.createUpdateRequest(request.getAvps(), MSISDN, requestedBucketSize, usedBucketSize, ratingGroup, serviceIdentifier); + TestHelper.createUpdateRequest(request.getAvps(), MSISDN, requestedBucketSize, usedBucketSize, ratingGroup, serviceIdentifier, ReportingReason.QUOTA_EXHAUSTED); client.sendNextRequest(request, session); @@ -201,7 +204,7 @@ public void simpleCreditControlRequestInitUpdateNoRSU() { ); // Only report usage, no request for new bucket - TestHelper.createUpdateRequest(request.getAvps(), MSISDN, -1L, 500_000L, ratingGroup, serviceIdentifier); + TestHelper.createUpdateRequest(request.getAvps(), MSISDN, -1L, 500_000L, ratingGroup, serviceIdentifier, ReportingReason.QUOTA_EXHAUSTED); client.sendNextRequest(request, session); @@ -347,6 +350,71 @@ public void testUnknownAVP() { } } + + @Test + @DisplayName("Test no MSCC in CCR-U") + public void testNoMsccInCcrU() { + + final int ratingGroup = 10; + final int serviceIdentifier = 1; + + Session session = client.createSession(new Object() {}.getClass().getEnclosingMethod().getName()); + Request initRequest = client.createRequest( + OCS_REALM, + OCS_HOST, + session + ); + + TestHelper.createInitRequest(initRequest.getAvps(), MSISDN, 500_000L, ratingGroup, serviceIdentifier); + + client.sendNextRequest(initRequest, session); + + waitForAnswer(session.getSessionId()); + + try { + Result result = client.getAnswer(session.getSessionId()); + assertEquals(DIAMETER_SUCCESS, result.getResultCode().longValue()); + AvpSet resultAvps = result.getResultAvps(); + assertEquals(OCS_HOST, resultAvps.getAvp(Avp.ORIGIN_HOST).getUTF8String()); + assertEquals(OCS_REALM, resultAvps.getAvp(Avp.ORIGIN_REALM).getUTF8String()); + assertEquals(RequestType.INITIAL_REQUEST, resultAvps.getAvp(Avp.CC_REQUEST_TYPE).getInteger32()); + Avp resultMSCC = resultAvps.getAvp(Avp.MULTIPLE_SERVICES_CREDIT_CONTROL); + assertEquals(DIAMETER_SUCCESS, resultMSCC.getGrouped().getAvp(Avp.RESULT_CODE).getInteger32()); + assertEquals(serviceIdentifier, resultMSCC.getGrouped().getAvp(Avp.SERVICE_IDENTIFIER_CCA).getUnsigned32()); + assertEquals(ratingGroup, resultMSCC.getGrouped().getAvp(Avp.RATING_GROUP).getUnsigned32()); + Avp granted = resultMSCC.getGrouped().getAvp(Avp.GRANTED_SERVICE_UNIT); + assertEquals(500_000L, granted.getGrouped().getAvp(Avp.CC_TOTAL_OCTETS).getUnsigned64()); + } catch (AvpDataException e) { + LOG.error("Failed to get Result-Code", e); + } + + Request updateRequest = client.createRequest( + OCS_REALM, + OCS_HOST, + session + ); + + TestHelper.createUpdateRequest(updateRequest.getAvps(),MSISDN, -1L, 500_000L, ratingGroup, serviceIdentifier, ReportingReason.QHT); + + client.sendNextRequest(updateRequest, session); + + waitForAnswer(session.getSessionId()); + + try { + Result result = client.getAnswer(session.getSessionId()); + assertEquals(DIAMETER_SUCCESS, result.getResultCode().longValue()); + AvpSet resultAvps = result.getResultAvps(); + assertEquals(OCS_HOST, resultAvps.getAvp(Avp.ORIGIN_HOST).getUTF8String()); + assertEquals(OCS_REALM, resultAvps.getAvp(Avp.ORIGIN_REALM).getUTF8String()); + assertEquals(RequestType.UPDATE_REQUEST, resultAvps.getAvp(Avp.CC_REQUEST_TYPE).getInteger32()); + Avp resultMSCC = resultAvps.getAvp(Avp.MULTIPLE_SERVICES_CREDIT_CONTROL); + assertNull( "No requested MSCC", resultMSCC); + assertEquals(86400, resultAvps.getAvp(Avp.VALIDITY_TIME).getInteger32()); + } catch (AvpDataException e) { + LOG.error("Failed to get Result-Code", e); + } + } + @Test public void testReAuthRequest() { Session session = client.createSession(new Object() {}.getClass().getEnclosingMethod().getName()); From e24470af828a03e9d5387265d91e4cf900c98985 Mon Sep 17 00:00:00 2001 From: mpeterss Date: Wed, 23 Oct 2019 14:34:59 +0200 Subject: [PATCH 135/173] Adding ReportingReason to OCSgw HA test --- ocsgw/src/test/java/org/ostelco/ocsgw/OcsHATest.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ocsgw/src/test/java/org/ostelco/ocsgw/OcsHATest.java b/ocsgw/src/test/java/org/ostelco/ocsgw/OcsHATest.java index 81d31b93d..bdc97ce47 100644 --- a/ocsgw/src/test/java/org/ostelco/ocsgw/OcsHATest.java +++ b/ocsgw/src/test/java/org/ostelco/ocsgw/OcsHATest.java @@ -5,6 +5,7 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; +import org.ostelco.diameter.model.ReportingReason; import org.ostelco.diameter.model.RequestType; import org.ostelco.diameter.test.Result; import org.ostelco.diameter.test.TestClient; @@ -230,7 +231,7 @@ private void haCreditControlRequestUpdate(Session session, String host) { session ); - TestHelper.createUpdateRequest(request.getAvps(), MSISDN, 400000L, 500000L, 1, 10); + TestHelper.createUpdateRequest(request.getAvps(), MSISDN, 400000L, 500000L, 1, 10, ReportingReason.QUOTA_EXHAUSTED); testPGW.sendNextRequest(request, session); From 68f4790ad8159b43cb1b63e381b0e69940df1a2b Mon Sep 17 00:00:00 2001 From: mpeterss Date: Wed, 23 Oct 2019 16:13:57 +0200 Subject: [PATCH 136/173] Remove ToDo that never has been updated --- docs/TODO.md | 24 ------------------------ 1 file changed, 24 deletions(-) delete mode 100644 docs/TODO.md diff --git a/docs/TODO.md b/docs/TODO.md deleted file mode 100644 index 68de918a5..000000000 --- a/docs/TODO.md +++ /dev/null @@ -1,24 +0,0 @@ -# TODO - - -* Start using something other than this file to track tasks. -* Get rid of the interface/impl pattern that is present in - the entities package. Since we simplified the data classes - with lombok, that separation gives little or no benefit. -* The interactions between the various types of messages are - confusing. Consider autogenerating sequence diagrams when - running tests to help document what is going on. -* Increase unit testability, restructure to make almost everything - unit testable. -* Refactor firebase database into something that is integration testable. -* Make a template project for dropwizard. -* Automatically generate javadoc in Travis build. -* Automatically publish javadoc to the github website. - https://github.com/blog/2233-publish-your-project-documentation-with-github-pages -* Look into making a healthcheck for firebase/firestore - - https://www.firebase.com/docs/web/guide/offline-capabilities.html#section-connection-state - this.firebaseDatabase.getReference("/.info/connected").addValueEventListener() - -* This looks like a good writeup of best (&worst) practices for testing - http://blog.codepipes.com/testing/software-testing-antipatterns.html We should - absorb this and adapt and institutionalise the practices we want to use. \ No newline at end of file From 3594ce19000785f0ffe0e7a34b0d7d47922571af Mon Sep 17 00:00:00 2001 From: mpeterss Date: Wed, 23 Oct 2019 16:14:55 +0200 Subject: [PATCH 137/173] Update doc on OCS gateway deployment --- docs/DEPLOY.md | 5 +---- docs/LOGS.md | 4 ++-- seagull/README.md | 7 +++++++ 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/docs/DEPLOY.md b/docs/DEPLOY.md index afcb81517..c7c10bef5 100644 --- a/docs/DEPLOY.md +++ b/docs/DEPLOY.md @@ -1,12 +1,9 @@ # Deploy to production -## Deploy OCSgw to GCP +## Deploy OCS gateway to GCP ./ocsgw/infra/script/deploy-ocsgw.sh -The script takes to parameters. First parameter is instance number [1/2/3]. Second parameter is environment [dev/prod]. -If no parameters passed it will deploy all instances in dev environment. - ## Deploy to kubernetes cluster on GCP Set env variable diff --git a/docs/LOGS.md b/docs/LOGS.md index 81b0429e4..13a1b8a2a 100644 --- a/docs/LOGS.md +++ b/docs/LOGS.md @@ -20,11 +20,11 @@ resource.labels.container_name="prime" * GKE container > private-cluster > All namespace_id * You can expand a single log and filter to log prime-only logs. -# OCSGW logs in GCP +# OCS Gateway logs in GCP Same steps as above. Use the filter below: ```properties -resource.type="global" +resource.type="gce_instance" logName="projects/GCP_PROJECT_ID/logs/ocsgw" ``` \ No newline at end of file diff --git a/seagull/README.md b/seagull/README.md index df5b48428..41c6da9e1 100644 --- a/seagull/README.md +++ b/seagull/README.md @@ -4,6 +4,13 @@ A Dockerized version of [Seagull](http://gull.sourceforge.net/ "Seagull") - a mu Based on https://github.com/codeghar/Seagull +#### Build image + +```` +docker build -t seagull . +```` + + ####To test with this Image with docker-compose * Use the docker-compose file for seagull to start Prime and OCS-gw From 8107743e3324bb325590eae21b5df9960f0bb1fa Mon Sep 17 00:00:00 2001 From: "Kjell M. Myksvoll" Date: Thu, 24 Oct 2019 09:49:11 +0200 Subject: [PATCH 138/173] Fixes incorrect reference to cypher record in 'getPurchaseTransactions' method --- .../main/kotlin/org/ostelco/prime/storage/graph/Neo4jStore.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/neo4j-store/src/main/kotlin/org/ostelco/prime/storage/graph/Neo4jStore.kt b/neo4j-store/src/main/kotlin/org/ostelco/prime/storage/graph/Neo4jStore.kt index 39daa7d46..bd407553d 100644 --- a/neo4j-store/src/main/kotlin/org/ostelco/prime/storage/graph/Neo4jStore.kt +++ b/neo4j-store/src/main/kotlin/org/ostelco/prime/storage/graph/Neo4jStore.kt @@ -2293,7 +2293,7 @@ object Neo4jStoreSingleton : GraphStore { RETURN pr """.trimIndent(), transaction) { statementResult -> statementResult.list { record -> - purchaseRecordEntity.createEntity(record["r"].asMap()) + purchaseRecordEntity.createEntity(record["pr"].asMap()) }.right() } } From a06b0b524d21558c6763dab2876d4f9004bbff35 Mon Sep 17 00:00:00 2001 From: Vihang Patil Date: Thu, 24 Oct 2019 13:13:05 +0200 Subject: [PATCH 139/173] Removed auth0 from customer api swagger spec --- prime/infra/dev/prime-customer-api.yaml | 34 ------------------------ prime/infra/prod/prime-customer-api.yaml | 34 ------------------------ 2 files changed, 68 deletions(-) diff --git a/prime/infra/dev/prime-customer-api.yaml b/prime/infra/dev/prime-customer-api.yaml index 4a01c61c2..3daba354b 100644 --- a/prime/infra/dev/prime-customer-api.yaml +++ b/prime/infra/dev/prime-customer-api.yaml @@ -71,7 +71,6 @@ paths: schema: type: string security: - - auth0_jwt: [] - firebase: [] "/context": get: @@ -87,7 +86,6 @@ paths: 404: description: "Customer not found." security: - - auth0_jwt: [] - firebase: [] "/customer": get: @@ -103,7 +101,6 @@ paths: 404: description: "Customer not found." security: - - auth0_jwt: [] - firebase: [] post: description: "Create a new customer." @@ -137,7 +134,6 @@ paths: 500: description: "Failed to store customer" security: - - auth0_jwt: [] - firebase: [] put: description: "Update an existing customer." @@ -167,7 +163,6 @@ paths: 500: description: "Failed to update customer info." security: - - auth0_jwt: [] - firebase: [] delete: description: "Remove customer." @@ -180,7 +175,6 @@ paths: 404: description: "Customer not found." security: - - auth0_jwt: [] - firebase: [] "/regions": get: @@ -194,7 +188,6 @@ paths: schema: $ref: '#/definitions/RegionDetailsList' security: - - auth0_jwt: [] - firebase: [] "/regions/{regionCode}": get: @@ -216,7 +209,6 @@ paths: 404: description: "Region not found." security: - - auth0_jwt: [] - firebase: [] "/regions/{regionCode}/kyc/jumio/scans": post: @@ -238,7 +230,6 @@ paths: 404: description: "Region not found." security: - - auth0_jwt: [] - firebase: [] "/regions/{regionCode}/kyc/jumio/scans/{scanId}": get: @@ -265,7 +256,6 @@ paths: 404: description: "Region or Scan not found." security: - - auth0_jwt: [] - firebase: [] "/regions/sg/kyc/myInfoConfig": get: @@ -281,7 +271,6 @@ paths: 404: description: "Config not found." security: - - auth0_jwt: [] - firebase: [] "/regions/sg/kyc/myInfo/{authorisationCode}": get: @@ -303,7 +292,6 @@ paths: 404: description: "Person Data not found." security: - - auth0_jwt: [] - firebase: [] "/regions/sg/kyc/myInfo/v3/config": get: @@ -319,7 +307,6 @@ paths: 404: description: "Config not found." security: - - auth0_jwt: [] - firebase: [] "/regions/sg/kyc/myInfo/v3/personData/{authorisationCode}": get: @@ -341,7 +328,6 @@ paths: 404: description: "Person Data not found." security: - - auth0_jwt: [] - firebase: [] "/regions/sg/kyc/dave/{nricFinId}": get: @@ -361,7 +347,6 @@ paths: 400: description: "Invalid NRIC/FIN ID" security: - - auth0_jwt: [] - firebase: [] "/regions/sg/kyc/profile": put: @@ -379,7 +364,6 @@ paths: 204: description: "Successfully updated customer's details." security: - - auth0_jwt: [] - firebase: [] "/regions/{regionCode}/simProfiles": get: @@ -403,7 +387,6 @@ paths: 500: description: "Service Unavailable" security: - - auth0_jwt: [] - firebase: [] post: description: "Provision SIM Profile for the user (identified by bearer token)." @@ -430,7 +413,6 @@ paths: 500: description: "Service Unavailable" security: - - auth0_jwt: [] - firebase: [] "/regions/{regionCode}/simProfiles/{iccId}": put: @@ -488,7 +470,6 @@ paths: 500: description: "Service Unavailable" security: - - auth0_jwt: [] - firebase: [] "/regions/{regionCode}/subscriptions": get: @@ -510,7 +491,6 @@ paths: 404: description: "No subscription found for this user." security: - - auth0_jwt: [] - firebase: [] "/subscriptions": get: @@ -526,7 +506,6 @@ paths: 404: description: "No subscription found for this user." security: - - auth0_jwt: [] - firebase: [] "/applicationToken": post: @@ -554,7 +533,6 @@ paths: 500: description: "Not able to store token." security: - - auth0_jwt: [] - firebase: [] "/paymentSources": get: @@ -572,7 +550,6 @@ paths: 503: description: "Service Unavailable" security: - - auth0_jwt: [] - firebase: [] post: description: "Add a new payment source for user" @@ -597,7 +574,6 @@ paths: 500: description: "Service Unavailable" security: - - auth0_jwt: [] - firebase: [] put: description: "Set the source as default for user" @@ -622,7 +598,6 @@ paths: 500: description: "Service Unavailable" security: - - auth0_jwt: [] - firebase: [] delete: description: "Remove a payment source for user" @@ -647,7 +622,6 @@ paths: 500: description: "Service Unavailable" security: - - auth0_jwt: [] - firebase: [] "/products": get: @@ -663,7 +637,6 @@ paths: 404: description: "No products found for the user." security: - - auth0_jwt: [] - firebase: [] "/products/{sku}/purchase": post: @@ -696,7 +669,6 @@ paths: 404: description: "Product not found." security: - - auth0_jwt: [] - firebase: [] "/purchases": get: @@ -717,7 +689,6 @@ paths: 500: description: "Service Unavailable" security: - - auth0_jwt: [] - firebase: [] "/bundles": get: @@ -733,7 +704,6 @@ paths: 404: description: "No bundle found for this user." security: - - auth0_jwt: [] - firebase: [] "/referred": get: @@ -749,7 +719,6 @@ paths: 404: description: "No referrals found for this user." security: - - auth0_jwt: [] - firebase: [] "/referred/by": get: @@ -765,7 +734,6 @@ paths: 404: description: "No 'referred by' found for this user." security: - - auth0_jwt: [] - firebase: [] "/graphql": get: @@ -787,7 +755,6 @@ paths: 404: description: "Not found" security: - - auth0_jwt: [] - firebase: [] post: description: "GraphQL POST endpoint" @@ -810,7 +777,6 @@ paths: 404: description: "Not found" security: - - auth0_jwt: [] - firebase: [] definitions: diff --git a/prime/infra/prod/prime-customer-api.yaml b/prime/infra/prod/prime-customer-api.yaml index 54d0df9bd..19ce866f0 100644 --- a/prime/infra/prod/prime-customer-api.yaml +++ b/prime/infra/prod/prime-customer-api.yaml @@ -71,7 +71,6 @@ paths: schema: type: string security: - - auth0_jwt: [] - firebase: [] "/context": get: @@ -87,7 +86,6 @@ paths: 404: description: "Customer not found." security: - - auth0_jwt: [] - firebase: [] "/customer": get: @@ -103,7 +101,6 @@ paths: 404: description: "Customer not found." security: - - auth0_jwt: [] - firebase: [] post: description: "Create a new customer." @@ -137,7 +134,6 @@ paths: 500: description: "Failed to store customer" security: - - auth0_jwt: [] - firebase: [] put: description: "Update an existing customer." @@ -167,7 +163,6 @@ paths: 500: description: "Failed to update customer info." security: - - auth0_jwt: [] - firebase: [] delete: description: "Remove customer." @@ -180,7 +175,6 @@ paths: 404: description: "Customer not found." security: - - auth0_jwt: [] - firebase: [] "/regions": get: @@ -194,7 +188,6 @@ paths: schema: $ref: '#/definitions/RegionDetailsList' security: - - auth0_jwt: [] - firebase: [] "/regions/{regionCode}": get: @@ -216,7 +209,6 @@ paths: 404: description: "Region not found." security: - - auth0_jwt: [] - firebase: [] "/regions/{regionCode}/kyc/jumio/scans": post: @@ -238,7 +230,6 @@ paths: 404: description: "Region not found." security: - - auth0_jwt: [] - firebase: [] "/regions/{regionCode}/kyc/jumio/scans/{scanId}": get: @@ -265,7 +256,6 @@ paths: 404: description: "Region or Scan not found." security: - - auth0_jwt: [] - firebase: [] "/regions/sg/kyc/myInfoConfig": get: @@ -281,7 +271,6 @@ paths: 404: description: "Config not found." security: - - auth0_jwt: [] - firebase: [] "/regions/sg/kyc/myInfo/{authorisationCode}": get: @@ -303,7 +292,6 @@ paths: 404: description: "Person Data not found." security: - - auth0_jwt: [] - firebase: [] "/regions/sg/kyc/myInfo/v3/config": get: @@ -319,7 +307,6 @@ paths: 404: description: "Config not found." security: - - auth0_jwt: [] - firebase: [] "/regions/sg/kyc/myInfo/v3/personData/{authorisationCode}": get: @@ -341,7 +328,6 @@ paths: 404: description: "Person Data not found." security: - - auth0_jwt: [] - firebase: [] "/regions/sg/kyc/dave/{nricFinId}": get: @@ -361,7 +347,6 @@ paths: 400: description: "Invalid NRIC/FIN ID" security: - - auth0_jwt: [] - firebase: [] "/regions/sg/kyc/profile": put: @@ -379,7 +364,6 @@ paths: 204: description: "Successfully updated customer's details." security: - - auth0_jwt: [] - firebase: [] "/regions/{regionCode}/simProfiles": get: @@ -403,7 +387,6 @@ paths: 500: description: "Service Unavailable" security: - - auth0_jwt: [] - firebase: [] post: description: "Provision SIM Profile for the user (identified by bearer token)." @@ -430,7 +413,6 @@ paths: 500: description: "Service Unavailable" security: - - auth0_jwt: [] - firebase: [] "/regions/{regionCode}/simProfiles/{iccId}": put: @@ -488,7 +470,6 @@ paths: 500: description: "Service Unavailable" security: - - auth0_jwt: [] - firebase: [] "/regions/{regionCode}/subscriptions": get: @@ -510,7 +491,6 @@ paths: 404: description: "No subscription found for this user." security: - - auth0_jwt: [] - firebase: [] "/subscriptions": get: @@ -526,7 +506,6 @@ paths: 404: description: "No subscription found for this user." security: - - auth0_jwt: [] - firebase: [] "/applicationToken": post: @@ -554,7 +533,6 @@ paths: 500: description: "Not able to store token." security: - - auth0_jwt: [] - firebase: [] "/paymentSources": get: @@ -572,7 +550,6 @@ paths: 503: description: "Service Unavailable" security: - - auth0_jwt: [] - firebase: [] post: description: "Add a new payment source for user" @@ -597,7 +574,6 @@ paths: 500: description: "Service Unavailable" security: - - auth0_jwt: [] - firebase: [] put: description: "Set the source as default for user" @@ -622,7 +598,6 @@ paths: 500: description: "Service Unavailable" security: - - auth0_jwt: [] - firebase: [] delete: description: "Remove a payment source for user" @@ -647,7 +622,6 @@ paths: 500: description: "Service Unavailable" security: - - auth0_jwt: [] - firebase: [] "/products": get: @@ -663,7 +637,6 @@ paths: 404: description: "No products found for the user." security: - - auth0_jwt: [] - firebase: [] "/products/{sku}/purchase": post: @@ -696,7 +669,6 @@ paths: 404: description: "Product not found." security: - - auth0_jwt: [] - firebase: [] "/purchases": get: @@ -717,7 +689,6 @@ paths: 500: description: "Service Unavailable" security: - - auth0_jwt: [] - firebase: [] "/bundles": get: @@ -733,7 +704,6 @@ paths: 404: description: "No bundle found for this user." security: - - auth0_jwt: [] - firebase: [] "/referred": get: @@ -749,7 +719,6 @@ paths: 404: description: "No referrals found for this user." security: - - auth0_jwt: [] - firebase: [] "/referred/by": get: @@ -765,7 +734,6 @@ paths: 404: description: "No 'referred by' found for this user." security: - - auth0_jwt: [] - firebase: [] "/graphql": get: @@ -787,7 +755,6 @@ paths: 404: description: "Not found" security: - - auth0_jwt: [] - firebase: [] post: description: "GraphQL POST endpoint" @@ -810,7 +777,6 @@ paths: 404: description: "Not found" security: - - auth0_jwt: [] - firebase: [] definitions: From a6810251b06f9da4212d236b3522d122c265a8de Mon Sep 17 00:00:00 2001 From: Vihang Patil Date: Thu, 24 Oct 2019 13:12:35 +0200 Subject: [PATCH 140/173] Added ADDRESS KYC to MY REGION --- .../endpoint/resources/KycResources.kt | 48 +++++++++++++------ .../endpoint/resources/RegionsResource.kt | 1 + .../customer/endpoint/store/SubscriberDAO.kt | 2 +- .../endpoint/store/SubscriberDAOImpl.kt | 4 +- .../ostelco/prime/storage/graph/Neo4jStore.kt | 17 ++++--- .../org/ostelco/prime/storage/Variants.kt | 2 +- prime/infra/dev/prime-customer-api.yaml | 19 +++++++- prime/infra/prod/prime-customer-api.yaml | 19 +++++++- 8 files changed, 85 insertions(+), 27 deletions(-) diff --git a/customer-endpoint/src/main/kotlin/org/ostelco/prime/customer/endpoint/resources/KycResources.kt b/customer-endpoint/src/main/kotlin/org/ostelco/prime/customer/endpoint/resources/KycResources.kt index 63023fad7..a9b37bbae 100644 --- a/customer-endpoint/src/main/kotlin/org/ostelco/prime/customer/endpoint/resources/KycResources.kt +++ b/customer-endpoint/src/main/kotlin/org/ostelco/prime/customer/endpoint/resources/KycResources.kt @@ -104,22 +104,19 @@ class SingaporeKycResource(private val dao: SubscriberDAO): KycResource(regionCo }.build() } - @EnableTracing - @PUT @Path("/profile") - @Produces(MediaType.APPLICATION_JSON) - fun saveProfile(@Auth token: AccessTokenPrincipal?, - @NotNull - @QueryParam("address") - address: String): Response = - if (token == null) { - Response.status(Response.Status.UNAUTHORIZED) - } else { - dao.saveAddress( - identity = token.identity, - address = address) - .responseBuilder(Response.Status.NO_CONTENT) - }.build() + fun saveProfile(): ProfileKycResource = ProfileKycResource(regionCode = "sg", dao = dao) +} + +/** + * [MalaysiaKycResource] uses [JumioKycResource] via parent class [KycResource]. + * It has Malaysia specific eKYC APIs. + * + */ +class MalaysiaKycResource(private val dao: SubscriberDAO): KycResource(regionCode = "my", dao = dao) { + + @Path("/profile") + fun saveProfile(): ProfileKycResource = ProfileKycResource(regionCode = "my", dao = dao) } class MyInfoResource(private val dao: SubscriberDAO, @@ -155,6 +152,27 @@ class MyInfoResource(private val dao: SubscriberDAO, }.build() } +class ProfileKycResource(private val regionCode: String, private val dao: SubscriberDAO) { + + @EnableTracing + @PUT + @Path("/") + @Produces(MediaType.APPLICATION_JSON) + fun saveProfile(@Auth token: AccessTokenPrincipal?, + @NotNull + @QueryParam("address") + address: String): Response = + if (token == null) { + Response.status(Response.Status.UNAUTHORIZED) + } else { + dao.saveAddress( + identity = token.identity, + address = address, + regionCode = regionCode) + .responseBuilder(Response.Status.NO_CONTENT) + }.build() +} + class JumioKycResource(private val regionCode: String, private val dao: SubscriberDAO) { @EnableTracing diff --git a/customer-endpoint/src/main/kotlin/org/ostelco/prime/customer/endpoint/resources/RegionsResource.kt b/customer-endpoint/src/main/kotlin/org/ostelco/prime/customer/endpoint/resources/RegionsResource.kt index 55deb96f2..704c59979 100644 --- a/customer-endpoint/src/main/kotlin/org/ostelco/prime/customer/endpoint/resources/RegionsResource.kt +++ b/customer-endpoint/src/main/kotlin/org/ostelco/prime/customer/endpoint/resources/RegionsResource.kt @@ -48,6 +48,7 @@ class RegionsResource(private val dao: SubscriberDAO) { regionCode: String ): KycResource = when (regionCode.toLowerCase()) { "sg" -> SingaporeKycResource(dao = dao) + "my" -> MalaysiaKycResource(dao = dao) else -> KycResource(regionCode = regionCode, dao = dao) } diff --git a/customer-endpoint/src/main/kotlin/org/ostelco/prime/customer/endpoint/store/SubscriberDAO.kt b/customer-endpoint/src/main/kotlin/org/ostelco/prime/customer/endpoint/store/SubscriberDAO.kt index 11ed81684..cf21c5b94 100644 --- a/customer-endpoint/src/main/kotlin/org/ostelco/prime/customer/endpoint/store/SubscriberDAO.kt +++ b/customer-endpoint/src/main/kotlin/org/ostelco/prime/customer/endpoint/store/SubscriberDAO.kt @@ -120,7 +120,7 @@ interface SubscriberDAO { fun checkNricFinIdUsingDave(identity: Identity, nricFinId: String): Either - fun saveAddress(identity: Identity, address: String): Either + fun saveAddress(identity: Identity, address: String, regionCode: String): Either // // Token diff --git a/customer-endpoint/src/main/kotlin/org/ostelco/prime/customer/endpoint/store/SubscriberDAOImpl.kt b/customer-endpoint/src/main/kotlin/org/ostelco/prime/customer/endpoint/store/SubscriberDAOImpl.kt index 0ef574f92..91b70d672 100644 --- a/customer-endpoint/src/main/kotlin/org/ostelco/prime/customer/endpoint/store/SubscriberDAOImpl.kt +++ b/customer-endpoint/src/main/kotlin/org/ostelco/prime/customer/endpoint/store/SubscriberDAOImpl.kt @@ -458,8 +458,8 @@ class SubscriberDAOImpl : SubscriberDAO { .mapLeft { mapStorageErrorToApiError("Invalid NRIC/FIN ID", ApiErrorCode.INVALID_NRIC_FIN_ID, it) } } - override fun saveAddress(identity: Identity, address: String): Either { - return storage.saveAddress(identity = identity, address = address) + override fun saveAddress(identity: Identity, address: String, regionCode: String): Either { + return storage.saveAddress(identity = identity, address = address, regionCode = regionCode) .mapLeft { mapStorageErrorToApiError("Failed to save address", ApiErrorCode.FAILED_TO_SAVE_ADDRESS, it) } } diff --git a/neo4j-store/src/main/kotlin/org/ostelco/prime/storage/graph/Neo4jStore.kt b/neo4j-store/src/main/kotlin/org/ostelco/prime/storage/graph/Neo4jStore.kt index bd407553d..6a68b3513 100644 --- a/neo4j-store/src/main/kotlin/org/ostelco/prime/storage/graph/Neo4jStore.kt +++ b/neo4j-store/src/main/kotlin/org/ostelco/prime/storage/graph/Neo4jStore.kt @@ -1817,7 +1817,8 @@ object Neo4jStoreSingleton : GraphStore { // override fun saveAddress( identity: org.ostelco.prime.model.Identity, - address: String): Either { + address: String, + regionCode: String): Either { return IO { Either.monad().binding { @@ -1827,14 +1828,14 @@ object Neo4jStoreSingleton : GraphStore { // set ADDRESS KYC Status to Pending setKycStatus( customer = customer, - regionCode = "sg", + regionCode = regionCode, kycType = ADDRESS, kycStatus = KycStatus.PENDING).bind() secureArchiveService.archiveEncrypted( customerId = customer.id, fileName = "address", - regionCodes = listOf("sg"), + regionCodes = listOf(regionCode), dataMap = mapOf( "address" to address.toByteArray()) ).bind() @@ -1842,7 +1843,7 @@ object Neo4jStoreSingleton : GraphStore { // set ADDRESS KYC Status to Approved setKycStatus( customer = customer, - regionCode = "sg", + regionCode = regionCode, kycType = ADDRESS).bind() }.fix() }.unsafeRunSync() @@ -1944,14 +1945,18 @@ object Neo4jStoreSingleton : GraphStore { private fun getKycStatusMapForRegion(regionCode: String): Map { return when (regionCode) { "sg" -> setOf(JUMIO, MY_INFO, NRIC_FIN, ADDRESS) + "my" -> setOf(JUMIO, ADDRESS) else -> setOf(JUMIO) }.map { it to KycStatus.PENDING }.toMap() } private fun getApprovedKycTypeSetList(regionCode: String): List> { return when (regionCode) { - "sg" -> listOf(setOf(MY_INFO, ADDRESS), - setOf(JUMIO, ADDRESS)) + "sg" -> listOf( + setOf(MY_INFO, ADDRESS), + setOf(JUMIO, ADDRESS) + ) + "my" -> listOf(setOf(JUMIO, ADDRESS)) else -> listOf(setOf(JUMIO)) } } diff --git a/prime-modules/src/main/kotlin/org/ostelco/prime/storage/Variants.kt b/prime-modules/src/main/kotlin/org/ostelco/prime/storage/Variants.kt index 8a6c231fd..1c3e7c92e 100644 --- a/prime-modules/src/main/kotlin/org/ostelco/prime/storage/Variants.kt +++ b/prime-modules/src/main/kotlin/org/ostelco/prime/storage/Variants.kt @@ -188,7 +188,7 @@ interface ClientGraphStore { /** * Save address and Phone number */ - fun saveAddress(identity: Identity, address: String): Either + fun saveAddress(identity: Identity, address: String, regionCode: String): Either } data class ConsumptionResult(val msisdnAnalyticsId: String, val granted: Long, val balance: Long) diff --git a/prime/infra/dev/prime-customer-api.yaml b/prime/infra/dev/prime-customer-api.yaml index 3daba354b..da2a663a6 100644 --- a/prime/infra/dev/prime-customer-api.yaml +++ b/prime/infra/dev/prime-customer-api.yaml @@ -350,7 +350,24 @@ paths: - firebase: [] "/regions/sg/kyc/profile": put: - description: "Update Singapore Customer's address and phone number." + description: "Update address for Singapore region." + produces: + - application/json + operationId: "updateDetails" + parameters: + - name: address + in: query + description: "Customer's Address" + required: true + type: string + responses: + 204: + description: "Successfully updated customer's details." + security: + - firebase: [] + "/regions/my/kyc/profile": + put: + description: "Update address for Malaysia region." produces: - application/json operationId: "updateDetails" diff --git a/prime/infra/prod/prime-customer-api.yaml b/prime/infra/prod/prime-customer-api.yaml index 19ce866f0..577fd7b49 100644 --- a/prime/infra/prod/prime-customer-api.yaml +++ b/prime/infra/prod/prime-customer-api.yaml @@ -350,7 +350,24 @@ paths: - firebase: [] "/regions/sg/kyc/profile": put: - description: "Update Singapore Customer's address and phone number." + description: "Update address for Singapore region." + produces: + - application/json + operationId: "updateDetails" + parameters: + - name: address + in: query + description: "Customer's Address" + required: true + type: string + responses: + 204: + description: "Successfully updated customer's details." + security: + - firebase: [] + "/regions/my/kyc/profile": + put: + description: "Update address for Malaysia region." produces: - application/json operationId: "updateDetails" From 68d25e0cc87e63a6433474441276cbd269a5a0c5 Mon Sep 17 00:00:00 2001 From: Vihang Patil Date: Thu, 24 Oct 2019 13:14:00 +0200 Subject: [PATCH 141/173] Updated prime-version --- prime/build.gradle.kts | 2 +- prime/script/start.sh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/prime/build.gradle.kts b/prime/build.gradle.kts index 70cc71287..e4b9b112d 100644 --- a/prime/build.gradle.kts +++ b/prime/build.gradle.kts @@ -10,7 +10,7 @@ plugins { } // Update version in [script/start.sh] too. -version = "1.68.0" +version = "1.69.0" dependencies { // interface module between prime and prime-modules diff --git a/prime/script/start.sh b/prime/script/start.sh index 05349e2a1..66306f6b0 100755 --- a/prime/script/start.sh +++ b/prime/script/start.sh @@ -5,5 +5,5 @@ exec java \ -Dfile.encoding=UTF-8 \ --add-opens java.base/java.lang=ALL-UNNAMED \ --add-opens java.base/java.io=ALL-UNNAMED \ - -agentpath:/opt/cprof/profiler_java_agent.so=-cprof_service=prime,-cprof_service_version=1.68.0,-logtostderr,-minloglevel=2,-cprof_enable_heap_sampling \ + -agentpath:/opt/cprof/profiler_java_agent.so=-cprof_service=prime,-cprof_service_version=1.69.0,-logtostderr,-minloglevel=2,-cprof_enable_heap_sampling \ -jar /prime.jar server /config/config.yaml From 1a8c11b09be446e09639cf6290ae947932ce0a7d Mon Sep 17 00:00:00 2001 From: Cyriaque Brousse <3663243+cyriaquebrousse@users.noreply.github.com> Date: Thu, 24 Oct 2019 14:04:21 +0200 Subject: [PATCH 142/173] Fix exception reporting in DelegatePubSubPublisher One call to logger.warn instead of two --- .../analytics/publishers/DelegatePubSubPublisher.kt | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/analytics-module/src/main/kotlin/org/ostelco/prime/analytics/publishers/DelegatePubSubPublisher.kt b/analytics-module/src/main/kotlin/org/ostelco/prime/analytics/publishers/DelegatePubSubPublisher.kt index 599ba22ff..d0972d651 100644 --- a/analytics-module/src/main/kotlin/org/ostelco/prime/analytics/publishers/DelegatePubSubPublisher.kt +++ b/analytics-module/src/main/kotlin/org/ostelco/prime/analytics/publishers/DelegatePubSubPublisher.kt @@ -57,17 +57,18 @@ class DelegatePubSubPublisher( ApiFutures.addCallback(future, object : ApiFutureCallback { override fun onFailure(throwable: Throwable) { + logger.warn("Error publishing message in topic: $topicId") if (throwable is ApiException) { // details on the API exception - logger.warn("Status code: {}", throwable.statusCode.code) - logger.warn("Retrying: {}", throwable.isRetryable) + logger.warn("Pubsub topic: $topicId\n" + + "Status code: ${throwable.statusCode.code}\n" + + "Retrying: ${throwable.isRetryable}") } - logger.warn("Error publishing message in topic: $topicId") } override fun onSuccess(messageId: String) { // Once published, returns server-assigned message ids (unique within the topic) - logger.debug("Published message $messageId") + logger.debug("Published message $messageId to topic $topicId") } }, DataConsumptionInfoPublisher.singleThreadScheduledExecutor) } From 8a556ce14a0ee4dc5a3dec7b8231f43d573a82f2 Mon Sep 17 00:00:00 2001 From: Vihang Patil Date: Thu, 24 Oct 2019 14:45:55 +0200 Subject: [PATCH 143/173] Review comments --- .../src/main/kotlin/org/ostelco/at/okhttp/Tests.kt | 4 ++-- prime/infra/dev/prime-customer-api.yaml | 4 ++-- prime/infra/prod/prime-customer-api.yaml | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/acceptance-tests/src/main/kotlin/org/ostelco/at/okhttp/Tests.kt b/acceptance-tests/src/main/kotlin/org/ostelco/at/okhttp/Tests.kt index 55c2a485b..d79a4e7d4 100644 --- a/acceptance-tests/src/main/kotlin/org/ostelco/at/okhttp/Tests.kt +++ b/acceptance-tests/src/main/kotlin/org/ostelco/at/okhttp/Tests.kt @@ -833,7 +833,7 @@ class SingaporeKycTest { assertEquals(regionDetails, regionDetailsList[sgRegionIndex], "RegionDetails do not match") } - client.updateDetails("Singapore") + client.updateDetailsForSG("Singapore") run { val regionDetailsList = client.allRegions @@ -921,7 +921,7 @@ class SingaporeKycTest { assertEquals(regionDetails, regionDetailsList[sgRegionIndex], "RegionDetails do not match") } - client.updateDetails("Singapore") + client.updateDetailsForSG("Singapore") run { val regionDetailsList = client.allRegions diff --git a/prime/infra/dev/prime-customer-api.yaml b/prime/infra/dev/prime-customer-api.yaml index da2a663a6..6dff36ed3 100644 --- a/prime/infra/dev/prime-customer-api.yaml +++ b/prime/infra/dev/prime-customer-api.yaml @@ -353,7 +353,7 @@ paths: description: "Update address for Singapore region." produces: - application/json - operationId: "updateDetails" + operationId: "updateDetailsForSG" parameters: - name: address in: query @@ -370,7 +370,7 @@ paths: description: "Update address for Malaysia region." produces: - application/json - operationId: "updateDetails" + operationId: "updateDetailsForMY" parameters: - name: address in: query diff --git a/prime/infra/prod/prime-customer-api.yaml b/prime/infra/prod/prime-customer-api.yaml index 577fd7b49..3edb5896d 100644 --- a/prime/infra/prod/prime-customer-api.yaml +++ b/prime/infra/prod/prime-customer-api.yaml @@ -353,7 +353,7 @@ paths: description: "Update address for Singapore region." produces: - application/json - operationId: "updateDetails" + operationId: "updateDetailsForSG" parameters: - name: address in: query @@ -370,7 +370,7 @@ paths: description: "Update address for Malaysia region." produces: - application/json - operationId: "updateDetails" + operationId: "updateDetailsForMY" parameters: - name: address in: query From 96060da10495c0d2e97fb47c9047632dc59e8761 Mon Sep 17 00:00:00 2001 From: mpeterss Date: Thu, 24 Oct 2019 14:57:30 +0200 Subject: [PATCH 144/173] Added message to log --- .../prime/analytics/publishers/DelegatePubSubPublisher.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/analytics-module/src/main/kotlin/org/ostelco/prime/analytics/publishers/DelegatePubSubPublisher.kt b/analytics-module/src/main/kotlin/org/ostelco/prime/analytics/publishers/DelegatePubSubPublisher.kt index d0972d651..6ae2aeeee 100644 --- a/analytics-module/src/main/kotlin/org/ostelco/prime/analytics/publishers/DelegatePubSubPublisher.kt +++ b/analytics-module/src/main/kotlin/org/ostelco/prime/analytics/publishers/DelegatePubSubPublisher.kt @@ -61,6 +61,7 @@ class DelegatePubSubPublisher( if (throwable is ApiException) { // details on the API exception logger.warn("Pubsub topic: $topicId\n" + + "Message : ${throwable.message}\n" + "Status code: ${throwable.statusCode.code}\n" + "Retrying: ${throwable.isRetryable}") } From ceb47f8ebbcbcf5f96df5d9f462f60f274abca9e Mon Sep 17 00:00:00 2001 From: mpeterss Date: Thu, 24 Oct 2019 15:33:34 +0200 Subject: [PATCH 145/173] Fix error message --- .../ostelco/prime/ocs/consumption/pubsub/PubSubClient.kt | 6 ++++-- .../ostelco/ocsgw/datasource/protobuf/PubSubDataSource.kt | 6 ++++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/ocs-ktc/src/main/kotlin/org/ostelco/prime/ocs/consumption/pubsub/PubSubClient.kt b/ocs-ktc/src/main/kotlin/org/ostelco/prime/ocs/consumption/pubsub/PubSubClient.kt index 5b6d83d04..b8d57f771 100644 --- a/ocs-ktc/src/main/kotlin/org/ostelco/prime/ocs/consumption/pubsub/PubSubClient.kt +++ b/ocs-ktc/src/main/kotlin/org/ostelco/prime/ocs/consumption/pubsub/PubSubClient.kt @@ -101,8 +101,10 @@ class PubSubClient( override fun onFailure(throwable: Throwable) { if (throwable is ApiException) { // details on the API exception - logger.error("Status code: {}", throwable.statusCode.code) - logger.error("Retrying: {}", throwable.isRetryable) + logger.warn("Pubsub messageId: $messageId\n" + + "Message : ${throwable.message}\n" + + "Status code: ${throwable.statusCode.code}\n" + + "Retrying: ${throwable.isRetryable}") } logger.error("Error sending CCR Request to PubSub") } diff --git a/ocsgw/src/main/java/org/ostelco/ocsgw/datasource/protobuf/PubSubDataSource.kt b/ocsgw/src/main/java/org/ostelco/ocsgw/datasource/protobuf/PubSubDataSource.kt index 396188732..90f412244 100644 --- a/ocsgw/src/main/java/org/ostelco/ocsgw/datasource/protobuf/PubSubDataSource.kt +++ b/ocsgw/src/main/java/org/ostelco/ocsgw/datasource/protobuf/PubSubDataSource.kt @@ -119,8 +119,10 @@ class PubSubDataSource( override fun onFailure(throwable: Throwable) { if (throwable is ApiException) { // details on the API exception - logger.warn("Status code: {}", throwable.statusCode.code) - logger.warn("Retrying: {}", throwable.isRetryable) + logger.warn("Pubsub topic: $ccaTopicId\n" + + "Message : ${throwable.message}\n" + + "Status code: ${throwable.statusCode.code}\n" + + "Retrying: ${throwable.isRetryable}") } logger.warn("Error sending CCR Request to PubSub") } From 7957d21b9a2825f92f76098b9825532ec7de37de Mon Sep 17 00:00:00 2001 From: Cyriaque Brousse <3663243+cyriaquebrousse@users.noreply.github.com> Date: Thu, 24 Oct 2019 15:34:20 +0200 Subject: [PATCH 146/173] Change to if/else structure to avoid double logging --- .../prime/analytics/publishers/DelegatePubSubPublisher.kt | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/analytics-module/src/main/kotlin/org/ostelco/prime/analytics/publishers/DelegatePubSubPublisher.kt b/analytics-module/src/main/kotlin/org/ostelco/prime/analytics/publishers/DelegatePubSubPublisher.kt index 6ae2aeeee..255e5cbac 100644 --- a/analytics-module/src/main/kotlin/org/ostelco/prime/analytics/publishers/DelegatePubSubPublisher.kt +++ b/analytics-module/src/main/kotlin/org/ostelco/prime/analytics/publishers/DelegatePubSubPublisher.kt @@ -57,13 +57,14 @@ class DelegatePubSubPublisher( ApiFutures.addCallback(future, object : ApiFutureCallback { override fun onFailure(throwable: Throwable) { - logger.warn("Error publishing message in topic: $topicId") if (throwable is ApiException) { // details on the API exception - logger.warn("Pubsub topic: $topicId\n" + - "Message : ${throwable.message}\n" + + logger.warn("Error publishing message to Pubsub topic: $topicId\n" + + "Message: ${throwable.message}\n" + "Status code: ${throwable.statusCode.code}\n" + "Retrying: ${throwable.isRetryable}") + } else { + logger.warn("Error publishing message to Pubsub topic: $topicId") } } From dbbca65eb89bc254b9cf4ced1bcbad7d2e66e73c Mon Sep 17 00:00:00 2001 From: mpeterss Date: Thu, 24 Oct 2019 20:24:21 +0200 Subject: [PATCH 147/173] Added more debug information --- .../org/ostelco/prime/ocs/consumption/pubsub/PubSubClient.kt | 3 ++- .../org/ostelco/ocsgw/datasource/protobuf/PubSubDataSource.kt | 4 +++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/ocs-ktc/src/main/kotlin/org/ostelco/prime/ocs/consumption/pubsub/PubSubClient.kt b/ocs-ktc/src/main/kotlin/org/ostelco/prime/ocs/consumption/pubsub/PubSubClient.kt index b8d57f771..1779c4c6c 100644 --- a/ocs-ktc/src/main/kotlin/org/ostelco/prime/ocs/consumption/pubsub/PubSubClient.kt +++ b/ocs-ktc/src/main/kotlin/org/ostelco/prime/ocs/consumption/pubsub/PubSubClient.kt @@ -105,8 +105,9 @@ class PubSubClient( "Message : ${throwable.message}\n" + "Status code: ${throwable.statusCode.code}\n" + "Retrying: ${throwable.isRetryable}") + } else { + logger.error("Error sending CCR Request to PubSub. messageId: $messageId") } - logger.error("Error sending CCR Request to PubSub") } override fun onSuccess(messageId: String) { diff --git a/ocsgw/src/main/java/org/ostelco/ocsgw/datasource/protobuf/PubSubDataSource.kt b/ocsgw/src/main/java/org/ostelco/ocsgw/datasource/protobuf/PubSubDataSource.kt index 90f412244..ec5c5eafb 100644 --- a/ocsgw/src/main/java/org/ostelco/ocsgw/datasource/protobuf/PubSubDataSource.kt +++ b/ocsgw/src/main/java/org/ostelco/ocsgw/datasource/protobuf/PubSubDataSource.kt @@ -120,11 +120,13 @@ class PubSubDataSource( if (throwable is ApiException) { // details on the API exception logger.warn("Pubsub topic: $ccaTopicId\n" + + "RequestId : ${creditControlRequestInfo.requestId} \n" + "Message : ${throwable.message}\n" + "Status code: ${throwable.statusCode.code}\n" + "Retrying: ${throwable.isRetryable}") + } else { + logger.warn("Error sending CCR Request to PubSub. topic: $ccaTopicId requestId ${creditControlRequestInfo.requestId}") } - logger.warn("Error sending CCR Request to PubSub") } override fun onSuccess(messageId: String) { From eb2437212cae74db38fecd3717709ec437034353 Mon Sep 17 00:00:00 2001 From: Vihang Patil Date: Sat, 26 Oct 2019 11:12:24 +0200 Subject: [PATCH 148/173] Removed empty path annotation, which is redundent and gives warnings. --- .../ostelco/prime/customer/endpoint/resources/KycResources.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/customer-endpoint/src/main/kotlin/org/ostelco/prime/customer/endpoint/resources/KycResources.kt b/customer-endpoint/src/main/kotlin/org/ostelco/prime/customer/endpoint/resources/KycResources.kt index a9b37bbae..a97959a39 100644 --- a/customer-endpoint/src/main/kotlin/org/ostelco/prime/customer/endpoint/resources/KycResources.kt +++ b/customer-endpoint/src/main/kotlin/org/ostelco/prime/customer/endpoint/resources/KycResources.kt @@ -156,7 +156,6 @@ class ProfileKycResource(private val regionCode: String, private val dao: Subscr @EnableTracing @PUT - @Path("/") @Produces(MediaType.APPLICATION_JSON) fun saveProfile(@Auth token: AccessTokenPrincipal?, @NotNull From a8a6b7ccda9e1c86905d0b1feea1d806dac49eb7 Mon Sep 17 00:00:00 2001 From: Vihang Patil Date: Sat, 26 Oct 2019 11:53:10 +0200 Subject: [PATCH 149/173] Remove duplicate nodes --- .../main/kotlin/org/ostelco/prime/storage/graph/Neo4jStore.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/neo4j-store/src/main/kotlin/org/ostelco/prime/storage/graph/Neo4jStore.kt b/neo4j-store/src/main/kotlin/org/ostelco/prime/storage/graph/Neo4jStore.kt index 6a68b3513..2d7561f70 100644 --- a/neo4j-store/src/main/kotlin/org/ostelco/prime/storage/graph/Neo4jStore.kt +++ b/neo4j-store/src/main/kotlin/org/ostelco/prime/storage/graph/Neo4jStore.kt @@ -931,7 +931,7 @@ object Neo4jStoreSingleton : GraphStore { -[:${customerToSegmentRelation.name}]->(:${segmentEntity.name}) <-[:${offerToSegmentRelation.name}]-(:${offerEntity.name}) -[:${offerToProductRelation.name}]->(product:${productEntity.name}) - RETURN product; + RETURN DISTINCT product; """.trimIndent(), transaction) { statementResult -> Either.right(statementResult From 8f89a56bf3bd7eb1dd0f27201761186915491107 Mon Sep 17 00:00:00 2001 From: Vihang Patil Date: Sat, 26 Oct 2019 12:00:49 +0200 Subject: [PATCH 150/173] Updated prime fix version --- prime/build.gradle.kts | 2 +- prime/script/start.sh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/prime/build.gradle.kts b/prime/build.gradle.kts index e4b9b112d..0de3b6aae 100644 --- a/prime/build.gradle.kts +++ b/prime/build.gradle.kts @@ -10,7 +10,7 @@ plugins { } // Update version in [script/start.sh] too. -version = "1.69.0" +version = "1.69.1" dependencies { // interface module between prime and prime-modules diff --git a/prime/script/start.sh b/prime/script/start.sh index 66306f6b0..94545612b 100755 --- a/prime/script/start.sh +++ b/prime/script/start.sh @@ -5,5 +5,5 @@ exec java \ -Dfile.encoding=UTF-8 \ --add-opens java.base/java.lang=ALL-UNNAMED \ --add-opens java.base/java.io=ALL-UNNAMED \ - -agentpath:/opt/cprof/profiler_java_agent.so=-cprof_service=prime,-cprof_service_version=1.69.0,-logtostderr,-minloglevel=2,-cprof_enable_heap_sampling \ + -agentpath:/opt/cprof/profiler_java_agent.so=-cprof_service=prime,-cprof_service_version=1.69.1,-logtostderr,-minloglevel=2,-cprof_enable_heap_sampling \ -jar /prime.jar server /config/config.yaml From 6ea9605b9fc677fdd4392f0bb43866a475f72556 Mon Sep 17 00:00:00 2001 From: Prasanth Ullattil Date: Mon, 28 Oct 2019 16:45:49 +0100 Subject: [PATCH 151/173] Cleanup of the Authentication path. No more warning message shown for auth timeouts. --- houston/src/actions/alert.actions.js | 4 ++-- houston/src/components/Search/Alert.js | 9 +++++++-- houston/src/components/Search/CustomerList.js | 1 - houston/src/helpers/api.js | 10 ++++++++-- 4 files changed, 17 insertions(+), 7 deletions(-) diff --git a/houston/src/actions/alert.actions.js b/houston/src/actions/alert.actions.js index 234f82655..71c569b43 100644 --- a/houston/src/actions/alert.actions.js +++ b/houston/src/actions/alert.actions.js @@ -15,8 +15,8 @@ const reducer = handleActions( [alertSuccess]: (state, { payload }) => { return { ...state, type: 'alert-success', message: payload }; }, - [alertError]: (state, { payload: { message } }) => { - return { ...state, type: 'alert-danger', message }; + [alertError]: (state, { payload: { message, code } }) => { + return { ...state, type: 'alert-danger', message, code }; }, [clearAlert]: () => defaultState }, diff --git a/houston/src/components/Search/Alert.js b/houston/src/components/Search/Alert.js index 0c14f7a6f..0521c6b27 100644 --- a/houston/src/components/Search/Alert.js +++ b/houston/src/components/Search/Alert.js @@ -4,6 +4,7 @@ import { connect } from 'react-redux'; import { Alert } from 'reactstrap'; import { alertActions } from '../../actions/alert.actions'; +import { authConstants } from '../../actions/auth.actions'; function AlertMessage(props) { function onDismiss(e) { @@ -12,8 +13,12 @@ function AlertMessage(props) { const visible = (props.alert && props.alert.type === 'alert-danger'); if (!visible) { - return null - }; + return null; + } + // Don't show Authentication failed message + if (props.alert.code === authConstants.AUTHENTICATION_FAILURE) { + return null; + } return ( {props.alert.message} diff --git a/houston/src/components/Search/CustomerList.js b/houston/src/components/Search/CustomerList.js index eb3a874a5..6c7a49d41 100644 --- a/houston/src/components/Search/CustomerList.js +++ b/houston/src/components/Search/CustomerList.js @@ -64,7 +64,6 @@ CustomerRow.propTypes = { export const CustomerList = props => { // If customer is set, remove the list. - console.log(JSON.stringify(props)) if (props.customer.id || !Array.isArray(props.subscribers)) { return null; } diff --git a/houston/src/helpers/api.js b/houston/src/helpers/api.js index d6fdfba23..c976da2b8 100644 --- a/houston/src/helpers/api.js +++ b/houston/src/helpers/api.js @@ -28,7 +28,6 @@ const apiCaller = async (endpoint, method, body, allowEmptyResponse, params = [] //console.log('API URL:', fullUrl); if (authHeaderResolver === null) { - console.log("apiCaller: authHeaderResolver not set"); return Promise.reject(); } const auth = authHeaderResolver(); @@ -99,6 +98,13 @@ export function transformError(errorObj) { // Action key that carries API call info interpreted by this Redux middleware. export const CALL_API = 'Call API'; +class ApiError extends Error { + constructor(message, code) { + super(message); + this.code = code; + } +} + // A Redux middleware that interprets actions with CALL_API info specified. // Performs the call and promises when such actions are dispatched. export default (store) => (next) => (action) => { @@ -134,7 +140,7 @@ export default (store) => (next) => (action) => { errorObj: error, error: transformError(error) })); - throw new Error(transformError(error)); + throw new ApiError(transformError(error), error.code); } ); } From 9b4bf273399f0eec5771545f6eff64430c157447 Mon Sep 17 00:00:00 2001 From: Prasanth Ullattil Date: Mon, 28 Oct 2019 16:53:23 +0100 Subject: [PATCH 152/173] Add error class name --- houston/src/helpers/api.js | 1 + 1 file changed, 1 insertion(+) diff --git a/houston/src/helpers/api.js b/houston/src/helpers/api.js index c976da2b8..2183e75be 100644 --- a/houston/src/helpers/api.js +++ b/houston/src/helpers/api.js @@ -101,6 +101,7 @@ export const CALL_API = 'Call API'; class ApiError extends Error { constructor(message, code) { super(message); + this.name = "ApiError"; this.code = code; } } From 371bde68d0eda3aefab1aa2512345c8047b5f449 Mon Sep 17 00:00:00 2001 From: Prasanth Ullattil Date: Tue, 29 Oct 2019 13:39:00 +0100 Subject: [PATCH 153/173] Cleanup of Customer list --- houston/src/components/Search/CustomerList.js | 42 +++++++------------ 1 file changed, 15 insertions(+), 27 deletions(-) diff --git a/houston/src/components/Search/CustomerList.js b/houston/src/components/Search/CustomerList.js index 6c7a49d41..1a14490b3 100644 --- a/houston/src/components/Search/CustomerList.js +++ b/houston/src/components/Search/CustomerList.js @@ -1,7 +1,7 @@ import React from 'react'; import { connect } from 'react-redux'; import PropTypes from 'prop-types'; -import { Col, Row, Card, CardBody, Button } from 'reactstrap'; +import { Button, Card, CardBody, CardText, Col, Row } from 'reactstrap'; import Highlighter from "react-highlight-words"; import { subscriberActions } from '../../actions/subscriber.actions'; @@ -31,26 +31,17 @@ export const CustomerRow = props => { } return ( -
- - {'Name:'} - {convertToHighlightedText(props.customer.nickname, query)} - - - {'Email:'} - {convertToHighlightedText(props.customer.contactEmail, query)} - - - {'ID:'} - {convertToHighlightedText(props.customer.id, query)} - -
- - - - - -
); + + + + {convertToHighlightedText(props.customer.nickname, query)}
+ {convertToHighlightedText(props.customer.contactEmail, query)}
+ {convertToHighlightedText(props.customer.id, query)}
+
+ +
+
+
); } CustomerRow.propTypes = { @@ -72,18 +63,15 @@ export const CustomerList = props => { listItems = props.subscribers.map((customer, index) =>
-
+
+
); } return (
Found following matching records...
- - - {listItems} - - + {listItems}
); } From 0873b73fd77d2d178b0c037945959834f51affde Mon Sep 17 00:00:00 2001 From: Prasanth Ullattil Date: Tue, 29 Oct 2019 13:43:52 +0100 Subject: [PATCH 154/173] Hide unused buttons --- houston/src/components/Search/DataUsage.js | 4 ++-- houston/src/components/Search/Subscription.js | 5 ++--- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/houston/src/components/Search/DataUsage.js b/houston/src/components/Search/DataUsage.js index d2c8a64e0..d612553f4 100644 --- a/houston/src/components/Search/DataUsage.js +++ b/houston/src/components/Search/DataUsage.js @@ -45,9 +45,9 @@ class DataUsage extends React.Component { {`Remaining ${props.balance}.`} - + {/* - + */}
{'Phone number:'} {`${subscription.msisdn}`} -
- + {/* - + */} Date: Tue, 29 Oct 2019 13:59:24 +0100 Subject: [PATCH 155/173] Style updates --- houston/src/components/Search/AuditLogs.js | 1 - houston/src/components/Search/DataUsage.js | 4 ++-- houston/src/components/Search/PaymentHistory.js | 4 ++-- houston/src/components/Search/Profile.js | 8 ++++---- houston/src/components/Search/SearchResults.js | 8 ++++++-- 5 files changed, 14 insertions(+), 11 deletions(-) diff --git a/houston/src/components/Search/AuditLogs.js b/houston/src/components/Search/AuditLogs.js index b6c131c2f..1c917c2aa 100644 --- a/houston/src/components/Search/AuditLogs.js +++ b/houston/src/components/Search/AuditLogs.js @@ -76,7 +76,6 @@ class AuditLogs extends React.Component { return ( - Audit Logs + Data balance - Data balance {`Remaining ${props.balance}.`} diff --git a/houston/src/components/Search/PaymentHistory.js b/houston/src/components/Search/PaymentHistory.js index 7f6faca0c..ce1f96263 100644 --- a/houston/src/components/Search/PaymentHistory.js +++ b/houston/src/components/Search/PaymentHistory.js @@ -1,7 +1,7 @@ import React from 'react'; import { connect } from 'react-redux'; import PropTypes from 'prop-types'; -import { Table, Card, CardBody, CardTitle, Button, UncontrolledTooltip } from 'reactstrap'; +import { Table, Card, CardBody, CardHeader, Button, UncontrolledTooltip } from 'reactstrap'; import { subscriberActions } from '../../actions/subscriber.actions'; import { convertTimestampToDate } from '../../helpers'; @@ -114,8 +114,8 @@ class PaymentHistory extends React.Component { return ( + Payment History - Payment History diff --git a/houston/src/components/Search/Profile.js b/houston/src/components/Search/Profile.js index 039a2d4d4..e8265e767 100644 --- a/houston/src/components/Search/Profile.js +++ b/houston/src/components/Search/Profile.js @@ -1,7 +1,7 @@ import React from 'react'; import { connect } from 'react-redux'; import PropTypes from 'prop-types'; -import { Col, Row, Card, CardBody, CardTitle, Button } from 'reactstrap'; +import { Col, Row, Card, CardBody, CardHeader, Button } from 'reactstrap'; import { subscriberActions } from '../../actions/subscriber.actions'; import Subscription from './Subscription'; @@ -41,15 +41,15 @@ class Profile extends React.Component { if (Array.isArray(props.subscriptions.items)) { listItems = props.subscriptions.items.map((subscription, index) =>
- +
); } return ( + Customer - User Profile {'Name:'}{`${props.profile.nickname}`} @@ -92,7 +92,7 @@ Profile.propTypes = { subscriptions: PropTypes.shape({ items: PropTypes.array, }), - deleteUser:PropTypes.func.isRequired + deleteUser: PropTypes.func.isRequired }; function mapStateToProps(state) { diff --git a/houston/src/components/Search/SearchResults.js b/houston/src/components/Search/SearchResults.js index 9c88d56ef..2269bcf5b 100644 --- a/houston/src/components/Search/SearchResults.js +++ b/houston/src/components/Search/SearchResults.js @@ -1,5 +1,5 @@ import React from 'react'; -import { Card, CardBody, CardTitle, Nav, NavItem, NavLink, TabContent, TabPane } from 'reactstrap'; +import { Card, CardBody, CardHeader, Nav, NavItem, NavLink, TabContent, TabPane } from 'reactstrap'; import classnames from 'classnames'; import Context from "./Context"; @@ -63,13 +63,14 @@ class SearchResults extends React.Component { +


+ Push Notifications - Push Notifications
+
+
+
From c7787db9666698b77207c71684e8ee582c947aa8 Mon Sep 17 00:00:00 2001 From: Prasanth Ullattil Date: Tue, 29 Oct 2019 14:25:27 +0100 Subject: [PATCH 156/173] Fix warnings --- houston/src/components/Search/AuditLogs.js | 2 +- houston/src/components/Search/CustomerList.js | 2 +- houston/src/components/Search/DataUsage.js | 2 +- houston/src/components/Search/Subscription.js | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/houston/src/components/Search/AuditLogs.js b/houston/src/components/Search/AuditLogs.js index 1c917c2aa..6dbbb29ea 100644 --- a/houston/src/components/Search/AuditLogs.js +++ b/houston/src/components/Search/AuditLogs.js @@ -2,7 +2,7 @@ import React from 'react'; import _ from 'lodash'; import { connect } from 'react-redux'; import PropTypes from 'prop-types'; -import { Card, CardBody, CardTitle } from 'reactstrap'; +import { Card, CardBody } from 'reactstrap'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' import ReactTable from "react-table" import "react-table/react-table.css"; diff --git a/houston/src/components/Search/CustomerList.js b/houston/src/components/Search/CustomerList.js index 1a14490b3..686b85d9a 100644 --- a/houston/src/components/Search/CustomerList.js +++ b/houston/src/components/Search/CustomerList.js @@ -1,7 +1,7 @@ import React from 'react'; import { connect } from 'react-redux'; import PropTypes from 'prop-types'; -import { Button, Card, CardBody, CardText, Col, Row } from 'reactstrap'; +import { Button, Card, CardBody, CardText } from 'reactstrap'; import Highlighter from "react-highlight-words"; import { subscriberActions } from '../../actions/subscriber.actions'; diff --git a/houston/src/components/Search/DataUsage.js b/houston/src/components/Search/DataUsage.js index 570f4210a..41a63cbce 100644 --- a/houston/src/components/Search/DataUsage.js +++ b/houston/src/components/Search/DataUsage.js @@ -1,7 +1,7 @@ import React from 'react'; import { connect } from 'react-redux'; import PropTypes from 'prop-types'; -import { Col, Row, Card, CardBody, CardHeader, Button } from 'reactstrap'; +import { Col, Row, Card, CardBody, CardHeader } from 'reactstrap'; import WarningModal from '../Shared/WarningModal'; import { humanReadableBytes } from '../../helpers'; diff --git a/houston/src/components/Search/Subscription.js b/houston/src/components/Search/Subscription.js index d91fc1171..1de5d40ed 100644 --- a/houston/src/components/Search/Subscription.js +++ b/houston/src/components/Search/Subscription.js @@ -1,6 +1,6 @@ import React from 'react'; import PropTypes from 'prop-types'; -import { Col, Row, Button } from 'reactstrap'; +import { Col, Row } from 'reactstrap'; import WarningModal from '../Shared/WarningModal'; From 654e57786bd0d0fb0fc71bd6ccc77c2fe808054a Mon Sep 17 00:00:00 2001 From: "Kjell M. Myksvoll" Date: Wed, 30 Oct 2019 08:59:17 +0100 Subject: [PATCH 157/173] Updates 'get purhcase records' query to follow the FOR_PURCHASE_OF relation --- .../main/kotlin/org/ostelco/prime/storage/graph/Neo4jStore.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/neo4j-store/src/main/kotlin/org/ostelco/prime/storage/graph/Neo4jStore.kt b/neo4j-store/src/main/kotlin/org/ostelco/prime/storage/graph/Neo4jStore.kt index 2d7561f70..9c34e083f 100644 --- a/neo4j-store/src/main/kotlin/org/ostelco/prime/storage/graph/Neo4jStore.kt +++ b/neo4j-store/src/main/kotlin/org/ostelco/prime/storage/graph/Neo4jStore.kt @@ -2293,7 +2293,7 @@ object Neo4jStoreSingleton : GraphStore { override fun getPurchaseTransactions(start: Long, end: Long): Either> = readTransaction { read(""" - MATCH(:${customerEntity.name})<-[:${forPurchaseByRelation.name}]-(pr:${purchaseRecordEntity.name}) + MATCH(pr:${purchaseRecordEntity.name})-[:${forPurchaseOfRelation.name}]->(:${productEntity.name}) WHERE toInteger(pr.timestamp) >= ${start} AND toInteger(pr.timestamp) <= ${end} AND toInteger(pr.`product/price/amount`) > 0 RETURN pr """.trimIndent(), transaction) { statementResult -> From 74efb36c6b81aa1072b6a5e9d773232a63746a4f Mon Sep 17 00:00:00 2001 From: "Kjell M. Myksvoll" Date: Wed, 30 Oct 2019 09:00:52 +0100 Subject: [PATCH 158/173] Removes 'properties' field from transaction check as it is not used --- .../kotlin/org/ostelco/prime/storage/graph/Neo4jStore.kt | 6 ++---- .../prime/paymentprocessor/StripePaymentProcessor.kt | 5 +---- .../kotlin/org/ostelco/prime/paymentprocessor/core/Model.kt | 2 +- 3 files changed, 4 insertions(+), 9 deletions(-) diff --git a/neo4j-store/src/main/kotlin/org/ostelco/prime/storage/graph/Neo4jStore.kt b/neo4j-store/src/main/kotlin/org/ostelco/prime/storage/graph/Neo4jStore.kt index 9c34e083f..1d0cfb2f1 100644 --- a/neo4j-store/src/main/kotlin/org/ostelco/prime/storage/graph/Neo4jStore.kt +++ b/neo4j-store/src/main/kotlin/org/ostelco/prime/storage/graph/Neo4jStore.kt @@ -2338,8 +2338,7 @@ object Neo4jStoreSingleton : GraphStore { "amount" to it.product.price.amount, "currency" to it.product.price.currency.toLowerCase(), "refunded" to (it.refund != null), - "created" to it.timestamp, - "properties" to it.properties) + "created" to it.timestamp) }.plus( paymentRecords.map { mapOf("type" to "paymentRecord", @@ -2347,8 +2346,7 @@ object Neo4jStoreSingleton : GraphStore { "amount" to it.amount, "currency" to it.currency, /* (Stripe) Always lower case. */ "refunded" to it.refunded, - "created" to it.created, - "properties" to it.properties) + "created" to it.created) } ).groupBy { it["chargeId"].hashCode() + it["amount"].hashCode() + diff --git a/payment-processor/src/main/kotlin/org/ostelco/prime/paymentprocessor/StripePaymentProcessor.kt b/payment-processor/src/main/kotlin/org/ostelco/prime/paymentprocessor/StripePaymentProcessor.kt index c0339b300..d4d2480b0 100644 --- a/payment-processor/src/main/kotlin/org/ostelco/prime/paymentprocessor/StripePaymentProcessor.kt +++ b/payment-processor/src/main/kotlin/org/ostelco/prime/paymentprocessor/StripePaymentProcessor.kt @@ -584,10 +584,7 @@ class StripePaymentProcessor : PaymentProcessor { amount = it.amount.toInt(), /* Note: 'int' is used internally for amounts. */ currency = it.currency, created = Instant.ofEpochSecond(it.created).toEpochMilli(), - refunded = it.refunded, - properties = mapOf("invoiceId" to intent.invoice, - "customerId" to intent.customer) - ) + refunded = it.refunded) } }.flatMap { it diff --git a/prime-modules/src/main/kotlin/org/ostelco/prime/paymentprocessor/core/Model.kt b/prime-modules/src/main/kotlin/org/ostelco/prime/paymentprocessor/core/Model.kt index 6b8ed3613..34977d802 100644 --- a/prime-modules/src/main/kotlin/org/ostelco/prime/paymentprocessor/core/Model.kt +++ b/prime-modules/src/main/kotlin/org/ostelco/prime/paymentprocessor/core/Model.kt @@ -32,4 +32,4 @@ data class InvoiceInfo(val id: String) data class InvoicePaymentInfo(val id: String, val chargeId: String) -data class PaymentTransactionInfo(val id: String, val amount: Int, val currency: String, val created: Long, val refunded: Boolean, val properties: Map) +data class PaymentTransactionInfo(val id: String, val amount: Int, val currency: String, val created: Long, val refunded: Boolean) From 98531cd57109ec826212337878e4c473f7e20e08 Mon Sep 17 00:00:00 2001 From: "Kjell M. Myksvoll" Date: Wed, 30 Oct 2019 08:59:17 +0100 Subject: [PATCH 159/173] Updates 'get purhcase records' query to follow the FOR_PURCHASE_OF relation --- .../main/kotlin/org/ostelco/prime/storage/graph/Neo4jStore.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/neo4j-store/src/main/kotlin/org/ostelco/prime/storage/graph/Neo4jStore.kt b/neo4j-store/src/main/kotlin/org/ostelco/prime/storage/graph/Neo4jStore.kt index 2d7561f70..9c34e083f 100644 --- a/neo4j-store/src/main/kotlin/org/ostelco/prime/storage/graph/Neo4jStore.kt +++ b/neo4j-store/src/main/kotlin/org/ostelco/prime/storage/graph/Neo4jStore.kt @@ -2293,7 +2293,7 @@ object Neo4jStoreSingleton : GraphStore { override fun getPurchaseTransactions(start: Long, end: Long): Either> = readTransaction { read(""" - MATCH(:${customerEntity.name})<-[:${forPurchaseByRelation.name}]-(pr:${purchaseRecordEntity.name}) + MATCH(pr:${purchaseRecordEntity.name})-[:${forPurchaseOfRelation.name}]->(:${productEntity.name}) WHERE toInteger(pr.timestamp) >= ${start} AND toInteger(pr.timestamp) <= ${end} AND toInteger(pr.`product/price/amount`) > 0 RETURN pr """.trimIndent(), transaction) { statementResult -> From 905cc7bc6a0e1bf269f50f3486ada8a6a68c25da Mon Sep 17 00:00:00 2001 From: "Kjell M. Myksvoll" Date: Wed, 30 Oct 2019 09:00:52 +0100 Subject: [PATCH 160/173] Removes 'properties' field from transaction check as it is not used --- .../kotlin/org/ostelco/prime/storage/graph/Neo4jStore.kt | 6 ++---- .../prime/paymentprocessor/StripePaymentProcessor.kt | 5 +---- .../kotlin/org/ostelco/prime/paymentprocessor/core/Model.kt | 2 +- 3 files changed, 4 insertions(+), 9 deletions(-) diff --git a/neo4j-store/src/main/kotlin/org/ostelco/prime/storage/graph/Neo4jStore.kt b/neo4j-store/src/main/kotlin/org/ostelco/prime/storage/graph/Neo4jStore.kt index 9c34e083f..1d0cfb2f1 100644 --- a/neo4j-store/src/main/kotlin/org/ostelco/prime/storage/graph/Neo4jStore.kt +++ b/neo4j-store/src/main/kotlin/org/ostelco/prime/storage/graph/Neo4jStore.kt @@ -2338,8 +2338,7 @@ object Neo4jStoreSingleton : GraphStore { "amount" to it.product.price.amount, "currency" to it.product.price.currency.toLowerCase(), "refunded" to (it.refund != null), - "created" to it.timestamp, - "properties" to it.properties) + "created" to it.timestamp) }.plus( paymentRecords.map { mapOf("type" to "paymentRecord", @@ -2347,8 +2346,7 @@ object Neo4jStoreSingleton : GraphStore { "amount" to it.amount, "currency" to it.currency, /* (Stripe) Always lower case. */ "refunded" to it.refunded, - "created" to it.created, - "properties" to it.properties) + "created" to it.created) } ).groupBy { it["chargeId"].hashCode() + it["amount"].hashCode() + diff --git a/payment-processor/src/main/kotlin/org/ostelco/prime/paymentprocessor/StripePaymentProcessor.kt b/payment-processor/src/main/kotlin/org/ostelco/prime/paymentprocessor/StripePaymentProcessor.kt index c0339b300..d4d2480b0 100644 --- a/payment-processor/src/main/kotlin/org/ostelco/prime/paymentprocessor/StripePaymentProcessor.kt +++ b/payment-processor/src/main/kotlin/org/ostelco/prime/paymentprocessor/StripePaymentProcessor.kt @@ -584,10 +584,7 @@ class StripePaymentProcessor : PaymentProcessor { amount = it.amount.toInt(), /* Note: 'int' is used internally for amounts. */ currency = it.currency, created = Instant.ofEpochSecond(it.created).toEpochMilli(), - refunded = it.refunded, - properties = mapOf("invoiceId" to intent.invoice, - "customerId" to intent.customer) - ) + refunded = it.refunded) } }.flatMap { it diff --git a/prime-modules/src/main/kotlin/org/ostelco/prime/paymentprocessor/core/Model.kt b/prime-modules/src/main/kotlin/org/ostelco/prime/paymentprocessor/core/Model.kt index 6b8ed3613..34977d802 100644 --- a/prime-modules/src/main/kotlin/org/ostelco/prime/paymentprocessor/core/Model.kt +++ b/prime-modules/src/main/kotlin/org/ostelco/prime/paymentprocessor/core/Model.kt @@ -32,4 +32,4 @@ data class InvoiceInfo(val id: String) data class InvoicePaymentInfo(val id: String, val chargeId: String) -data class PaymentTransactionInfo(val id: String, val amount: Int, val currency: String, val created: Long, val refunded: Boolean, val properties: Map) +data class PaymentTransactionInfo(val id: String, val amount: Int, val currency: String, val created: Long, val refunded: Boolean) From 8964887e03a8c8ccab4e2cb72d20eede734cdc77 Mon Sep 17 00:00:00 2001 From: Prasanth Ullattil Date: Wed, 30 Oct 2019 09:58:35 +0100 Subject: [PATCH 161/173] Rearrange search bar --- houston/src/components/Search/SearchForm.js | 24 +++++++++++++-------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/houston/src/components/Search/SearchForm.js b/houston/src/components/Search/SearchForm.js index 0a46adab6..36a671419 100644 --- a/houston/src/components/Search/SearchForm.js +++ b/houston/src/components/Search/SearchForm.js @@ -1,6 +1,6 @@ import React, { useState } from 'react'; import PropTypes from 'prop-types'; -import { Button, Form, FormGroup, Label, Input } from 'reactstrap'; +import { Button, Col, Form, FormGroup, Input, Label, Row } from 'reactstrap'; import { getTextType } from '../../helpers'; @@ -39,15 +39,21 @@ export default function SearchForm(props) {

- - + + +
+ + + + + + - ); From 7545a9e0cd5a7deb89fb7e31128558f1ac6e7b83 Mon Sep 17 00:00:00 2001 From: Prasanth Ullattil Date: Wed, 30 Oct 2019 10:04:10 +0100 Subject: [PATCH 162/173] Upgrade redux --- houston/package.json | 2 +- houston/yarn.lock | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/houston/package.json b/houston/package.json index 11a6cd617..42a696bdb 100644 --- a/houston/package.json +++ b/houston/package.json @@ -23,7 +23,7 @@ "react-dom": "^16.10.2", "react-highlight-words": "^0.16.0", "react-json-view": "^1.19.1", - "react-redux": "^7.0.1", + "react-redux": "^7.1.1", "react-router": "^5.1.2", "react-router-dom": "^5.1.2", "react-scripts": "3.2.0", diff --git a/houston/yarn.lock b/houston/yarn.lock index 6e052f94d..c4c89dfdc 100644 --- a/houston/yarn.lock +++ b/houston/yarn.lock @@ -8767,7 +8767,7 @@ react-popper@^1.3.3: typed-styles "^0.0.7" warning "^4.0.2" -react-redux@^7.0.1: +react-redux@^7.1.1: version "7.1.1" resolved "https://registry.yarnpkg.com/react-redux/-/react-redux-7.1.1.tgz#ce6eee1b734a7a76e0788b3309bf78ff6b34fa0a" integrity sha512-QsW0vcmVVdNQzEkrgzh2W3Ksvr8cqpAv5FhEk7tNEft+5pp7rXxAudTz3VOPawRkLIepItpkEIyLcN/VVXzjTg== From 82fd8b1fd3fb75235ae0dd750ec8e468a124ce7d Mon Sep 17 00:00:00 2001 From: Prasanth Ullattil Date: Wed, 30 Oct 2019 11:54:15 +0100 Subject: [PATCH 163/173] Remove illegal characters from search query --- .../org/ostelco/prime/support/resources/HoustonResources.kt | 2 ++ houston/src/components/Search/SearchForm.js | 3 ++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/customer-support-endpoint/src/main/kotlin/org/ostelco/prime/support/resources/HoustonResources.kt b/customer-support-endpoint/src/main/kotlin/org/ostelco/prime/support/resources/HoustonResources.kt index a04d51a1f..ba4e2d87f 100644 --- a/customer-support-endpoint/src/main/kotlin/org/ostelco/prime/support/resources/HoustonResources.kt +++ b/customer-support-endpoint/src/main/kotlin/org/ostelco/prime/support/resources/HoustonResources.kt @@ -63,6 +63,8 @@ class ProfilesResource { Response.status(Response.Status.UNAUTHORIZED) } else { val trimmedQuery = query.trim() + .replace("'", "") + .replace("\"", "") if (isMsisdn(trimmedQuery)) { logger.info("${token.name} Accessing profile for msisdn: $query") getProfileListForMsisdn(trimmedQuery) diff --git a/houston/src/components/Search/SearchForm.js b/houston/src/components/Search/SearchForm.js index 36a671419..aa30e15a3 100644 --- a/houston/src/components/Search/SearchForm.js +++ b/houston/src/components/Search/SearchForm.js @@ -8,7 +8,8 @@ function useFormInput(initialValue, submit) { const [value, setValue] = useState(initialValue); function onChange(e) { - setValue(e.target.value); + const cleanValue = e.target.value.replace(/['"]+/g,''); + setValue(cleanValue); } function onSubmit(e) { From 1f229700b8c0381aa2d1ff1510268df84f3f58b1 Mon Sep 17 00:00:00 2001 From: Vihang Patil Date: Tue, 29 Oct 2019 11:33:30 +0100 Subject: [PATCH 164/173] Added ExCustomer entity to track deleted customers --- .../kotlin/org/ostelco/prime/storage/graph/model/Model.kt | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/neo4j-store/src/main/kotlin/org/ostelco/prime/storage/graph/model/Model.kt b/neo4j-store/src/main/kotlin/org/ostelco/prime/storage/graph/model/Model.kt index 397befbcd..c1c6a1c26 100644 --- a/neo4j-store/src/main/kotlin/org/ostelco/prime/storage/graph/model/Model.kt +++ b/neo4j-store/src/main/kotlin/org/ostelco/prime/storage/graph/model/Model.kt @@ -38,3 +38,10 @@ data class Segment(override val id: String) : HasId { } data class Offer(override val id: String) : HasId + +data class ExCustomer( + override val id:String, + val terminationDate: String) : HasId { + + companion object +} From 2d2eb3e08abf35fabc911bc09bc37e0a1703d1cd Mon Sep 17 00:00:00 2001 From: Vihang Patil Date: Tue, 29 Oct 2019 11:33:50 +0100 Subject: [PATCH 165/173] DSL for ExCustomer --- .../kotlin/org/ostelco/prime/dsl/Syntax.kt | 52 +++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/neo4j-store/src/main/kotlin/org/ostelco/prime/dsl/Syntax.kt b/neo4j-store/src/main/kotlin/org/ostelco/prime/dsl/Syntax.kt index 735abed51..0cf01877b 100644 --- a/neo4j-store/src/main/kotlin/org/ostelco/prime/dsl/Syntax.kt +++ b/neo4j-store/src/main/kotlin/org/ostelco/prime/dsl/Syntax.kt @@ -9,9 +9,13 @@ import org.ostelco.prime.model.PurchaseRecord import org.ostelco.prime.model.Region import org.ostelco.prime.model.ScanInformation import org.ostelco.prime.model.Subscription +import org.ostelco.prime.storage.graph.Neo4jStoreSingleton.customerRegionRelation import org.ostelco.prime.storage.graph.Neo4jStoreSingleton.customerToBundleRelation import org.ostelco.prime.storage.graph.Neo4jStoreSingleton.customerToSegmentRelation import org.ostelco.prime.storage.graph.Neo4jStoreSingleton.customerToSimProfileRelation +import org.ostelco.prime.storage.graph.Neo4jStoreSingleton.exCustomerRegionRelation +import org.ostelco.prime.storage.graph.Neo4jStoreSingleton.exCustomerToSimProfileRelation +import org.ostelco.prime.storage.graph.Neo4jStoreSingleton.exSubscriptionRelation import org.ostelco.prime.storage.graph.Neo4jStoreSingleton.forPurchaseByRelation import org.ostelco.prime.storage.graph.Neo4jStoreSingleton.forPurchaseOfRelation import org.ostelco.prime.storage.graph.Neo4jStoreSingleton.identifiesRelation @@ -23,6 +27,7 @@ import org.ostelco.prime.storage.graph.Neo4jStoreSingleton.subscriptionRelation import org.ostelco.prime.storage.graph.Neo4jStoreSingleton.subscriptionSimProfileRelation import org.ostelco.prime.storage.graph.Neo4jStoreSingleton.subscriptionToBundleRelation import org.ostelco.prime.storage.graph.RelationType +import org.ostelco.prime.storage.graph.model.ExCustomer import org.ostelco.prime.storage.graph.model.Identity import org.ostelco.prime.storage.graph.model.Segment import org.ostelco.prime.storage.graph.model.SimProfile @@ -122,6 +127,24 @@ data class CustomerContext(override val id: String) : EntityContext(Cu toId = segment.id) } +data class ExCustomerContext(override val id: String) : EntityContext(ExCustomer::class, id) { + + infix fun had(simProfile: SimProfileContext) = RelationExpression( + relationType = exCustomerToSimProfileRelation, + fromId = id, + toId = simProfile.id) + + infix fun subscribedTo(subscription: SubscriptionContext) = RelationExpression( + relationType = exSubscriptionRelation, + fromId = id, + toId = subscription.id) + + infix fun belongedTo(region: RegionContext) = RelationExpression( + relationType = exCustomerRegionRelation, + fromId = id, + toId = region.id) +} + data class BundleContext(override val id: String) : EntityContext(Bundle::class, id) data class RegionContext(override val id: String) : EntityContext(Region::class, id) @@ -202,6 +225,12 @@ infix fun Customer.Companion.referredBy(customer: CustomerContext) = fromId = customer.id ) +// +// ExCustomer +// + +infix fun ExCustomer.Companion.withId(id: String): ExCustomerContext = ExCustomerContext(id) + // // Bundle // @@ -226,6 +255,18 @@ infix fun Region.Companion.linkedToSimProfile(simProfile: SimProfileContext) = fromId = simProfile.id ) +infix fun Region.Companion.linkedToCustomer(customer: CustomerContext) = + RelatedFromClause( + relationType = customerRegionRelation, + fromId = customer.id + ) + +infix fun Region.Companion.linkedToExCustomer(exCustomer: ExCustomerContext) = + RelatedFromClause( + relationType = exCustomerRegionRelation, + fromId = exCustomer.id + ) + // // SimProfiles // @@ -244,6 +285,11 @@ infix fun SimProfile.Companion.forCustomer(customer: CustomerContext) = fromId = customer.id ) +infix fun SimProfile.Companion.forExCustomer(exCustomer: ExCustomerContext) = + RelatedFromClause( + relationType = exCustomerToSimProfileRelation, + fromId = exCustomer.id + ) // // Subscription // @@ -262,6 +308,12 @@ infix fun Subscription.Companion.subscribedBy(customer: CustomerContext) = fromId = customer.id ) +infix fun Subscription.Companion.wasSubscribedBy(exCustomer: ExCustomerContext) = + RelatedFromClause( + relationType = exSubscriptionRelation, + fromId = exCustomer.id + ) + // // ScanInfo // From 190071303710fa0caa9d728b78ed99e52492a3f3 Mon Sep 17 00:00:00 2001 From: Vihang Patil Date: Tue, 29 Oct 2019 11:34:59 +0100 Subject: [PATCH 166/173] Relations and their stores for ExCustomer entity --- .../ostelco/prime/storage/graph/Neo4jStore.kt | 36 ++++++++++++++++++- 1 file changed, 35 insertions(+), 1 deletion(-) diff --git a/neo4j-store/src/main/kotlin/org/ostelco/prime/storage/graph/Neo4jStore.kt b/neo4j-store/src/main/kotlin/org/ostelco/prime/storage/graph/Neo4jStore.kt index 1d0cfb2f1..4a7936e3e 100644 --- a/neo4j-store/src/main/kotlin/org/ostelco/prime/storage/graph/Neo4jStore.kt +++ b/neo4j-store/src/main/kotlin/org/ostelco/prime/storage/graph/Neo4jStore.kt @@ -19,6 +19,7 @@ import org.ostelco.prime.dsl.WriteTransaction import org.ostelco.prime.dsl.forCustomer import org.ostelco.prime.dsl.forPurchaseBy import org.ostelco.prime.dsl.identifiedBy +import org.ostelco.prime.dsl.linkedToCustomer import org.ostelco.prime.dsl.linkedToRegion import org.ostelco.prime.dsl.linkedToSimProfile import org.ostelco.prime.dsl.readTransaction @@ -106,6 +107,8 @@ import org.ostelco.prime.storage.graph.Graph.writeSuspended import org.ostelco.prime.storage.graph.Relation.BELONG_TO_SEGMENT import org.ostelco.prime.storage.graph.Relation.FOR_PURCHASE_BY import org.ostelco.prime.storage.graph.Relation.FOR_PURCHASE_OF +import org.ostelco.prime.storage.graph.Relation.HAD_SIM_PROFILE +import org.ostelco.prime.storage.graph.Relation.HAD_SUBSCRIPTION import org.ostelco.prime.storage.graph.Relation.HAS_BUNDLE import org.ostelco.prime.storage.graph.Relation.HAS_SIM_PROFILE import org.ostelco.prime.storage.graph.Relation.HAS_SUBSCRIPTION @@ -115,6 +118,7 @@ import org.ostelco.prime.storage.graph.Relation.OFFERED_TO_SEGMENT import org.ostelco.prime.storage.graph.Relation.OFFER_HAS_PRODUCT import org.ostelco.prime.storage.graph.Relation.REFERRED import org.ostelco.prime.storage.graph.model.CustomerRegion +import org.ostelco.prime.storage.graph.model.ExCustomer import org.ostelco.prime.storage.graph.model.Identifies import org.ostelco.prime.storage.graph.model.Identity import org.ostelco.prime.storage.graph.model.Offer @@ -124,6 +128,7 @@ import org.ostelco.prime.storage.graph.model.SimProfile import org.ostelco.prime.storage.graph.model.SubscriptionToBundle import org.ostelco.prime.tracing.Trace import java.time.Instant +import java.time.LocalDate import java.util.* import java.util.stream.Collectors import javax.ws.rs.core.MultivaluedMap @@ -142,10 +147,14 @@ enum class Relation( HAS_SUBSCRIPTION(from = Customer::class, to = Subscription::class), // (Customer) -[HAS_SUBSCRIPTION]-> (Subscription) + HAD_SUBSCRIPTION(from = ExCustomer::class, to = Subscription::class), // (ExCustomer) -[HAD_SUBSCRIPTION]-> (Subscription) + HAS_BUNDLE(from = Customer::class, to = Bundle::class), // (Customer) -[HAS_BUNDLE]-> (Bundle) HAS_SIM_PROFILE(from = Customer::class, to = SimProfile::class), // (Customer) -[HAS_SIM_PROFILE]-> (SimProfile) + HAD_SIM_PROFILE(from = ExCustomer::class, to = SimProfile::class), // (ExCustomer) -[HAD_SIM_PROFILE]-> (SimProfile) + SUBSCRIBES_TO_PLAN(from = Customer::class, to = Plan::class), // (Customer) -[SUBSCRIBES_TO_PLAN]-> (Plan) LINKED_TO_BUNDLE(from = Subscription::class, to = Bundle::class), // (Subscription) -[LINKED_TO_BUNDLE]-> (Bundle) @@ -166,6 +175,8 @@ enum class Relation( BELONG_TO_REGION(from = Customer::class, to = Region::class), // (Customer) -[BELONG_TO_REGION]-> (Region) + BELONGED_TO_REGION(from = ExCustomer::class, to = Region::class), // (ExCustomer) -[BELONGED_TO_REGION]-> (Region) + SIM_PROFILE_FOR_REGION(from = SimProfile::class, to = Region::class), // (SimProfile) -[SIM_PROFILE_FOR_REGION]-> (Region) SUBSCRIPTION_UNDER_SIM_PROFILE(from = Subscription::class, to = SimProfile::class), // (Subscription) -[SUBSCRIPTION_UNDER_SIM_PROFILE]-> (SimProfile) @@ -187,6 +198,8 @@ object Neo4jStoreSingleton : GraphStore { private val customerEntity = Customer::class.entityType + private val exCustomerEntity = ExCustomer::class.entityType + private val productEntity = Product::class.entityType private val purchaseRecordEntity = PurchaseRecord::class.entityType @@ -221,6 +234,13 @@ object Neo4jStoreSingleton : GraphStore { dataClass = None::class.java) .also { RelationStore(it) } + val exSubscriptionRelation = RelationType( + relation = HAD_SUBSCRIPTION, + from = exCustomerEntity, + to = subscriptionEntity, + dataClass = None::class.java) + .also { RelationStore(it) } + val customerToBundleRelation = RelationType( relation = HAS_BUNDLE, from = customerEntity, @@ -242,6 +262,13 @@ object Neo4jStoreSingleton : GraphStore { dataClass = None::class.java) .also { RelationStore(it) } + val exCustomerToSimProfileRelation = RelationType( + relation = HAD_SIM_PROFILE, + from = exCustomerEntity, + to = simProfileEntity, + dataClass = None::class.java) + .also { RelationStore(it) } + val forPurchaseByRelation = RelationType( relation = FOR_PURCHASE_BY, from = purchaseRecordEntity, @@ -270,13 +297,20 @@ object Neo4jStoreSingleton : GraphStore { dataClass = PlanSubscription::class.java) private val subscribesToPlanRelationStore = UniqueRelationStore(subscribesToPlanRelation) - private val customerRegionRelation = RelationType( + val customerRegionRelation = RelationType( relation = Relation.BELONG_TO_REGION, from = customerEntity, to = regionEntity, dataClass = CustomerRegion::class.java) private val customerRegionRelationStore = UniqueRelationStore(customerRegionRelation) + val exCustomerRegionRelation = RelationType( + relation = Relation.BELONGED_TO_REGION, + from = exCustomerEntity, + to = regionEntity, + dataClass = None::class.java) + .also { UniqueRelationStore(it) } + val scanInformationRelation = RelationType( relation = Relation.EKYC_SCAN, from = customerEntity, From 383fbbdf57132e4a083253651ea05a1253a227c6 Mon Sep 17 00:00:00 2001 From: Vihang Patil Date: Tue, 29 Oct 2019 11:35:42 +0100 Subject: [PATCH 167/173] ExCustomer node before removing Customer node --- .../ostelco/prime/storage/graph/Neo4jStore.kt | 60 +++++++++++++++---- 1 file changed, 48 insertions(+), 12 deletions(-) diff --git a/neo4j-store/src/main/kotlin/org/ostelco/prime/storage/graph/Neo4jStore.kt b/neo4j-store/src/main/kotlin/org/ostelco/prime/storage/graph/Neo4jStore.kt index 4a7936e3e..f06de8836 100644 --- a/neo4j-store/src/main/kotlin/org/ostelco/prime/storage/graph/Neo4jStore.kt +++ b/neo4j-store/src/main/kotlin/org/ostelco/prime/storage/graph/Neo4jStore.kt @@ -438,19 +438,55 @@ object Neo4jStoreSingleton : GraphStore { .ifFailedThenRollback(transaction) } - // TODO vihang: When we read and then delete, it fails when deserialization does not work. override fun removeCustomer(identity: org.ostelco.prime.model.Identity): Either = writeTransaction { - write(query = """ - MATCH (i:${identityEntity.name} {id:'${identity.id}'})-[:${identifiesRelation.name}]->(c:${customerEntity.name}) - OPTIONAL MATCH (c)-[:${customerToBundleRelation.name}]->(b:${bundleEntity.name}) - OPTIONAL MATCH (c)-[:${scanInformationRelation.name}]->(s:${scanInformationEntity.name}) - DETACH DELETE i, c, b, s; - """.trimIndent(), transaction = transaction) { statementResult -> - Either.cond( - test = statementResult.summary().counters().nodesDeleted() > 0, - ifTrue = {}, - ifFalse = { NotFoundError(type = identityEntity.name, id = identity.id) }) - } + IO { + Either.monad().binding { + // get customer id + val customerId = getCustomerId(identity).bind() + // create ex-customer with same id + create { ExCustomer(id = customerId, terminationDate = LocalDate.now().toString()) }.bind() + // get all subscriptions and link them to ex-customer + val subscriptions = get(Subscription subscribedBy (Customer withId customerId)).bind() + for (subscription in subscriptions) { + fact { (ExCustomer withId customerId) subscribedTo (Subscription withMsisdn subscription.msisdn) }.bind() + } + // get all SIM profiles and link them to ex-customer. + val simProfiles = get(SimProfile forCustomer (Customer withId customerId)).bind() + val simProfileRegions = mutableSetOf() + for (simProfile in simProfiles) { + fact { (ExCustomer withId customerId) had (SimProfile withId simProfile.id) }.bind() + // also get regions linked to those SimProfiles. + simProfileRegions.addAll(get(Region linkedToSimProfile (SimProfile withId simProfile.id)).bind()) + } + // get Regions linked to Customer + val regions = get(Region linkedToCustomer (Customer withId customerId)).bind() + // TODO vihang: clear eKYC data for Regions without any SimProfile +// val regionsWithoutSimProfile = regions - simProfileRegions +// // Link regions with SIM profiles to ExCustomer +// for (region in simProfileRegions) { +// fact { (ExCustomer withId customerId) belongedTo (Region withCode region.id) } +// } + // (For now) Link regions to ExCustomer + for (region in regions) { + fact { (ExCustomer withId customerId) belongedTo (Region withCode region.id) } + } + + // TODO vihang: When we read and then delete, it fails when deserialization does not work. + write(query = """ + MATCH (i:${identityEntity.name} {id:'${identity.id}'})-[:${identifiesRelation.name}]->(c:${customerEntity.name}) + OPTIONAL MATCH (c)-[:${customerToBundleRelation.name}]->(b:${bundleEntity.name}) + OPTIONAL MATCH (c)<-[:${forPurchaseByRelation.name}]-(pr:${purchaseRecordEntity.name}) + OPTIONAL MATCH (c)-[:${scanInformationRelation.name}]->(s:${scanInformationEntity.name}) + DETACH DELETE i, c, b, pr, s; + """.trimIndent(), transaction = transaction) { statementResult -> + Either.cond( + test = statementResult.summary().counters().nodesDeleted() > 0, + ifTrue = {}, + ifFalse = { NotFoundError(type = identityEntity.name, id = identity.id) }) + }.bind() + }.fix() + }.unsafeRunSync() + .ifFailedThenRollback(transaction) } // From c35a6b4f5b488c175de991c06c5b9d6830c3d3cd Mon Sep 17 00:00:00 2001 From: Vihang Patil Date: Tue, 29 Oct 2019 14:00:02 +0100 Subject: [PATCH 168/173] Unit tests --- .../prime/storage/graph/Neo4jStoreTest.kt | 255 +++++++++++++----- 1 file changed, 189 insertions(+), 66 deletions(-) diff --git a/neo4j-store/src/test/kotlin/org/ostelco/prime/storage/graph/Neo4jStoreTest.kt b/neo4j-store/src/test/kotlin/org/ostelco/prime/storage/graph/Neo4jStoreTest.kt index bf0bfd624..7a305e5e2 100644 --- a/neo4j-store/src/test/kotlin/org/ostelco/prime/storage/graph/Neo4jStoreTest.kt +++ b/neo4j-store/src/test/kotlin/org/ostelco/prime/storage/graph/Neo4jStoreTest.kt @@ -1,6 +1,10 @@ package org.ostelco.prime.storage.graph +import arrow.core.Either +import arrow.core.fix import arrow.core.right +import arrow.effects.IO +import arrow.instances.either.monad.monad import com.palantir.docker.compose.DockerComposeRule import com.palantir.docker.compose.connection.waiting.HealthChecks import kotlinx.coroutines.runBlocking @@ -14,6 +18,11 @@ import org.neo4j.driver.v1.AccessMode.WRITE import org.ostelco.prime.analytics.AnalyticsService import org.ostelco.prime.appnotifier.AppNotifier import org.ostelco.prime.dsl.DSL.job +import org.ostelco.prime.dsl.forExCustomer +import org.ostelco.prime.dsl.linkedToExCustomer +import org.ostelco.prime.dsl.readTransaction +import org.ostelco.prime.dsl.wasSubscribedBy +import org.ostelco.prime.dsl.withId import org.ostelco.prime.kts.engine.KtsServiceFactory import org.ostelco.prime.kts.engine.reader.ClasspathResourceTextReader import org.ostelco.prime.model.Customer @@ -42,6 +51,7 @@ import org.ostelco.prime.model.ScanStatus import org.ostelco.prime.model.SimEntry import org.ostelco.prime.model.SimProfile import org.ostelco.prime.model.SimProfileStatus.AVAILABLE_FOR_DOWNLOAD +import org.ostelco.prime.model.Subscription import org.ostelco.prime.notifications.EmailNotifier import org.ostelco.prime.paymentprocessor.PaymentProcessor import org.ostelco.prime.paymentprocessor.core.InvoiceInfo @@ -50,9 +60,12 @@ import org.ostelco.prime.paymentprocessor.core.ProfileInfo import org.ostelco.prime.sim.SimManager import org.ostelco.prime.storage.NotFoundError import org.ostelco.prime.storage.ScanInformationStore +import org.ostelco.prime.storage.StoreError +import org.ostelco.prime.storage.graph.model.ExCustomer import org.ostelco.prime.storage.graph.model.Segment import org.ostelco.prime.tracing.Trace import java.time.Instant +import java.time.LocalDate import java.util.* import javax.ws.rs.core.MultivaluedHashMap import javax.ws.rs.core.MultivaluedMap @@ -234,10 +247,10 @@ class Neo4jStoreTest { val chargeId = UUID.randomUUID().toString() // mock - Mockito.`when`(mockPaymentProcessor.getPaymentProfile(customerId = CUSTOMER.id)) + `when`(mockPaymentProcessor.getPaymentProfile(customerId = CUSTOMER.id)) .thenReturn(ProfileInfo(EMAIL).right()) - Mockito.`when`(mockPaymentProcessor.createInvoice( + `when`(mockPaymentProcessor.createInvoice( customerId = CUSTOMER.id, amount = 24900, currency = "NOK", @@ -246,7 +259,7 @@ class Neo4jStoreTest { sourceId = null) ).thenReturn(InvoiceInfo(invoiceId).right()) - Mockito.`when`(mockPaymentProcessor.payInvoice( + `when`(mockPaymentProcessor.payInvoice( invoiceId = invoiceId) ).thenReturn(InvoicePaymentInfo(invoiceId, chargeId).right()) @@ -346,9 +359,10 @@ class Neo4jStoreTest { @Test fun `set and get Purchase record`() { - assert(Neo4jStoreSingleton.addCustomer( + Neo4jStoreSingleton.addCustomer( identity = IDENTITY, - customer = CUSTOMER).isRight()) + customer = CUSTOMER) + .mapLeft { fail(it.message) } val product = createProduct("1GB_249NOK") val now = Instant.now().toEpochMilli() @@ -373,9 +387,10 @@ class Neo4jStoreTest { @Test fun `create products, offer, segment and then get products for a customer`() { - assert(Neo4jStoreSingleton.addCustomer( + Neo4jStoreSingleton.addCustomer( identity = IDENTITY, - customer = CUSTOMER).isRight()) + customer = CUSTOMER) + .mapLeft { fail(it.message) } // prep job { @@ -477,9 +492,10 @@ class Neo4jStoreTest { create { Region(REGION_CODE, "Norway") } }.mapLeft { fail(it.message) } - assert(Neo4jStoreSingleton.addCustomer( + Neo4jStoreSingleton.addCustomer( identity = IDENTITY, - customer = CUSTOMER).isRight()) + customer = CUSTOMER) + .mapLeft { fail(it.message) } // test Neo4jStoreSingleton.createNewJumioKycScanId(identity = IDENTITY, regionCode = REGION_CODE).map { @@ -499,9 +515,10 @@ class Neo4jStoreTest { create { Region(REGION_CODE, "Norway") } }.mapLeft { fail(it.message) } - assert(Neo4jStoreSingleton.addCustomer( + Neo4jStoreSingleton.addCustomer( identity = IDENTITY, - customer = CUSTOMER).isRight()) + customer = CUSTOMER) + .mapLeft { fail(it.message) } // test Neo4jStoreSingleton.createNewJumioKycScanId(identity = IDENTITY, regionCode = REGION_CODE).map { newScan -> @@ -523,9 +540,10 @@ class Neo4jStoreTest { create { Region(REGION_CODE, "Norway") } }.mapLeft { fail(it.message) } - assert(Neo4jStoreSingleton.addCustomer( + Neo4jStoreSingleton.addCustomer( identity = IDENTITY, - customer = CUSTOMER).isRight()) + customer = CUSTOMER) + .mapLeft { fail(it.message) } Neo4jStoreSingleton.createNewJumioKycScanId(identity = IDENTITY, regionCode = REGION_CODE).map { val newScanInformation = ScanInformation( @@ -552,7 +570,7 @@ class Neo4jStoreTest { vendorData.add(JumioScanData.SCAN_IMAGE.s, imgUrl) vendorData.add(JumioScanData.SCAN_IMAGE_BACKSIDE.s, imgUrl2) - Mockito.`when`(mockScanInformationStore.upsertVendorScanInformation(customerId = CUSTOMER.id, countryCode = REGION, vendorData = vendorData)) + `when`(mockScanInformationStore.upsertVendorScanInformation(customerId = CUSTOMER.id, countryCode = REGION, vendorData = vendorData)) .thenReturn(Unit.right()) Neo4jStoreSingleton.updateScanInformation(newScanInformation, vendorData).mapLeft { @@ -571,9 +589,10 @@ class Neo4jStoreTest { create { Region(REGION_CODE, "Norway") } }.mapLeft { fail(it.message) } - assert(Neo4jStoreSingleton.addCustomer( + Neo4jStoreSingleton.addCustomer( identity = IDENTITY, - customer = CUSTOMER).isRight()) + customer = CUSTOMER) + .mapLeft { fail(it.message) } // test Neo4jStoreSingleton.createNewJumioKycScanId(identity = IDENTITY, regionCode = REGION_CODE).map { @@ -618,12 +637,14 @@ class Neo4jStoreTest { val fakeEmail = "fake-$EMAIL" val fakeIdentity = Identity(id = fakeEmail, type = "EMAIL", provider = "email") - assert(Neo4jStoreSingleton.addCustomer( + Neo4jStoreSingleton.addCustomer( identity = IDENTITY, - customer = CUSTOMER).isRight()) - assert(Neo4jStoreSingleton.addCustomer( + customer = CUSTOMER) + .mapLeft { fail(it.message) } + Neo4jStoreSingleton.addCustomer( identity = fakeIdentity, - customer = Customer(contactEmail = fakeEmail, nickname = NAME)).isRight()) + customer = Customer(contactEmail = fakeEmail, nickname = NAME)) + .mapLeft { fail(it.message) } // test Neo4jStoreSingleton.createNewJumioKycScanId(fakeIdentity, REGION_CODE).mapLeft { @@ -649,19 +670,21 @@ class Neo4jStoreTest { create { Region(REGION_CODE, "Norway") } }.mapLeft { fail(it.message) } - assert(Neo4jStoreSingleton.addCustomer( + Neo4jStoreSingleton.addCustomer( identity = IDENTITY, - customer = CUSTOMER).isRight()) + customer = CUSTOMER) + .mapLeft { fail(it.message) } - assert(Neo4jStoreSingleton.createCustomerRegionSetting( + Neo4jStoreSingleton.createCustomerRegionSetting( customer = CUSTOMER, status = APPROVED, - regionCode = REGION_CODE).isRight()) + regionCode = REGION_CODE) + .mapLeft { fail(it.message) } - Mockito.`when`(mockSimManager.allocateNextEsimProfile("Loltel", "default")) + `when`(mockSimManager.allocateNextEsimProfile("Loltel", "default")) .thenReturn(SimEntry(iccId = "iccId", eSimActivationCode = "eSimActivationCode", msisdnList = emptyList(), status = AVAILABLE_FOR_DOWNLOAD).right()) - Mockito.`when`(mockSimManager.getSimProfile("Loltel", "iccId")) + `when`(mockSimManager.getSimProfile("Loltel", "iccId")) .thenReturn(SimEntry(iccId = "iccId", eSimActivationCode = "eSimActivationCode", msisdnList = emptyList(), status = AVAILABLE_FOR_DOWNLOAD).right()) // test @@ -702,9 +725,10 @@ class Neo4jStoreTest { create { Region(REGION_CODE, "Norway") } }.mapLeft { fail(it.message) } - assert(Neo4jStoreSingleton.addCustomer( + Neo4jStoreSingleton.addCustomer( identity = IDENTITY, - customer = CUSTOMER).isRight()) + customer = CUSTOMER) + .mapLeft { fail(it.message) } // test Neo4jStoreSingleton.getAllRegionDetails(identity = IDENTITY) @@ -724,9 +748,10 @@ class Neo4jStoreTest { create { Region(REGION_CODE, "Norway") } }.mapLeft { fail(it.message) } - assert(Neo4jStoreSingleton.addCustomer( + Neo4jStoreSingleton.addCustomer( identity = IDENTITY, - customer = CUSTOMER).isRight()) + customer = CUSTOMER) + .mapLeft { fail(it.message) } // test Neo4jStoreSingleton.getRegionDetails(identity = IDENTITY, regionCode = REGION_CODE) @@ -747,18 +772,21 @@ class Neo4jStoreTest { create { Region("sg", "Singapore") } }.mapLeft { fail(it.message) } - assert(Neo4jStoreSingleton.addCustomer( + Neo4jStoreSingleton.addCustomer( identity = IDENTITY, - customer = CUSTOMER).isRight()) + customer = CUSTOMER) + .mapLeft { fail(it.message) } - assert(Neo4jStoreSingleton.createCustomerRegionSetting( + Neo4jStoreSingleton.createCustomerRegionSetting( customer = CUSTOMER, status = APPROVED, - regionCode = "no").isRight()) - assert(Neo4jStoreSingleton.createCustomerRegionSetting( + regionCode = "no") + .mapLeft { fail(it.message) } + Neo4jStoreSingleton.createCustomerRegionSetting( customer = CUSTOMER, status = PENDING, - regionCode = "sg").isRight()) + regionCode = "sg") + .mapLeft { fail(it.message) } // test Neo4jStoreSingleton.getAllRegionDetails(identity = IDENTITY) @@ -791,18 +819,22 @@ class Neo4jStoreTest { create { Region("sg", "Singapore") } }.mapLeft { fail(it.message) } - assert(Neo4jStoreSingleton.addCustomer( + Neo4jStoreSingleton.addCustomer( identity = IDENTITY, - customer = CUSTOMER).isRight()) + customer = CUSTOMER) + .mapLeft { fail(it.message) } - assert(Neo4jStoreSingleton.createCustomerRegionSetting( + Neo4jStoreSingleton.createCustomerRegionSetting( customer = CUSTOMER, status = APPROVED, - regionCode = "no").isRight()) - assert(Neo4jStoreSingleton.createCustomerRegionSetting( + regionCode = "no") + .mapLeft { fail(it.message) } + + Neo4jStoreSingleton.createCustomerRegionSetting( customer = CUSTOMER, status = PENDING, - regionCode = "sg").isRight()) + regionCode = "sg") + .mapLeft { fail(it.message) } // test Neo4jStoreSingleton.getRegionDetails(identity = IDENTITY, regionCode = REGION_CODE) @@ -830,29 +862,34 @@ class Neo4jStoreTest { create { Region("sg", "Singapore") } }.mapLeft { fail(it.message) } - assert(Neo4jStoreSingleton.addCustomer( + Neo4jStoreSingleton.addCustomer( identity = IDENTITY, - customer = CUSTOMER).isRight()) + customer = CUSTOMER) + .mapLeft { fail(it.message) } - assert(Neo4jStoreSingleton.createCustomerRegionSetting( + Neo4jStoreSingleton.createCustomerRegionSetting( customer = CUSTOMER, status = APPROVED, - regionCode = "no").isRight()) - assert(Neo4jStoreSingleton.createCustomerRegionSetting( + regionCode = "no") + .mapLeft { fail(it.message) } + + Neo4jStoreSingleton.createCustomerRegionSetting( customer = CUSTOMER, status = PENDING, - regionCode = "sg").isRight()) + regionCode = "sg") + .mapLeft { fail(it.message) } - Mockito.`when`(mockSimManager.allocateNextEsimProfile("Loltel", "default")) + `when`(mockSimManager.allocateNextEsimProfile("Loltel", "default")) .thenReturn(SimEntry(iccId = "iccId", eSimActivationCode = "eSimActivationCode", msisdnList = emptyList(), status = AVAILABLE_FOR_DOWNLOAD).right()) - Mockito.`when`(mockSimManager.getSimProfile("Loltel", "iccId")) + `when`(mockSimManager.getSimProfile("Loltel", "iccId")) .thenReturn(SimEntry(iccId = "iccId", eSimActivationCode = "eSimActivationCode", msisdnList = emptyList(), status = AVAILABLE_FOR_DOWNLOAD).right()) - assert(Neo4jStoreSingleton.provisionSimProfile( + Neo4jStoreSingleton.provisionSimProfile( identity = IDENTITY, regionCode = REGION_CODE, - profileType = "default").isRight()) + profileType = "default") + .mapLeft { fail(it.message) } // test Neo4jStoreSingleton.getAllRegionDetails(identity = IDENTITY) @@ -894,29 +931,33 @@ class Neo4jStoreTest { create { Region("sg", "Singapore") } }.mapLeft { fail(it.message) } - assert(Neo4jStoreSingleton.addCustomer( + Neo4jStoreSingleton.addCustomer( identity = IDENTITY, - customer = CUSTOMER).isRight()) + customer = CUSTOMER) + .mapLeft { fail(it.message) } - assert(Neo4jStoreSingleton.createCustomerRegionSetting( + Neo4jStoreSingleton.createCustomerRegionSetting( customer = CUSTOMER, status = APPROVED, - regionCode = "no").isRight()) - assert(Neo4jStoreSingleton.createCustomerRegionSetting( + regionCode = "no") + .mapLeft { fail(it.message) } + Neo4jStoreSingleton.createCustomerRegionSetting( customer = CUSTOMER, status = PENDING, - regionCode = "sg").isRight()) + regionCode = "sg") + .mapLeft { fail(it.message) } - Mockito.`when`(mockSimManager.allocateNextEsimProfile("Loltel", "default")) + `when`(mockSimManager.allocateNextEsimProfile("Loltel", "default")) .thenReturn(SimEntry(iccId = "iccId", eSimActivationCode = "eSimActivationCode", msisdnList = emptyList(), status = AVAILABLE_FOR_DOWNLOAD).right()) - Mockito.`when`(mockSimManager.getSimProfile("Loltel", "iccId")) + `when`(mockSimManager.getSimProfile("Loltel", "iccId")) .thenReturn(SimEntry(iccId = "iccId", eSimActivationCode = "eSimActivationCode", msisdnList = emptyList(), status = AVAILABLE_FOR_DOWNLOAD).right()) - assert(Neo4jStoreSingleton.provisionSimProfile( + Neo4jStoreSingleton.provisionSimProfile( identity = IDENTITY, regionCode = REGION_CODE, - profileType = "default").isRight()) + profileType = "default") + .mapLeft { fail(it.message) } // test Neo4jStoreSingleton.getRegionDetails(identity = IDENTITY, regionCode = REGION_CODE) @@ -947,9 +988,10 @@ class Neo4jStoreTest { Neo4jStoreSingleton.createSegment(org.ostelco.prime.model.Segment(id = "country-sg")) .mapLeft { fail(it.message) } - assert(Neo4jStoreSingleton.addCustomer( + Neo4jStoreSingleton.addCustomer( identity = IDENTITY, - customer = CUSTOMER).isRight()) + customer = CUSTOMER) + .mapLeft { fail(it.message) } Neo4jStoreSingleton.getRegionDetails( identity = IDENTITY, @@ -998,9 +1040,10 @@ class Neo4jStoreTest { create { Region("sg", "Singapore") } }.mapLeft { fail(it.message) } - assert(Neo4jStoreSingleton.addCustomer( + Neo4jStoreSingleton.addCustomer( identity = IDENTITY, - customer = CUSTOMER).isRight()) + customer = CUSTOMER) + .mapLeft { fail(it.message) } Neo4jStoreSingleton.getRegionDetails( identity = IDENTITY, @@ -1061,6 +1104,86 @@ class Neo4jStoreTest { }) } + @Test + fun `test delete customer`() { + + // setup + job { + create { Region("sg", "Singapore") } + create { Segment(id = "country-sg") } + }.mapLeft { fail(it.message) } + + IO { + Either.monad().binding { + + Neo4jStoreSingleton.addCustomer( + identity = IDENTITY, + customer = CUSTOMER).bind() + + Neo4jStoreSingleton.approveRegionForCustomer( + customerId = CUSTOMER.id, + regionCode = "sg") + .bind() + + Neo4jStoreSingleton.addSubscription( + identity = IDENTITY, + regionCode = "sg", + iccId = ICC_ID, + alias = ALIAS, + msisdn = MSISDN) + .mapLeft { fail(it.message) } + .bind() + + // test + Neo4jStoreSingleton.removeCustomer(identity = IDENTITY).bind() + }.fix() + }.unsafeRunSync() + .mapLeft { fail(it.message) } + + // asserts + readTransaction { + IO { + Either.monad().binding { + + val exCustomer = get(ExCustomer withId CUSTOMER.id).bind() + assertEquals( + expected = ExCustomer(id = CUSTOMER.id, terminationDate = "%d-%d-%d".format(LocalDate.now().year, LocalDate.now().monthValue, LocalDate.now().dayOfMonth)), + actual = exCustomer, + message = "ExCustomer does not match") + + val simProfiles = get(org.ostelco.prime.storage.graph.model.SimProfile forExCustomer (ExCustomer withId CUSTOMER.id)).bind() + assertEquals(expected = 1, actual = simProfiles.size, message = "No SIM profiles found for ExCustomer") + + val simProfile = simProfiles[0] + assertEquals( + expected = simProfile.iccId, + actual = ICC_ID, + message = "ICC ID of simProfile for ExCustomer do not match") + + val subscriptions = get(Subscription wasSubscribedBy (ExCustomer withId CUSTOMER.id)).bind() + assertEquals(expected = 1, actual = subscriptions.size, message = "No subscriptions found for ExCustomer") + + val subscription = subscriptions[0] + assertEquals( + expected = subscription.msisdn, + actual = MSISDN, + message = "MSISDN of subscription for ExCustomer do not match") + + val regions = get(Region linkedToExCustomer (ExCustomer withId CUSTOMER.id)).bind() + assertEquals(expected = 1, actual = regions.size, message = "No regions found for ExCustomer") + + val region = regions[0] + assertEquals( + expected = Region("sg", "Singapore"), + actual = region, + message = "Region for ExCustomer do not match") + + }.fix() + }.unsafeRunSync() + .mapLeft { fail(it.message) } + } + } + companion object { const val EMAIL = "foo@bar.com" const val NAME = "Test User" From 5a21b5d49d69ee35315b9e417261c19c197ab484 Mon Sep 17 00:00:00 2001 From: Vihang Patil Date: Wed, 30 Oct 2019 14:27:05 +0100 Subject: [PATCH 169/173] Updated prime version --- prime/build.gradle.kts | 2 +- prime/script/start.sh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/prime/build.gradle.kts b/prime/build.gradle.kts index 0de3b6aae..57ae7dace 100644 --- a/prime/build.gradle.kts +++ b/prime/build.gradle.kts @@ -10,7 +10,7 @@ plugins { } // Update version in [script/start.sh] too. -version = "1.69.1" +version = "1.70.0" dependencies { // interface module between prime and prime-modules diff --git a/prime/script/start.sh b/prime/script/start.sh index 94545612b..3c1f7e6d4 100755 --- a/prime/script/start.sh +++ b/prime/script/start.sh @@ -5,5 +5,5 @@ exec java \ -Dfile.encoding=UTF-8 \ --add-opens java.base/java.lang=ALL-UNNAMED \ --add-opens java.base/java.io=ALL-UNNAMED \ - -agentpath:/opt/cprof/profiler_java_agent.so=-cprof_service=prime,-cprof_service_version=1.69.1,-logtostderr,-minloglevel=2,-cprof_enable_heap_sampling \ + -agentpath:/opt/cprof/profiler_java_agent.so=-cprof_service=prime,-cprof_service_version=1.70.0,-logtostderr,-minloglevel=2,-cprof_enable_heap_sampling \ -jar /prime.jar server /config/config.yaml From 1be6979363a42b08307e33ebb1475f7edc99942c Mon Sep 17 00:00:00 2001 From: Vihang Patil Date: Wed, 30 Oct 2019 15:00:34 +0100 Subject: [PATCH 170/173] Added missing bind() --- .../main/kotlin/org/ostelco/prime/storage/graph/Neo4jStore.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/neo4j-store/src/main/kotlin/org/ostelco/prime/storage/graph/Neo4jStore.kt b/neo4j-store/src/main/kotlin/org/ostelco/prime/storage/graph/Neo4jStore.kt index f06de8836..e2c0657c0 100644 --- a/neo4j-store/src/main/kotlin/org/ostelco/prime/storage/graph/Neo4jStore.kt +++ b/neo4j-store/src/main/kotlin/org/ostelco/prime/storage/graph/Neo4jStore.kt @@ -464,11 +464,11 @@ object Neo4jStoreSingleton : GraphStore { // val regionsWithoutSimProfile = regions - simProfileRegions // // Link regions with SIM profiles to ExCustomer // for (region in simProfileRegions) { -// fact { (ExCustomer withId customerId) belongedTo (Region withCode region.id) } +// fact { (ExCustomer withId customerId) belongedTo (Region withCode region.id) }.bind() // } // (For now) Link regions to ExCustomer for (region in regions) { - fact { (ExCustomer withId customerId) belongedTo (Region withCode region.id) } + fact { (ExCustomer withId customerId) belongedTo (Region withCode region.id) }.bind() } // TODO vihang: When we read and then delete, it fails when deserialization does not work. From ecc9bfcb24486459cbb158d0a76676df32d333cd Mon Sep 17 00:00:00 2001 From: mpeterss Date: Wed, 30 Oct 2019 16:42:16 +0100 Subject: [PATCH 171/173] Fix TCC Timer issue when session is moved between gateways Since the session can be moved from one OCS gateway instance to another, we might end up with trailing timers on the instence from which the session was moved. The TCC Timer will setback the state of the session to IDLE which might not be true as it now lives on another node. This will disable the timer. Since the gateway does not store any reservation locally it is not needed at this point. --- .../diameter/ha/timer/ReplicatedTimerTaskData.kt | 14 +++++++++++--- .../java/org/ostelco/ocsgw/OcsApplication.java | 2 +- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/diameter-ha/src/main/kotlin/org/ostelco/diameter/ha/timer/ReplicatedTimerTaskData.kt b/diameter-ha/src/main/kotlin/org/ostelco/diameter/ha/timer/ReplicatedTimerTaskData.kt index 4f496ccde..0c277f24c 100644 --- a/diameter-ha/src/main/kotlin/org/ostelco/diameter/ha/timer/ReplicatedTimerTaskData.kt +++ b/diameter-ha/src/main/kotlin/org/ostelco/diameter/ha/timer/ReplicatedTimerTaskData.kt @@ -72,9 +72,17 @@ class ReplicatedTimerTask(val data: ReplicatedTimerTaskData, private val session logger.debug("Task with id ${data.taskID} is recurring, not removing it") } - logger.debug("Firing Timer with id ${data.taskID}") - - runTask() + /* The TCC_CCASERVER_TIMER is supposed to clear any reservation and set state back to IDLE + We do not store the reservation in the gateway, therefore we do not need to trigger the + onTimer imlp. But we should clear any internals kept for this session to not waste memory. + */ + if (!data.taskID.toString().endsWith("TCC_CCASERVER_TIMER")) { + logger.debug("Firing Timer with id ${data.taskID}") + runTask() + } else { + // clear local session. As the timer was created by this instance it should be local. + sessionDataSource.removeSession(data.sessionId) + } } private fun removeFromScheduler() { diff --git a/ocsgw/src/main/java/org/ostelco/ocsgw/OcsApplication.java b/ocsgw/src/main/java/org/ostelco/ocsgw/OcsApplication.java index 4229a1890..e8bb63c64 100644 --- a/ocsgw/src/main/java/org/ostelco/ocsgw/OcsApplication.java +++ b/ocsgw/src/main/java/org/ostelco/ocsgw/OcsApplication.java @@ -140,7 +140,7 @@ public void doCreditControlRequest(ServerCCASession session, JCreditControlReque try { OcsServer.INSTANCE.handleRequest$ocsgw(session, request); } catch (Exception e) { - LOG.error("[><] Failure processing Credit-Control-Request [" + RequestType.getTypeAsString(request.getRequestTypeAVPValue()) + "]", e); + LOG.error("[><] Failure processing Credit-Control-Request [" + RequestType.getTypeAsString(request.getRequestTypeAVPValue()) + "] + [session.getSessionId()]", e); } break; case RequestType.EVENT_REQUEST: From e68c2bb0c1cee6b19ff32bb97f9f5ea0ccf796d3 Mon Sep 17 00:00:00 2001 From: mpeterss Date: Wed, 30 Oct 2019 16:48:52 +0100 Subject: [PATCH 172/173] Added logg --- .../org/ostelco/diameter/ha/timer/ReplicatedTimerTaskData.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/diameter-ha/src/main/kotlin/org/ostelco/diameter/ha/timer/ReplicatedTimerTaskData.kt b/diameter-ha/src/main/kotlin/org/ostelco/diameter/ha/timer/ReplicatedTimerTaskData.kt index 0c277f24c..ad91b2d43 100644 --- a/diameter-ha/src/main/kotlin/org/ostelco/diameter/ha/timer/ReplicatedTimerTaskData.kt +++ b/diameter-ha/src/main/kotlin/org/ostelco/diameter/ha/timer/ReplicatedTimerTaskData.kt @@ -81,6 +81,7 @@ class ReplicatedTimerTask(val data: ReplicatedTimerTaskData, private val session runTask() } else { // clear local session. As the timer was created by this instance it should be local. + logger.debug("Skipping Timer with id ${data.taskID}, removing session ${data.sessionId}") sessionDataSource.removeSession(data.sessionId) } } From 50da4b7895b2d21d84b418966ea3cd6aa902239e Mon Sep 17 00:00:00 2001 From: "Kjell M. Myksvoll" Date: Fri, 1 Nov 2019 09:26:36 +0100 Subject: [PATCH 173/173] Fix incorrect formatting of date in neo4j unit test --- .../kotlin/org/ostelco/prime/storage/graph/Neo4jStoreTest.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/neo4j-store/src/test/kotlin/org/ostelco/prime/storage/graph/Neo4jStoreTest.kt b/neo4j-store/src/test/kotlin/org/ostelco/prime/storage/graph/Neo4jStoreTest.kt index 7a305e5e2..1861b2342 100644 --- a/neo4j-store/src/test/kotlin/org/ostelco/prime/storage/graph/Neo4jStoreTest.kt +++ b/neo4j-store/src/test/kotlin/org/ostelco/prime/storage/graph/Neo4jStoreTest.kt @@ -1147,7 +1147,7 @@ class Neo4jStoreTest { val exCustomer = get(ExCustomer withId CUSTOMER.id).bind() assertEquals( - expected = ExCustomer(id = CUSTOMER.id, terminationDate = "%d-%d-%d".format(LocalDate.now().year, LocalDate.now().monthValue, LocalDate.now().dayOfMonth)), + expected = ExCustomer(id = CUSTOMER.id, terminationDate = "%d-%02d-%02d".format(LocalDate.now().year, LocalDate.now().monthValue, LocalDate.now().dayOfMonth)), actual = exCustomer, message = "ExCustomer does not match") @@ -1247,4 +1247,4 @@ class Neo4jStoreTest { Neo4jClient.stop() } } -} \ No newline at end of file +}