Skip to content
GitLab
Menu
Projects
Groups
Snippets
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
Menu
Open sidebar
TousAntiCovid sources
DCC Server
Commits
ae740b6a
Commit
ae740b6a
authored
Feb 11, 2022
by
Sapotille Orange
Committed by
Bergamote Orange
Feb 11, 2022
Browse files
feat(dcc): add metrics and logs
parent
ccf41259
Changes
8
Hide whitespace changes
Inline
Side-by-side
pom.xml
View file @
ae740b6a
...
...
@@ -49,6 +49,10 @@
<groupId>
org.springframework.boot
</groupId>
<artifactId>
spring-boot-starter-actuator
</artifactId>
</dependency>
<dependency>
<groupId>
io.micrometer
</groupId>
<artifactId>
micrometer-registry-prometheus
</artifactId>
</dependency>
<dependency>
<groupId>
org.jetbrains.kotlin
</groupId>
<artifactId>
kotlin-reflect
</artifactId>
...
...
@@ -98,10 +102,6 @@
<artifactId>
cose-java
</artifactId>
<version>
1.1.0
</version>
</dependency>
<dependency>
<groupId>
io.micrometer
</groupId>
<artifactId>
micrometer-registry-prometheus
</artifactId>
</dependency>
<dependency>
<groupId>
commons-codec
</groupId>
<artifactId>
commons-codec
</artifactId>
...
...
@@ -162,6 +162,12 @@
<version>
1.5.0
</version>
<scope>
test
</scope>
</dependency>
<dependency>
<groupId>
io.github.hakky54
</groupId>
<artifactId>
logcaptor
</artifactId>
<version>
2.7.8
</version>
<scope>
test
</scope>
</dependency>
<!-- CUCUMBER -->
<dependency>
...
...
src/main/kotlin/fr/gouv/tac/dcclight/controller/AggregateController.kt
View file @
ae740b6a
package
fr.gouv.tac.dcclight.controller
import
com.fasterxml.jackson.databind.ObjectMapper
import
ehn.techiop.hcert.kotlin.chain.Error
import
ehn.techiop.hcert.kotlin.chain.VerificationException
import
fr.gouv.tac.dcclight.api.AggregateApi
import
fr.gouv.tac.dcclight.api.model.CertificatesAggregate
...
...
@@ -8,6 +9,7 @@ import fr.gouv.tac.dcclight.api.model.DcclightsAggregationRequest
import
fr.gouv.tac.dcclight.api.model.EncryptedApiResponse
import
fr.gouv.tac.dcclight.service.DccDecodingService
import
fr.gouv.tac.dcclight.service.GenerationService
import
fr.gouv.tac.dcclight.utils.MetricsService
import
org.slf4j.LoggerFactory
import
org.springframework.http.HttpStatus.OK
import
org.springframework.http.ResponseEntity
...
...
@@ -26,7 +28,8 @@ class AggregateController(
val
dccDecodingService
:
DccDecodingService
,
val
generationService
:
GenerationService
,
val
softwareEncryptionTool
:
SoftwareEncryptionTool
,
val
objectMapper
:
ObjectMapper
val
objectMapper
:
ObjectMapper
,
val
metricsService
:
MetricsService
)
:
AggregateApi
{
private
val
log
=
LoggerFactory
.
getLogger
(
this
::
class
.
java
)
...
...
@@ -34,6 +37,7 @@ class AggregateController(
override
fun
aggregate
(
dcclightsAggregationRequest
:
DcclightsAggregationRequest
):
ResponseEntity
<
EncryptedApiResponse
>
{
val
decoder
=
Base64
.
getDecoder
()
val
appPublicKey
=
decoder
.
decode
(
dcclightsAggregationRequest
.
publicKey
)
val
key
=
softwareEncryptionTool
.
getEncryptionKey
(
appPublicKey
)
val
bindingResult
:
BindingResult
=
BeanPropertyBindingResult
(
dcclightsAggregationRequest
.
certificates
,
"response"
)
...
...
@@ -43,7 +47,13 @@ class AggregateController(
softwareEncryptionTool
.
decrypt
(
key
,
decoder
.
decode
(
certificate
)).
toString
(
UTF_8
)
).
also
{
it
.
verificationResult
.
error
?.
let
{
error
->
log
.
error
(
error
.
name
)
if
(
error
.
name
.
equals
(
Error
.
CWT_EXPIRED
))
{
metricsService
.
countAndLogRejectDccWithTechnicalValidityDateInThePast
(
it
)
}
else
if
(
error
.
name
.
equals
(
Error
.
SIGNATURE_INVALID
))
{
metricsService
.
countRejectDccWithWrongSignatureKeys
()
}
else
{
log
.
warn
(
error
.
name
)
}
throw
VerificationException
(
error
)
}
}
...
...
src/main/kotlin/fr/gouv/tac/dcclight/service/GenerationService.kt
View file @
ae740b6a
...
...
@@ -3,6 +3,7 @@ package fr.gouv.tac.dcclight.service
import
ehn.techiop.hcert.kotlin.chain.DecodeResult
import
fr.gouv.tac.dcclight.service.error.DccValidationError.INCOMPATIBLE_DCC_COMBINATION
import
fr.gouv.tac.dcclight.service.error.reject
import
fr.gouv.tac.dcclight.utils.MetricsService
import
org.springframework.stereotype.Service
import
org.springframework.validation.BindException
import
org.springframework.validation.BindingResult
...
...
@@ -14,10 +15,13 @@ class GenerationService(
val
dcclightEncodingService
:
DcclightEncodingService
,
val
dccEncodingService
:
DccEncodingService
,
val
derogationDscBlocklistRulesService
:
DerogationDscBlocklistRulesService
,
val
aggregationRulesConfiguration
:
AggregationSanitaryRulesService
val
aggregationRulesConfiguration
:
AggregationSanitaryRulesService
,
val
metricsService
:
MetricsService
)
{
fun
generateAggregate
(
decryptedDCCs
:
List
<
DecodeResult
>,
bindingResult
:
BindingResult
):
String
{
val
personHealthData
=
PersonHealthData
(
decryptedDCCs
,
derogationDscBlocklistRulesService
,
aggregationRulesConfiguration
,
bindingResult
)
val
personHealthData
=
PersonHealthData
(
decryptedDCCs
,
derogationDscBlocklistRulesService
,
aggregationRulesConfiguration
,
bindingResult
,
metricsService
)
if
(
personHealthData
.
isEligibleToDerogation
())
{
return
personHealthData
.
derogationDcclight
(
dcclightEncodingService
)
}
...
...
@@ -25,6 +29,7 @@ class GenerationService(
if
(
aggregationDcc
==
null
)
{
bindingResult
.
reject
(
INCOMPATIBLE_DCC_COMBINATION
)
metricsService
.
countAndLogRejectDccWithIncompatibleDccCombination
(
decryptedDCCs
)
throw
BindException
(
bindingResult
)
}
return
aggregationDcc
...
...
src/main/kotlin/fr/gouv/tac/dcclight/service/PersonHealthData.kt
View file @
ae740b6a
...
...
@@ -31,9 +31,11 @@ import fr.gouv.tac.dcclight.service.error.DccValidationError.MISSING_CERTIFICATE
import
fr.gouv.tac.dcclight.service.error.DccValidationError.MISSING_CERTIFICATE_ISSUER
import
fr.gouv.tac.dcclight.service.error.DccValidationError.MISSING_GIVEN_NAME_TRANSLITERATED
import
fr.gouv.tac.dcclight.service.error.reject
import
fr.gouv.tac.dcclight.utils.MetricsService
import
kotlinx.datetime.Clock.System.now
import
kotlinx.datetime.TimeZone.Companion.currentSystemDefault
import
kotlinx.datetime.atStartOfDayIn
import
org.apache.commons.codec.digest.DigestUtils
import
org.springframework.validation.BindException
import
org.springframework.validation.BindingResult
import
kotlin.time.Duration.Companion.days
...
...
@@ -45,7 +47,8 @@ class PersonHealthData(
decodeResults
:
List
<
DecodeResult
>,
private
val
derogationDscBlocklistRulesService
:
DerogationDscBlocklistRulesService
,
private
val
aggregationSanitaryRulesService
:
AggregationSanitaryRulesService
,
bindingResult
:
BindingResult
bindingResult
:
BindingResult
,
private
val
metricsService
:
MetricsService
)
{
private
val
vaccinations
:
List
<
Vaccination
>
...
...
@@ -112,8 +115,10 @@ class PersonHealthData(
if
(
decodeResults
.
any
{
it
.
chainDecodeResult
.
eudgc
==
null
})
{
throw
IllegalStateException
(
"DecodeResults eudgc fields are not supposed to be empty"
)
}
if
(
decodeResults
.
any
{
derogationDscBlocklistRulesService
.
isBlocklisted
(
it
.
chainDecodeResult
.
eudgc
!!
)
})
{
if
(
decodeResults
.
any
()
{
derogationDscBlocklistRulesService
.
isBlocklisted
(
it
.
chainDecodeResult
.
eudgc
!!
)
})
{
bindingResult
.
reject
(
BLOCKLISTED_CERTIFICATE
)
metricsService
.
countRejectDccInBlocklist
()
throw
BindException
(
bindingResult
)
}
val
referenceDcc
=
getFirstCertificateWithGivenNameAndLastName
(
decodeResults
)
verifyDccPersonalInformation
(
referenceDcc
,
decodeResults
,
bindingResult
)
...
...
@@ -123,14 +128,15 @@ class PersonHealthData(
if
(
decodeResults
.
any
{
it
.
chainDecodeResult
.
eudgc
!!
.
getIssuer
().
isEmpty
()
})
{
bindingResult
.
reject
(
MISSING_CERTIFICATE_ISSUER
)
}
val
anyStartWithAggregatePrefix
=
decodeResults
.
any
{
it
.
chainDecodeResult
.
eudgc
!!
.
getIdentifier
().
startsWith
(
"URN:UVCI:01:FR:DGSAG/"
)
}
if
(
anyStartWithAggregatePrefix
)
{
if
(
decodeResults
.
any
()
{
it
.
chainDecodeResult
.
eudgc
!!
.
getIdentifier
().
startsWith
(
"URN:UVCI:01:FR:DGSAG/"
)
})
{
bindingResult
.
reject
(
AGGREGATE_CERTIFICATE_NOT_ALLOWED
)
metricsService
.
countRejectDccWithNotAllowedCertificate
()
throw
BindException
(
bindingResult
)
}
if
(
decodeResults
.
any
{
it
.
chainDecodeResult
.
eudgc
!!
.
getProofInstant
()
>
now
().
plus
(
1
.
days
)
})
{
val
firstDccWithDateInFutureMoreThanOneDay
=
decodeResults
.
firstOrNull
()
{
it
.
chainDecodeResult
.
eudgc
!!
.
getProofInstant
()
>
now
().
plus
(
1
.
days
)
}
if
(
firstDccWithDateInFutureMoreThanOneDay
!=
null
)
{
bindingResult
.
reject
(
CERTIFICATE_PROOF_DATE_TOO_FAR_IN_FUTURE
)
metricsService
.
countRejectDccWithDateInFutureMoreThanOneDay
(
firstDccWithDateInFutureMoreThanOneDay
)
}
if
(
bindingResult
.
hasErrors
())
{
throw
BindException
(
bindingResult
)
...
...
@@ -223,38 +229,38 @@ class PersonHealthData(
decodeResults
:
List
<
DecodeResult
>,
result
:
BindingResult
)
{
if
(
decodeResults
.
any
{
it
.
chainDecodeResult
.
eudgc
!!
.
subject
.
givenNameTransliterated
==
null
})
{
if
(
decodeResults
.
any
{
it
.
chainDecodeResult
.
eudgc
?
.
subject
?
.
givenNameTransliterated
==
null
})
{
result
.
reject
(
MISSING_GIVEN_NAME_TRANSLITERATED
)
}
// directly throw exception to avoid NPE in following code due to String? to String casts
if
(
result
.
hasErrors
())
{
throw
BindException
(
result
)
}
val
allMatchReferenceFamilyName
=
decodeResults
.
all
{
it
.
chainDecodeResult
.
eudgc
!!
.
subject
.
familyNameTransliterated
.
trimEnd
(
'<'
)
==
referenceDcc
.
subject
.
familyNameTransliterated
.
trimEnd
(
'<'
)
}
if
(!
allMatchReferenceFamilyName
)
{
val
firstInconsistentFamilyName
=
decodeResults
.
firstOrNull
()
{
it
.
chainDecodeResult
.
eudgc
?.
subject
?.
familyNameTransliterated
!!
.
trimEnd
(
'<'
)
!=
referenceDcc
.
subject
.
familyNameTransliterated
.
trimEnd
(
'<'
)
}
if
(
firstInconsistentFamilyName
!=
null
)
{
result
.
reject
(
INCONSISTENT_FAMILY_NAME_TRANSLITERATED
)
metricsService
.
countAndLogRejectDccWithIncorrecFamilyName
(
firstInconsistentFamilyName
,
referenceDcc
)
throw
BindException
(
result
)
}
val
allMatchReferenceGivenName
=
decodeResults
.
all
{
it
.
chainDecodeResult
.
eudgc
!!
.
subject
.
givenNameTransliterated
!!
.
trimEnd
(
'<'
)
==
referenceDcc
.
subject
.
givenNameTransliterated
!!
.
trimEnd
(
'<'
)
}
if
(!
allMatchReferenceGivenName
)
{
val
firstInconsistentGivenName
=
decodeResults
.
firstOrNull
()
{
it
.
chainDecodeResult
.
eudgc
?.
subject
?.
givenNameTransliterated
!!
.
trimEnd
(
'<'
)
!=
referenceDcc
.
subject
.
givenNameTransliterated
!!
.
trimEnd
(
'<'
)
}
if
(
firstInconsistentGivenName
!=
null
)
{
result
.
reject
(
INCONSISTENT_GIVEN_NAME_TRANSLITERATED
)
metricsService
.
countAndLogRejectDccWithIncorrecGivenName
(
firstInconsistentGivenName
,
referenceDcc
)
throw
BindException
(
result
)
}
val
allMatchDateOfBirthString
=
decodeResults
.
map
{
results
->
results
.
chainDecodeResult
.
eudgc
!!
}
.
all
{
it
.
dateOfBirthString
==
referenceDcc
.
dateOfBirthString
}
if
(!
allMatchDateOfBirthString
)
{
result
.
reject
(
INCONSISTENT_DATE_OF_BIRTH_STRING
)
val
firstInconsistentDateOfBirth
=
decodeResults
.
map
{
results
->
results
.
chainDecodeResult
.
eudgc
}.
firstOrNull
()
{
it
?.
dateOfBirthString
!=
referenceDcc
.
dateOfBirthString
}
if
(
result
.
hasErrors
())
{
if
(
firstInconsistentDateOfBirth
!=
null
)
{
result
.
reject
(
INCONSISTENT_DATE_OF_BIRTH_STRING
)
metricsService
.
countAndLogRejectDccWithIncorrecDateOfBirth
(
firstInconsistentDateOfBirth
,
referenceDcc
)
throw
BindException
(
result
)
}
}
...
...
@@ -263,7 +269,7 @@ class PersonHealthData(
decodeResults
.
map
{
it
.
chainDecodeResult
}
.
filter
{
it
.
eudgc
!=
null
}
.
map
{
it
.
eudgc
!!
}
.
sortedWith
(
GreenCertificatesDateComparator
())
.
sortedWith
(
PersonHealthData
.
Companion
.
GreenCertificatesDateComparator
())
private
fun
getFirstCertificateWithGivenNameAndLastName
(
decodeResults
:
List
<
DecodeResult
>)
=
decodeResults
...
...
@@ -302,6 +308,22 @@ class PersonHealthData(
}
.
map
{
it
.
chainDecodeResult
.
eudgc
!!
.
tests
!!
[
0
]
!!
}
private
fun
getConcatenatedIdentifier
():
String
{
val
concatenatedIdentifiers
=
temporallySortedDcc
.
joinToString
(
""
)
{
if
(
it
.
hasOneTest
())
{
it
.
tests
!!
[
0
]
!!
.
certificateIdentifier
}
else
if
(
it
.
hasOneVaccination
())
{
it
.
vaccinations
!!
[
0
]
!!
.
certificateIdentifier
}
else
if
(
it
.
hasOneRecoveryStatement
())
{
it
.
recoveryStatements
!!
[
0
]
!!
.
certificateIdentifier
}
else
{
throw
IllegalStateException
(
"Trying to compare at least one certificate with no sanitary proof"
)
}
}
return
"01:FR:DGSAG/"
+
DigestUtils
.
sha256Hex
(
concatenatedIdentifiers
).
take
(
19
)
}
private
fun
isDerogationCompatibleTest
(
it
:
Test
):
Boolean
{
val
now
=
now
()
return
!
derogationDscBlocklistRulesService
.
isBlocklisted
(
it
)
&&
...
...
src/main/kotlin/fr/gouv/tac/dcclight/utils/MetricsService.kt
0 → 100644
View file @
ae740b6a
package
fr.gouv.tac.dcclight.utils
import
ehn.techiop.hcert.kotlin.chain.DecodeResult
import
ehn.techiop.hcert.kotlin.data.GreenCertificate
import
fr.gouv.tac.dcclight.extension.getProofInstant
import
fr.gouv.tac.dcclight.extension.getType
import
io.micrometer.core.instrument.Counter
import
io.micrometer.core.instrument.MeterRegistry
import
kotlinx.datetime.Clock
import
kotlinx.datetime.Clock.System.now
import
org.slf4j.LoggerFactory
import
org.springframework.stereotype.Service
@Service
class
MetricsService
(
private
val
meterRegistry
:
MeterRegistry
)
{
private
val
log
=
LoggerFactory
.
getLogger
(
this
::
class
.
java
)
private
val
counters
:
Map
<
String
,
Counter
>
=
HashMap
()
private
val
inconsistentFamilyNameCounter
=
Counter
.
builder
(
"dcc.request.aggregation.inconsistent.family.name"
)
.
description
(
"Number of inconsistent family name"
)
.
register
(
meterRegistry
)
private
val
inconsistentGivenNameCounter
=
Counter
.
builder
(
"dcc.request.aggregation.inconsistent.given.name"
)
.
description
(
"Number of inconsistent given name"
)
.
register
(
meterRegistry
)
private
val
inconsistentDateOfBirthCounter
=
Counter
.
builder
(
"dcc.request.aggregation.inconsistent.date.of.birth"
)
.
description
(
"Number of inconsistent date of birth"
)
.
register
(
meterRegistry
)
private
val
incompatibleDccCombinationCounter
=
Counter
.
builder
(
"dcc.request.aggregation.incompatible.dcc.combination"
)
.
description
(
"Number of incompatible DCC combination"
)
.
register
(
meterRegistry
)
private
val
aggregateCertificateNotAllowedCounter
=
Counter
.
builder
(
"dcc.request.aggregation.aggregate.certificate.not.allowed"
)
.
description
(
"Number of rejected DCC due to provided certificate coming from a previous aggregation"
)
.
register
(
meterRegistry
)
private
val
wrongSignatureKeysCounter
=
Counter
.
builder
(
"dcc.request.aggregation.decoding.error"
)
.
description
(
"Number of rejected DCC due to wrong signature keys"
)
.
tag
(
"type"
,
"wrong_signature_keys"
)
.
register
(
meterRegistry
)
private
val
technicalValidityDateInThePastCounter
=
Counter
.
builder
(
"dcc.request.aggregation.decoding.error"
)
.
description
(
"Number of rejected DCC due to technical validity date in the past"
)
.
tag
(
"type"
,
"technical_validity_date_in_the_past"
)
.
register
(
meterRegistry
)
private
val
dccInBlockListCounter
=
Counter
.
builder
(
"dcc.request.aggregation.dcc.inblocklist"
)
.
description
(
"Number of DCC rejected due to being on the blocklist"
)
.
register
(
meterRegistry
)
private
val
dccWithDateInFutureMoreThanOneDayCounter
=
Counter
.
builder
(
"dcc.request.aggregation.sanitary.proof.date.in.future"
)
.
description
(
"Number of DCC with a sanitary proof date in more than one day in the future"
)
.
register
(
meterRegistry
)
fun
countAndLogRejectDccWithIncorrecFamilyName
(
firstInconsistentFamilyName
:
DecodeResult
,
referenceDcc
:
GreenCertificate
)
{
log
.
info
(
"""Transliterated family name is inconsistent across provided dcc list.
DCC type: ${firstInconsistentFamilyName.chainDecodeResult.eudgc?.getType()?.type},
reference family name: ${anonymizeData(referenceDcc.subject.familyNameTransliterated)}
DCC wrong family name: ${anonymizeData(firstInconsistentFamilyName.chainDecodeResult.eudgc?.subject?.familyNameTransliterated!!)},
date: ${Clock.System.now()}
"""
.
trimIndent
()
)
inconsistentFamilyNameCounter
.
increment
()
}
fun
countAndLogRejectDccWithIncorrecGivenName
(
firstInconsistentGivenName
:
DecodeResult
,
referenceDcc
:
GreenCertificate
)
{
log
.
info
(
"""Transliterated given name is inconsistent across provided dcc list.
DCC type: ${firstInconsistentGivenName.chainDecodeResult.eudgc?.getType()?.type},
reference given name: ${anonymizeData(referenceDcc.subject.givenNameTransliterated!!)}
DCC wrong given name: ${anonymizeData(firstInconsistentGivenName.chainDecodeResult.eudgc?.subject?.givenNameTransliterated!!)},
date: ${Clock.System.now()}
"""
.
trimIndent
()
)
inconsistentGivenNameCounter
.
increment
()
}
fun
countAndLogRejectDccWithIncorrecDateOfBirth
(
firstInconsistentDateOfBirth
:
GreenCertificate
,
referenceDcc
:
GreenCertificate
)
{
log
.
info
(
"""Date of birth string is inconsistent across provided dcc list.
DCC type: ${firstInconsistentDateOfBirth.getType()?.type},
reference date of birth: ${anonymizeData(referenceDcc.dateOfBirthString)}
DCC wrong date of birth: ${anonymizeData(firstInconsistentDateOfBirth.dateOfBirthString)},
date: ${Clock.System.now()}
"""
.
trimIndent
()
)
inconsistentDateOfBirthCounter
.
increment
()
}
fun
countAndLogRejectDccWithIncompatibleDccCombination
(
decryptedDCCs
:
List
<
DecodeResult
>)
{
log
.
info
(
"""Provided certificates are not compatible with any operation.
date: ${Clock.System.now()},
list of DCC: ${decryptedDCCs.mapIndexed{ index,it -> getDccInformations(it, index) }}
"""
.
trimIndent
()
)
incompatibleDccCombinationCounter
.
increment
()
}
fun
countRejectDccWithWrongSignatureKeys
()
{
wrongSignatureKeysCounter
.
increment
()
}
fun
countRejectDccWithNotAllowedCertificate
()
{
aggregateCertificateNotAllowedCounter
.
increment
()
}
fun
countAndLogRejectDccWithTechnicalValidityDateInThePast
(
decryptedDCC
:
DecodeResult
)
{
log
.
info
(
"""End of technical validity is in the past.
DCC type: ${decryptedDCC.chainDecodeResult.eudgc?.getType()?.type},
End of validity: ${decryptedDCC.verificationResult.expirationTime},
Date: ${now()}
"""
.
trimIndent
()
)
technicalValidityDateInThePastCounter
.
increment
()
}
fun
countRejectDccInBlocklist
()
{
dccInBlockListCounter
.
increment
()
}
fun
countRejectDccWithDateInFutureMoreThanOneDay
(
decryptedDCC
:
DecodeResult
)
{
log
.
info
(
"""At least one of the provided certificates has a sanitary proof date in more than one day
DCC type: ${decryptedDCC.chainDecodeResult.eudgc?.getType()?.type},
DCC date: ${decryptedDCC.chainDecodeResult.eudgc?.getProofInstant()},
date: ${now()}
"""
.
trimIndent
()
)
dccWithDateInFutureMoreThanOneDayCounter
.
increment
()
}
// fun countAggregationSuccessfullyGenerated(ruleName: String) {
// val formattedRuleName = ruleName.replace("\\s+", ".").lowercase()
// var counter = counters[formattedRuleName];
//
// if(counter == null) {
// counter = Counter.builder("dcc.request.aggregation.successful")
// .description("Number of aggregation successfully generated")
// .tag("rule name", formattedRuleName)
// .register(meterRegistry)
// counters.put(formattedRuleName,counter)
// }
// counter.increment()
//
// }
private
fun
anonymizeData
(
name
:
String
):
String
{
return
"[a-zA-Z0-9]"
.
toRegex
().
replace
(
name
,
"#"
)
}
fun
getDccInformations
(
decryptedDCC
:
DecodeResult
,
position
:
Int
):
String
?
{
if
(
decryptedDCC
.
chainDecodeResult
.
eudgc
?.
getType
()
?.
type
!!
.
equals
(
"v"
))
{
val
vaccinationProof
=
decryptedDCC
.
chainDecodeResult
.
eudgc
?.
vaccinations
!!
[
0
]
!!
return
"""
{
DCC type: v,
position: $position,
vaccine code: ${vaccinationProof.vaccine.valueSetEntry.display},
vaccine type: ${vaccinationProof.medicinalProduct.valueSetEntry.display},
vaccine manufacturer: ${vaccinationProof.authorizationHolder.valueSetEntry.display},
dose number: ${vaccinationProof.doseNumber},
total dose number: ${vaccinationProof.doseTotalNumber},
country: ${vaccinationProof.country},
vaccination date: ${vaccinationProof.date}
vaccination certificate issuer: ${vaccinationProof.certificateIssuer}
}
"""
.
trimIndent
()
}
else
if
(
decryptedDCC
.
chainDecodeResult
.
eudgc
?.
getType
()
?.
type
!!
.
equals
(
"t"
))
{
val
testProof
=
decryptedDCC
.
chainDecodeResult
.
eudgc
?.
tests
!!
[
0
]
!!
return
"""
{
DCC type: t,
position: $position,
test type: ${testProof.type.valueSetEntry.display},
test name: ${testProof.nameNaa},
test device: ${testProof.nameRat?.valueSetEntry?.display},
test date sample: ${testProof.dateTimeSample},
test date result: ${testProof.dateTimeResult} ,
test result: ${testProof.resultPositive.valueSetEntry.display},
test country: ${testProof.country}
test certificate issuer: ${testProof.certificateIssuer}
}
"""
.
trimIndent
()
}
else
if
(
decryptedDCC
.
chainDecodeResult
.
eudgc
?.
getType
()
?.
type
!!
.
equals
(
"r"
))
{
val
recoveryProof
=
decryptedDCC
.
chainDecodeResult
.
eudgc
?.
recoveryStatements
!!
[
0
]
!!
return
"""
{
DCC type: r,
position: $position,
recovery date of first positive test: ${recoveryProof.dateOfFirstPositiveTestResult},
country: ${recoveryProof.country},
recovery certificate valid from: ${recoveryProof.certificateValidFrom},
recovery certificate valid until: ${recoveryProof.certificateValidUntil}
recovery certificate issuer: ${recoveryProof.certificateIssuer}
}
"""
.
trimIndent
()
}
else
{
throw
IllegalStateException
(
"Trying to compare at least one certificate with no sanitary proof"
)
}
}
}
src/main/resources/application.yml
View file @
ae740b6a
...
...
@@ -24,7 +24,7 @@ management:
endpoints
:
web
:
exposure
:
include
:
health, prometheus, info
include
:
health, prometheus, info
, metrics
endpoint
:
health
:
probes
:
...
...
src/test/kotlin/fr/gouv/tac/dcclight/TestAndVaccinationCombinationErrorsTest.kt
View file @
ae740b6a
...
...
@@ -206,8 +206,6 @@ class TestAndVaccinationCombinationErrorsTest {
.
body
(
"message"
,
equalTo
(
"Request body contains invalid attributes"
))
.
body
(
"errors[0].code"
,
equalTo
(
"INCONSISTENT_FAMILY_NAME_TRANSLITERATED"
))
.
body
(
"errors[0].message"
,
equalTo
(
"Transliterated family name is inconsistent across provided dcc list"
))
.
body
(
"errors[1].code"
,
equalTo
(
"INCONSISTENT_GIVEN_NAME_TRANSLITERATED"
))
.
body
(
"errors[1].message"
,
equalTo
(
"Transliterated given name is inconsistent across provided dcc list"
))
}
@ParameterizedTest
...
...
@@ -321,10 +319,6 @@ class TestAndVaccinationCombinationErrorsTest {
.
body
(
"message"
,
equalTo
(
"Request body contains invalid attributes"
))
.
body
(
"errors[0].code"
,
equalTo
(
"INCONSISTENT_FAMILY_NAME_TRANSLITERATED"
))
.
body
(
"errors[0].message"
,
equalTo
(
"Transliterated family name is inconsistent across provided dcc list"
))
.
body
(
"errors[1].code"
,
equalTo
(
"INCONSISTENT_GIVEN_NAME_TRANSLITERATED"
))
.
body
(
"errors[1].message"
,
equalTo
(
"Transliterated given name is inconsistent across provided dcc list"
))
.
body
(
"errors[2].code"
,
equalTo
(
"INCONSISTENT_DATE_OF_BIRTH_STRING"
))
.
body
(
"errors[2].message"
,
equalTo
(
"Date of birth string is inconsistent across provided dcc list"
))
}
@ParameterizedTest
...
...
src/test/kotlin/fr/gouv/tac/dcclight/service/PersonHealthDataTest.kt
View file @
ae740b6a
...
...
@@ -17,6 +17,7 @@ import fr.gouv.tac.dcclight.test.defaultPerson
import
fr.gouv.tac.dcclight.test.defaultTest
import
fr.gouv.tac.dcclight.test.defaultVaccination
import
fr.gouv.tac.dcclight.test.entryOf
import
fr.gouv.tac.dcclight.utils.MetricsService
import
kotlinx.datetime.Clock.System.now
import
kotlinx.datetime.TimeZone.Companion.currentSystemDefault
import
kotlinx.datetime.toLocalDateTime
...
...
@@ -47,6 +48,7 @@ class PersonHealthDataTest {
private
val
aggregationSanitaryRulesServiceMock
:
AggregationSanitaryRulesService
=
mock
(
AggregationSanitaryRulesService
::
class
.
java
)
private
val
metricsService
:
MetricsService
=
mock
(
MetricsService
::
class
.
java
)
@TestInstance
(
PER_CLASS
)
@Nested
...
...
@@ -73,7 +75,8 @@ class PersonHealthDataTest {
list
,
derogationDscBlocklistRulesServiceMock
,
aggregationSanitaryRulesServiceMock
,
bindingResult
bindingResult
,
metricsService
)
Assertions
.
assertTrue
(
personHealthData
.
hasCompliantTestInLastDay
())
}
...
...
@@ -99,7 +102,8 @@ class PersonHealthDataTest {
list
,
derogationDscBlocklistRulesServiceMock
,
aggregationSanitaryRulesServiceMock
,
bindingResult
bindingResult
,
metricsService
)
Assertions
.
assertTrue
(
personHealthData
.
hasCompliantTestInLastDay
())
}
...
...
@@ -126,7 +130,8 @@ class PersonHealthDataTest {
list
,
derogationDscBlocklistRulesServiceMock
,
aggregationSanitaryRulesServiceMock
,
bindingResult
bindingResult
,
metricsService
)
Assertions
.
assertFalse
(
personHealthData
.
hasCompliantTestInLastDay
())
}
...
...
@@ -153,7 +158,8 @@ class PersonHealthDataTest {
list
,
derogationDscBlocklistRulesServiceMock
,
aggregationSanitaryRulesServiceMock
,
bindingResult
bindingResult
,
metricsService
)
Assertions
.
assertFalse
(
personHealthData
.
hasCompliantTestInLastDay
())
}
...
...
@@ -180,7 +186,8 @@ class PersonHealthDataTest {
list
,
derogationDscBlocklistRulesServiceMock
,
aggregationSanitaryRulesServiceMock
,
bindingResult
bindingResult
,
metricsService
)
Assertions
.
assertTrue
(
personHealthData
.
hasCompliantTestInLastDay
())
}
...
...
@@ -206,7 +213,8 @@ class PersonHealthDataTest {
list
,
derogationDscBlocklistRulesServiceMock
,
aggregationSanitaryRulesServiceMock
,
bindingResult
bindingResult
,
metricsService
)
Assertions
.
assertFalse
(
personHealthData
.
hasCompliantTestInLastDay
())
}
...
...
@@ -233,7 +241,8 @@ class PersonHealthDataTest {
list
,
derogationDscBlocklistRulesServiceMock
,
aggregationSanitaryRulesServiceMock
,
bindingResult
bindingResult
,
metricsService
)
Assertions
.
assertFalse
(
personHealthData
.
hasCompliantTestInLastDay
())
}
...
...
@@ -260,7 +269,8 @@ class PersonHealthDataTest {
list
,
derogationDscBlocklistRulesServiceMock
,
aggregationSanitaryRulesServiceMock
,
bindingResult
bindingResult
,
metricsService
)
Assertions
.
assertFalse
(
personHealthData
.
hasCompliantTestInLastDay
())
}
...
...
@@ -293,7 +303,8 @@ class PersonHealthDataTest {
list
,
derogationDscBlocklistRulesServiceMock
,
aggregationSanitaryRulesServiceMock
,
bindingResult
bindingResult
,