Skip to content

Commit

Permalink
feat: expose reject reason in recovery request.
Browse files Browse the repository at this point in the history
  • Loading branch information
gdethier committed May 23, 2024
1 parent c3c8136 commit 1745305
Show file tree
Hide file tree
Showing 11 changed files with 50 additions and 24 deletions.
4 changes: 4 additions & 0 deletions resources/schemas.json
Original file line number Diff line number Diff line change
Expand Up @@ -2025,6 +2025,10 @@
"type": "string",
"format": "uuid",
"description": "The ID of created Protection Request"
},
"rejectReason": {
"type": "string",
"description": "If REJECTED, the reason motivating the rejection."
}
},
"required": [
Expand Down
2 changes: 2 additions & 0 deletions src/logion/controllers/components.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1085,6 +1085,8 @@ export interface components {
* @description The ID of created Protection Request
*/
id: string;
/** @description If REJECTED, the reason motivating the rejection. */
rejectReason?: string;
};
RecoveryRequestsView: {
requests?: components["schemas"]["RecoveryRequestView"][];
Expand Down
2 changes: 1 addition & 1 deletion src/logion/controllers/protectionrequest.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ import {
FetchProtectionRequestsSpecification,
ProtectionRequestFactory,
ProtectionRequestDescription,
LegalOfficerDecisionDescription,
ProtectionRequestStatus,
} from '../model/protectionrequest.model.js';
import { components } from './components.js';
Expand All @@ -32,6 +31,7 @@ import { LocalsObject } from 'pug';
import { LocRequestAdapter, UserPrivateData } from "./adapters/locrequestadapter.js";
import { LocRequestRepository } from '../model/locrequest.model.js';
import { ValidAccountId } from "@logion/node-api";
import { LegalOfficerDecisionDescription } from '../model/decision.js';

type CreateProtectionRequestView = components["schemas"]["CreateProtectionRequestView"];
type ProtectionRequestView = components["schemas"]["ProtectionRequestView"];
Expand Down
2 changes: 2 additions & 0 deletions src/logion/controllers/recovery.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ export class RecoveryController extends ApiController {
type: "ACCOUNT",
userIdentity: requireDefined(userIdentity),
userPostalAddress: requireDefined(userPostalAddress),
rejectReason: accountRecoveryRequest.getDecision()?.rejectReason,
};
}

Expand All @@ -95,6 +96,7 @@ export class RecoveryController extends ApiController {
type: "SECRET",
userIdentity: description.userIdentity,
userPostalAddress: description.userPostalAddress,
rejectReason: secretRecoveryRequest.getDecision()?.rejectReason,
};
}
}
5 changes: 5 additions & 0 deletions src/logion/model/decision.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,8 @@ export class LegalOfficerDecision {
@Column({ length: 255, name: "reject_reason", nullable: true })
rejectReason?: string;
}

export interface LegalOfficerDecisionDescription {
readonly decisionOn: string;
readonly rejectReason?: string;
}
7 changes: 1 addition & 6 deletions src/logion/model/protectionrequest.model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { appDataSource, Log, badRequest, requireDefined } from "@logion/rest-api
import { LocRequestRepository } from "./locrequest.model.js";
import { ValidAccountId } from "@logion/node-api";
import { DB_SS58_PREFIX } from "./supportedaccountid.model.js";
import { LegalOfficerDecision } from "./decision.js";
import { LegalOfficerDecision, LegalOfficerDecisionDescription } from "./decision.js";

const { logger } = Log;

Expand Down Expand Up @@ -240,11 +240,6 @@ export interface ProtectionRequestDescription {
readonly addressToRecover: ValidAccountId | null,
}

export interface LegalOfficerDecisionDescription {
readonly decisionOn: string;
readonly rejectReason?: string;
}

