diff --git a/Makefile b/Makefile index c60ee365..3dababca 100644 --- a/Makefile +++ b/Makefile @@ -50,6 +50,13 @@ test-api: # get lpas ./api-test/tester -expectedStatus=200 REQUEST POST $(URL)/lpas '{"uids": [$(LPA_UID)]}' + + # certificate provider opt out + $(eval LPA_UID := "$(shell ./api-test/tester UID)") + cat ./docs/example-lpa.json | ./api-test/tester -expectedStatus=201 REQUEST PUT $(URL)/lpas/$(LPA_UID) "`xargs -0`" + ./api-test/tester -expectedStatus=200 REQUEST GET $(URL)/lpas/$(LPA_UID) '' + cat ./docs/certificate-provider-opt-out.json | ./api-test/tester -expectedStatus=201 REQUEST POST $(URL)/lpas/$(LPA_UID)/updates "`xargs -0`" + .PHONY: test-api test-pact: diff --git a/docs/certificate-provider-opt-out.json b/docs/certificate-provider-opt-out.json new file mode 100644 index 00000000..c90a2db5 --- /dev/null +++ b/docs/certificate-provider-opt-out.json @@ -0,0 +1,4 @@ +{ + "type": "CERTIFICATE_PROVIDER_OPT_OUT", + "changes": [] +} diff --git a/docs/openapi/openapi.yaml b/docs/openapi/openapi.yaml index 3ebcd503..4f157635 100644 --- a/docs/openapi/openapi.yaml +++ b/docs/openapi/openapi.yaml @@ -267,6 +267,7 @@ components: - TRUST_CORPORATION_SIGN - PERFECT - REGISTER + - CERTIFICATE_PROVIDER_OPT_OUT changes: type: array items: diff --git a/internal/shared/lpa.go b/internal/shared/lpa.go index 62877acb..25836c7e 100644 --- a/internal/shared/lpa.go +++ b/internal/shared/lpa.go @@ -47,7 +47,8 @@ func (e LpaType) IsValid() bool { type LpaStatus string const ( - LpaStatusProcessing = LpaStatus("processing") - LpaStatusPerfect = LpaStatus("perfect") - LpaStatusRegistered = LpaStatus("registered") + LpaStatusProcessing = LpaStatus("processing") + LpaStatusPerfect = LpaStatus("perfect") + LpaStatusRegistered = LpaStatus("registered") + LpaStatusCannotRegister = LpaStatus("cannot-register") ) diff --git a/lambda/update/certificate_provider_opt_out.go b/lambda/update/certificate_provider_opt_out.go new file mode 100644 index 00000000..16bc6359 --- /dev/null +++ b/lambda/update/certificate_provider_opt_out.go @@ -0,0 +1,23 @@ +package main + +import "github.com/ministryofjustice/opg-data-lpa-store/internal/shared" + +type CertificateProviderOptOut struct{} + +func (c CertificateProviderOptOut) Apply(lpa *shared.Lpa) []shared.FieldError { + if lpa.CertificateProvider.SignedAt != nil && !lpa.CertificateProvider.SignedAt.IsZero() { + return []shared.FieldError{{Source: "/type", Detail: "certificate provider cannot opt out after providing certificate"}} + } + + lpa.Status = shared.LpaStatusCannotRegister + + return nil +} + +func validateCertificateProviderOptOut(changes []shared.Change) (CertificateProviderOptOut, []shared.FieldError) { + if len(changes) > 0 { + return CertificateProviderOptOut{}, []shared.FieldError{{Source: "/changes", Detail: "expected empty"}} + } + + return CertificateProviderOptOut{}, nil +} diff --git a/lambda/update/certificate_provider_opt_out_test.go b/lambda/update/certificate_provider_opt_out_test.go new file mode 100644 index 00000000..979bedf2 --- /dev/null +++ b/lambda/update/certificate_provider_opt_out_test.go @@ -0,0 +1,70 @@ +package main + +import ( + "encoding/json" + "testing" + "time" + + "github.com/ministryofjustice/opg-data-lpa-store/internal/shared" + "github.com/stretchr/testify/assert" +) + +func TestCertificateProviderOptOutApply(t *testing.T) { + lpa := &shared.Lpa{Status: shared.LpaStatusProcessing} + c := CertificateProviderOptOut{} + + errors := c.Apply(lpa) + + assert.Empty(t, errors) + assert.Equal(t, shared.LpaStatusCannotRegister, lpa.Status) +} + +func TestCertificateProviderOptOutApplyWhenCertificateProvided(t *testing.T) { + now := time.Now() + certificateProvider := shared.CertificateProvider{Email: "a@example", SignedAt: &now} + lpa := &shared.Lpa{LpaInit: shared.LpaInit{ + CertificateProvider: certificateProvider}, + } + + errors := CertificateProviderOptOut{}.Apply(lpa) + + assert.Equal(t, errors, []shared.FieldError{{Source: "/type", Detail: "certificate provider cannot opt out after providing certificate"}}) + assert.Equal(t, certificateProvider, lpa.CertificateProvider) +} + +func TestValidateUpdateCertificateProviderOptOut(t *testing.T) { + testcases := map[string]struct { + update shared.Update + lpa *shared.Lpa + errors []shared.FieldError + }{ + "valid": { + update: shared.Update{ + Type: "CERTIFICATE_PROVIDER_OPT_OUT", + Changes: []shared.Change{}, + }, + }, + "with changes": { + update: shared.Update{ + Type: "CERTIFICATE_PROVIDER_OPT_OUT", + Changes: []shared.Change{ + { + Key: "/something/someValue", + New: json.RawMessage(`"not expected"`), + Old: jsonNull, + }, + }, + }, + errors: []shared.FieldError{ + {Source: "/changes", Detail: "expected empty"}, + }, + }, + } + + for name, tc := range testcases { + t.Run(name, func(t *testing.T) { + _, errors := validateUpdate(tc.update, &shared.Lpa{}) + assert.ElementsMatch(t, tc.errors, errors) + }) + } +} diff --git a/lambda/update/validate.go b/lambda/update/validate.go index bdd998ab..5cf2f2e7 100644 --- a/lambda/update/validate.go +++ b/lambda/update/validate.go @@ -10,16 +10,18 @@ type Applyable interface { func validateUpdate(update shared.Update, lpa *shared.Lpa) (Applyable, []shared.FieldError) { switch update.Type { - case "CERTIFICATE_PROVIDER_SIGN": - return validateCertificateProviderSign(update.Changes, lpa) case "ATTORNEY_SIGN": return validateAttorneySign(update.Changes, lpa) - case "TRUST_CORPORATION_SIGN": - return validateTrustCorporationSign(update.Changes, lpa) + case "CERTIFICATE_PROVIDER_OPT_OUT": + return validateCertificateProviderOptOut(update.Changes) + case "CERTIFICATE_PROVIDER_SIGN": + return validateCertificateProviderSign(update.Changes, lpa) case "PERFECT": return validatePerfect(update.Changes) case "REGISTER": return validateRegister(update.Changes) + case "TRUST_CORPORATION_SIGN": + return validateTrustCorporationSign(update.Changes, lpa) default: return nil, []shared.FieldError{{Source: "/type", Detail: "invalid value"}} }