export interface NewProtectionRequestParameters {
readonly id: string;
readonly requesterAddress: ValidAccountId,
Expand Down
13 changes: 12 additions & 1 deletion src/logion/model/secret_recovery.model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { Moment } from "moment";
import { ValidAccountId } from "@logion/node-api";
import { DB_SS58_PREFIX } from "./supportedaccountid.model.js";
import moment from "moment";
import { LegalOfficerDecision } from "./decision.js";
import { LegalOfficerDecision, LegalOfficerDecisionDescription } from "./decision.js";

export type SecretRecoveryRequestStatus = 'PENDING' | 'REJECTED' | 'ACCEPTED';

Expand Down Expand Up @@ -72,6 +72,17 @@ export class SecretRecoveryRequestAggregateRoot {
this.status = 'ACCEPTED';
this.decision!.accept(decisionOn);
}

getDecision(): LegalOfficerDecisionDescription | undefined {
if (!this.decision || this.decision.decisionOn === undefined) {
return undefined
}
const { decisionOn, rejectReason } = this.decision
return {
decisionOn,
rejectReason,
}
}
}

export interface SecretRecoveryRequestDescription {
Expand Down
3 changes: 1 addition & 2 deletions test/unit/controllers/protectionrequest.controller.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import {
ProtectionRequestAggregateRoot,
NewProtectionRequestParameters,
ProtectionRequestDescription,
LegalOfficerDecisionDescription,
} from '../../../src/logion/model/protectionrequest.model.js';
import { ALICE, BOB, CHARLY, BOB_ACCOUNT, ALICE_ACCOUNT, CHARLY_ACCOUNT } from '../../helpers/addresses.js';
import { ProtectionRequestController } from '../../../src/logion/controllers/protectionrequest.controller.js';
Expand All @@ -26,7 +25,7 @@ import { LocRequestAdapter } from "../../../src/logion/controllers/adapters/locr
import { ValidAccountId } from "@logion/node-api";
import { DB_SS58_PREFIX, EmbeddableNullableAccountId } from "../../../src/logion/model/supportedaccountid.model.js";
import { LocRequestDescription } from 'src/logion/model/loc_vos.js';
import { LegalOfficerDecision } from 'src/logion/model/decision.js';
import { LegalOfficerDecision, LegalOfficerDecisionDescription } from 'src/logion/model/decision.js';

const DECISION_TIMESTAMP = "2021-06-10T16:25:23.668294";
const { mockAuthenticationWithCondition, setupApp, mockLegalOfficerOnNode, mockAuthenticationWithAuthenticatedUser, mockAuthenticatedUser } = TestApp;
Expand Down
32 changes: 20 additions & 12 deletions test/unit/controllers/recovery.controller.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,10 @@ describe('RecoveryController', () => {
expect(response.body.requests[1].userIdentity).toEqual(IDENTITY);
expect(response.body.requests[1].userPostalAddress).toEqual(POSTAL_ADDRESS);
expect(response.body.requests[1].createdOn).toBe(SECRET_CREATED_ON.toISOString());
expect(response.body.requests[1].status).toBe("ACCEPTED");
expect(response.body.requests[1].status).toBe("REJECTED");
expect(response.body.requests[1].id).toBe(SECRET_RECOVERY_REQUEST_ID);
expect(response.body.requests[1].type).toBe("SECRET");
expect(response.body.requests[1].rejectReason).toBe(REJECT_REASON);
});
});
});
Expand Down Expand Up @@ -83,13 +84,14 @@ function mockProtectionRequest(): Mock<ProtectionRequestAggregateRoot> {
createdOn: ACCOUNT_CREATED_ON,
addressToRecover: null,
}
const protectionRequest = new Mock<ProtectionRequestAggregateRoot>()
protectionRequest.setup(instance => instance.getDescription()).returns(description)
protectionRequest.setup(instance => instance.getLegalOfficer()).returns(ALICE_ACCOUNT)
protectionRequest.setup(instance => instance.getOtherLegalOfficer()).returns(BOB_ACCOUNT)
protectionRequest.setup(instance => instance.getRequester()).returns(REQUESTER)
protectionRequest.setup(instance => instance.getAddressToRecover()).returns(null)
return protectionRequest
const protectionRequest = new Mock<ProtectionRequestAggregateRoot>();
protectionRequest.setup(instance => instance.getDescription()).returns(description);
protectionRequest.setup(instance => instance.getLegalOfficer()).returns(ALICE_ACCOUNT);
protectionRequest.setup(instance => instance.getOtherLegalOfficer()).returns(BOB_ACCOUNT);
protectionRequest.setup(instance => instance.getRequester()).returns(REQUESTER);
protectionRequest.setup(instance => instance.getAddressToRecover()).returns(null);
protectionRequest.setup(instance => instance.getDecision()).returns(undefined);
return protectionRequest;
}

const REQUESTER = ValidAccountId.polkadot("5H4MvAsobfZ6bBCDyj5dsrWYLrA8HrRzaqa9p61UXtxMhSCY");
Expand Down Expand Up @@ -129,18 +131,24 @@ function mockLocRequestAdapter(): LocRequestAdapter {
function mockSecretRecoveryRequest(): Mock<SecretRecoveryRequestAggregateRoot> {
const description: SecretRecoveryRequestDescription = {
id: SECRET_RECOVERY_REQUEST_ID,
status: "ACCEPTED",
status: "REJECTED",
requesterIdentityLocId: REQUESTER_IDENTITY_LOC_ID,
createdOn: SECRET_CREATED_ON,
challenge: "Challenge",
secretName: "Key",
userIdentity: IDENTITY,
userPostalAddress: POSTAL_ADDRESS,
}
const protectionRequest = new Mock<SecretRecoveryRequestAggregateRoot>()
protectionRequest.setup(instance => instance.getDescription()).returns(description)
return protectionRequest
const protectionRequest = new Mock<SecretRecoveryRequestAggregateRoot>();
protectionRequest.setup(instance => instance.getDescription()).returns(description);
protectionRequest.setup(instance => instance.getDecision()).returns({
decisionOn: DECISION_ON.toISOString(),
rejectReason: REJECT_REASON,
});
return protectionRequest;
}

const SECRET_RECOVERY_REQUEST_ID = "f293127e-8356-47a7-a6b7-480cdd1daabd";
const SECRET_CREATED_ON = moment();
const DECISION_ON = moment();
const REJECT_REASON = "Because.";
2 changes: 1 addition & 1 deletion test/unit/model/secret_recovery.model.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ describe("SecretRecoveryRequestAggregateRoot", () => {
request.reject("Because.", moment());

expect(request.status).toBe("REJECTED");
const decision = request.decision;
const decision = request.getDecision();
expect(decision?.decisionOn).toBeDefined();
expect(decision?.rejectReason).toBe("Because.");
})
Expand Down
2 changes: 1 addition & 1 deletion test/unit/services/notification-test-data.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import {
ProtectionRequestDescription,
LegalOfficerDecisionDescription
} from "../../../src/logion/model/protectionrequest.model.js";
import { ALICE_ACCOUNT, BOB_ACCOUNT } from "../../helpers/addresses.js";
import { LegalOfficer } from "../../../src/logion/model/legalofficer.model.js";
Expand All @@ -9,6 +8,7 @@ import { ValidAccountId } from "@logion/node-api";
import { LocRequestDecision, LocRequestDescription } from "src/logion/model/loc_vos.js";
import { SecretRecoveryRequestDescription } from "src/logion/model/secret_recovery.model.js";
import moment from "moment";
import { LegalOfficerDecisionDescription } from "src/logion/model/decision.js";

export const notifiedProtection: ProtectionRequestDescription & { decision: LegalOfficerDecisionDescription } = {
id: "a7ff4ab6-5bef-4310-9c28-bcbd653565c3",
Expand Down

0 comments on commit 1745305

Please sign in to comment.